/*
* Copyright (c) 1993-1997,2004 Rinet Corp., Novosibirsk, Russia
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
* Redistribution in binary form may occur without any restrictions.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_HAS_COLORS
#ifdef HAVE_SLCURSES
#include <slcurses.h>
#elif HAVE_NCURSES
#include <ncurses.h>
#else
#include <curses.h>
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <netdb.h>
#include <errno.h>
#include "colormask.h"
#include "trafshow.h"
#include "netstat.h"
#ifdef DEBUG
#include "show_stat.h" /* just for hdr2str() */
#endif
/* mask entry */
struct cm_entry {
struct internet_header in_hdr;
int src_mask; /* source ip address mask */
int dst_mask; /* destination ip address mask */
short pair; /* color-pair */
int attr; /* video attributes; bold, blink, etc */
};
static struct cm_entry *color_mask = NULL;
static int n_masks = 0;
static int n_pairs = 0;
static const char *rc_file = 0;
static int rc_line;
/* SLcurses can't handle attributes as well; so hack it */
#ifdef HAVE_SLCURSES
static void
slang_init_pair(short pair, short fc, short bc, int at)
{
SLtt_set_color_object(pair, ((fc | (bc << 8)) << 8) | at);
}
static int
slang_pair_content(short pair, short *fc, short *bc)
{
int attr;
SLtt_Char_Type at;
at = SLtt_get_color_object(pair);
attr = at & (A_BOLD | A_BLINK);
at &= ~(A_BOLD | A_BLINK);
at >>= 8;
*fc = at & 0xff;
*bc = (at >> 8) & 0xff;
return attr;
}
#endif /* HAVE_SLCURSES */
static short
findpair(short f, short b, int a)
{
int i;
short f1 = -1, b1 = -1;
struct cm_entry *cm;
for (cm = color_mask, i = 0; cm != NULL && i < n_masks-1; cm++, i++) {
#ifdef HAVE_SLCURSES
int a1 = slang_pair_content(cm->pair, &f1, &b1);
if (f1 >= COLORS) f1 = -1;
if (b1 >= COLORS) b1 = -1;
if (f == f1 && b == b1 && a == a1) return cm->pair;
#else
pair_content(cm->pair, &f1, &b1);
if (f1 >= COLORS) f1 = -1;
if (b1 >= COLORS) b1 = -1;
if (f == f1 && b == b1) return cm->pair;
#endif
}
return 0;
}
static int
add_colormask(const char *s, struct cm_entry *m)
{
int i, attr = 0;
short fc, bc;
char f[100], *b;
static char *ctab[8] = { "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white" };
#ifdef HAVE_USE_DEFAULT_COLORS
static short fc_def = -1, bc_def = -1;
#else
static short fc_def = COLOR_WHITE, bc_def = COLOR_BLACK;
#endif
if ((b = strchr(strcpy(f, s), ':')) != NULL) *b++ = '\0';
if (*f) {
for (i = 0; i < 8; i++)
if (!strcasecmp(ctab[i], f)) break;
if (i < 8) fc = i;
else {
fc = atoi(f);
if (fc < 1 || fc > COLORS) {
fprintf(stderr, "%s: line %d: Unknown color `%s'\n",
rc_file, rc_line, f);
return -1;
}
}
if (isupper((int)*f)) attr |= A_BOLD;
} else fc = fc_def;
if (b && *b) {
for (i = 0; i < 8; i++)
if (!strcasecmp(ctab[i], b)) break;
if (i < 8) bc = i;
else {
bc = atoi(b);
if (bc < 1 || bc > COLORS) {
fprintf(stderr, "%s: line %d: Unknown color `%s'\n",
rc_file, rc_line, b);
return -1;
}
}
if (isupper((int)*b)) attr |= A_BLINK;
} else bc = bc_def;
if (m != NULL) {
if ((color_mask = realloc(color_mask, ++n_masks * sizeof(struct cm_entry))) == NULL) {
fprintf(stderr, "add_colormask: realloc: Out of memory?\n");
return -1;
}
if ((m->pair = findpair(fc, bc, attr)) == 0) {
if (++n_pairs < COLOR_PAIRS-1) {
#ifdef HAVE_SLCURSES
slang_init_pair(n_pairs, fc, bc, attr);
#else
init_pair(n_pairs, fc, bc);
#endif
} else {
fprintf(stderr, "%s: line %d: Max %d color-pairs can be used\n",
rc_file, rc_line, COLOR_PAIRS-1);
return -1;
}
m->pair = n_pairs;
}
m->attr = attr;
memcpy(color_mask + (n_masks-1), m, sizeof(struct cm_entry));
} else { /* default colors */
#ifdef HAVE_SLCURSES
slang_init_pair(0, fc, bc, attr);
#else
#ifdef HAVE_BKGD
init_pair(COLOR_PAIRS-1, fc, bc);
bkgd(COLOR_PAIR(COLOR_PAIRS-1) | attr);
#elif HAVE_WBKGD
init_pair(COLOR_PAIRS-1, fc, bc);
wbkgd(stdscr, COLOR_PAIR(COLOR_PAIRS-1) | attr);
#else /* assume the color-pair 0 is background for whole screen */
init_pair(0, fc, bc);
#endif
#endif
fc_def = fc;
bc_def = bc;
}
return 0;
}
static int
is_any(const char *s)
{
if (!s || !*s) return 0;
return (!strcmp(s, "*") || !strcasecmp(s, "any") || !strcasecmp(s, "all"));
}
static int
is_number(const char *s)
{
if (!s || !*s) return 0;
for (; *s; s++) {
if (!isdigit((int)*s)) return 0;
}
return 1;
}
static char *
str2proto(const char *str, int *proto)
{
int num;
struct protoent *pe;
if (is_any(str)) {
*proto = 0;
return "";
}
if (is_number(str)) {
num = atoi(str);
if (num > 0 && num <= 0xff) {
if ((pe = getprotobynumber(num)) != 0) {
*proto = pe->p_proto;
return pe->p_name;
}
*proto = num;
return "";
}
}
if ((pe = getprotobyname(str)) != 0) {
*proto = pe->p_proto;
return pe->p_name;
}
fprintf(stderr, "%s: line %d: Unknown protocol `%s'\n",
rc_file, rc_line, str);
return 0;
}
static int
str2port(const char *str, const char *proto)
{
int num;
struct servent *se;
if (is_any(str))
return 0;
num = atoi(str);
if (num > 0 && num <= 0xffff)
return htons((u_int16_t)num);
if ((se = getservbyname(str, (proto && *proto) ? proto : 0)) != 0)
return se->s_port;
if (proto && *proto) {
fprintf(stderr, "%s: line %d: Unknown port `%s' at protocol `%s'\n",
rc_file, rc_line, str, proto);
} else {
fprintf(stderr, "%s: line %d: Unknown port `%s'\n",
rc_file, rc_line, str);
}
return -1;
}
static int
str2addr(const char *str, const char *proto, struct ip_address *addr, int *mask)
{
int op, ver = 0;
char buf[256], *cp, *mp, *pp;
if (proto && !strcasecmp(proto, "IPv6")) {
#ifdef INET6
ver = 6;
#else
fprintf(stderr, "%s: line %d: IPv6 is unsupported at this system\n",
rc_file, rc_line);
return -1;
#endif
}
cp = strcpy(buf, str);
if ((mp = strchr(cp, '/')) != 0) {
*mp++ = '\0';
cp = mp;
}
if ((pp = strchr(cp, ',')) != 0) {
*pp++ = '\0';
}
if (mp && !is_number(mp)) {
fprintf(stderr, "%s: line %d: %s: Mask must be number of bits\n",
rc_file, rc_line, mp);
return -1;
}
if (!is_any(buf)) {
op = 0;
#ifdef INET6
if (ver == 6 || strchr(buf, ':')) {
ver = 6;
op = inet_pton(AF_INET6, buf, &addr->ip6_addr);
if (op < 0) {
fprintf(stderr, "%s: line %d: %s: %s\n",
rc_file, rc_line, buf, strerror(errno));
return -1;
}
}
#endif
if (!op) {
ver = 4;
op = inet_pton(AF_INET, buf, &addr->ip_addr);
if (op < 0) {
fprintf(stderr, "%s: line %d: %s: %s\n",
rc_file, rc_line, buf, strerror(errno));
return -1;
}
}
if (!op) {
struct hostent *he;
if ((he = gethostbyname(buf)) == 0) {
fprintf(stderr, "%s: line %d: %s: Unknown host\n",
rc_file, rc_line, buf);
return -1;
}
if (he->h_addrtype == AF_INET) {
ver = 4;
memcpy(&addr->ip_addr, he->h_addr,
MIN(sizeof(addr->ip_addr), he->h_length));
}
#ifdef INET6
else if (he->h_addrtype == AF_INET6) {
ver = 6;
memcpy(&addr->ip6_addr, he->h_addr,
MIN(sizeof(addr->ip6_addr), he->h_length));
}
#endif
else {
fprintf(stderr, "%s: line %d: %s: Unknown address family\n",
rc_file, rc_line, buf);
return -1;
}
}
}
if (pp) {
if ((op = str2port(pp, proto)) == -1)
return -1;
addr->ip_port = op;
}
if (mask) {
if (mp) {
op = atoi(mp);
if (op < 8 || op > 128) {
fprintf(stderr, "%s: line %d: %d: Wrong mask\n",
rc_file, rc_line, op);
return -1;
}
*mask = op;
} else *mask = 0;
}
return ver;
}
int
init_colormask()
{
FILE *fp;
int num;
struct cm_entry me, *cm;
char *cp, buf[1024];
char s1[256], s2[256], s3[256], s4[256];
if (rc_file) {
free((char *)rc_file);
rc_file = 0;
}
if (!color_conf) {
if ((cp = getenv("HOME")) != 0) {
(void)strcpy(buf, cp);
(void)strcat(buf, "/");
} else buf[0] = '\0';
(void)strcat(buf, ".");
(void)strcat(buf, progname);
if ((fp = fopen(buf, "r")) == NULL) {
(void)strcpy(buf, "/etc/");
(void)strcat(buf, progname);
if ((fp = fopen(buf, "r")) == NULL) return 0;
}
rc_file = strdup(buf);
} else {
if ((fp = fopen(color_conf, "r")) == NULL) {
fprintf(stderr, "%s: %s\n", color_conf, strerror(errno));
return -1;
}
rc_file = strdup(color_conf);
}
if (!rc_file) {
fprintf(stderr, "init_colormask: strdup: Out of memory?\n");
(void)fclose(fp);
return -1;
}
rc_line = 0;
cm = &me;
#ifdef HAVE_USE_DEFAULT_COLORS
use_default_colors();
#endif
while (fgets(buf, sizeof(buf), fp) != NULL) {
rc_line++;
if (buf[0] == '\n' || buf[0] == '#') continue;
if ((cp = strchr(buf, '#')) != NULL) {
*cp++ = '\n';
*cp = '\0';
}
memset(cm, 0, sizeof(struct cm_entry));
num = sscanf(buf, "%s %s %s %s\n", s1, s2, s3, s4);
if (num == 2) {
if (strcasecmp(s1, "default")) {
if ((cp = strchr(s1, '/')) != 0) {
*cp++ = '\0';
if ((cp = str2proto(cp, &num)) == 0) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.proto = num;
}
if ((num = str2port(s1, cp)) == -1) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.src.ip_port = num;
cm->in_hdr.dst.ip_port = 0;
if (add_colormask(s2, cm) < 0) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.src.ip_port = 0;
cm->in_hdr.dst.ip_port = num;
if (add_colormask(s2, cm) < 0) {
(void)fclose(fp);
return -1;
}
} else if (add_colormask(s2, 0) < 0) {
(void)fclose(fp);
return -1;
}
} else if (num == 3) {
num = str2addr(s1, 0, &cm->in_hdr.src, &cm->src_mask);
if (num == -1) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.ver = num;
num = str2addr(s2, 0, &cm->in_hdr.dst, &cm->dst_mask);
if (num == -1) {
(void)fclose(fp);
return -1;
}
if (!cm->in_hdr.ver) {
cm->in_hdr.ver = num;
} else if (num && num != cm->in_hdr.ver) {
fprintf(stderr, "%s: line %d: Addresses family mismatch\n",
rc_file, rc_line);
(void)fclose(fp);
return -1;
}
if (add_colormask(s3, cm) < 0) {
(void)fclose(fp);
return -1;
}
} else if (num == 4) {
if ((cp = str2proto(s1, &num)) == 0) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.proto = num;
num = str2addr(s2, cp, &cm->in_hdr.src, &cm->src_mask);
if (num == -1) {
(void)fclose(fp);
return -1;
}
cm->in_hdr.ver = num;
num = str2addr(s3, cp, &cm->in_hdr.dst, &cm->dst_mask);
if (num == -1) {
(void)fclose(fp);
return -1;
}
if (!cm->in_hdr.ver) {
cm->in_hdr.ver = num;
} else if (num && num != cm->in_hdr.ver) {
fprintf(stderr, "%s: line %d: Addresses family mismatch\n",
rc_file, rc_line);
(void)fclose(fp);
return -1;
}
if (add_colormask(s4, cm) < 0) {
(void)fclose(fp);
return -1;
}
} else {
fprintf(stderr, "%s: line %d: Bad format\n",
rc_file, rc_line);
(void)fclose(fp);
return -1;
}
}
(void)fclose(fp);
#ifdef DEBUG
for (cm = color_mask, num = 0; cm && num < n_masks; cm++, num++) {
struct netstat_header nh;
memset(&nh, 0, sizeof(nh));
nh.in_hdr = cm->in_hdr;
hdr2str(&nh, s1, sizeof(s1), s2, sizeof(s2), s3, sizeof(s3));
fprintf(stderr, "%d:", num+1);
fprintf(stderr, " proto=%s", s3);
fprintf(stderr, " src=%s", s1);
fprintf(stderr, " src_mask=%d", cm->src_mask);
fprintf(stderr, " dst=%s", s2);
fprintf(stderr, " dst_mask=%d", cm->dst_mask);
fprintf(stderr, " color_pair=%d\r\n", (int)cm->pair);
}
fflush(stderr);
pause();
#endif
return n_masks;
}
static u_int32_t
netmask(int bits)
{
register u_int32_t mask = 0;
int i;
for (i = 0; i < bits; i++) {
mask >>= 1;
mask |= 0x80000000L;
}
return (u_int32_t)htonl(mask);
}
int
colormask(nh)
const struct netstat_header *nh;
{
/* sanity check */
if (!nh) return A_NORMAL;
if (nh->in_hdr.ver) {
register const struct cm_entry *cm;
int i;
for (cm = color_mask, i = 0; cm && i < n_masks; cm++, i++) {
/* IP version */
if (cm->in_hdr.ver) {
if (nh->in_hdr.ver != cm->in_hdr.ver)
continue;
}
/* IP protocol */
if (cm->in_hdr.proto) {
if (nh->in_hdr.proto != cm->in_hdr.proto)
continue;
}
/* IP source address */
if (cm->in_hdr.src.ip_addr.s_addr) {
if (cm->src_mask) {
u_int32_t mask = netmask(cm->src_mask);
if ((nh->in_hdr.src.ip_addr.s_addr & mask) ^
(cm->in_hdr.src.ip_addr.s_addr & mask))
continue;
} else if (nh->in_hdr.src.ip_addr.s_addr !=
cm->in_hdr.src.ip_addr.s_addr)
continue;
}
/* IP source port */
if (cm->in_hdr.src.ip_port) {
if (nh->in_hdr.src.ip_port != cm->in_hdr.src.ip_port)
continue;
}
/* IP destination address */
if (cm->in_hdr.dst.ip_addr.s_addr) {
if (cm->dst_mask) {
u_int32_t mask = netmask(cm->dst_mask);
if ((nh->in_hdr.dst.ip_addr.s_addr & mask) ^
(cm->in_hdr.dst.ip_addr.s_addr & mask))
continue;
} else if (nh->in_hdr.dst.ip_addr.s_addr !=
cm->in_hdr.dst.ip_addr.s_addr)
continue;
}
/* IP destination port */
if (cm->in_hdr.dst.ip_port) {
if (nh->in_hdr.dst.ip_port != cm->in_hdr.dst.ip_port)
continue;
}
#ifdef HAVE_SLCURSES
return (COLOR_PAIR(cm->pair));
#else
return (COLOR_PAIR(cm->pair) | cm->attr);
#endif
}
}
return A_NORMAL;
}
#endif /* HAVE_HAS_COLORS */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>