Home | History | Annotate | Download | only in snoop
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <ctype.h>
     29 #include <sys/types.h>
     30 #include <sys/socket.h>
     31 #include <sys/sockio.h>
     32 #include <net/if.h>
     33 #include <netinet/in_systm.h>
     34 #include <netinet/in.h>
     35 #include <netinet/if_ether.h>
     36 #include <netinet/ip.h>
     37 #include <netdb.h>
     38 #include <string.h>
     39 #include <signal.h>
     40 #include <setjmp.h>
     41 #include <arpa/inet.h>
     42 #include "snoop.h"
     43 
     44 static sigjmp_buf nisjmp;
     45 
     46 #define	MAXHASH 1024  /* must be a power of 2 */
     47 
     48 #define	SEPARATORS " \t\n"
     49 
     50 struct hostdata {
     51 	struct hostdata	*h_next;
     52 	char		*h_hostname;
     53 	int		h_pktsout;
     54 	int		h_pktsin;
     55 };
     56 
     57 struct hostdata4 {
     58 	struct hostdata4	*h4_next;
     59 	char		*h4_hostname;
     60 	int		h4_pktsout;
     61 	int		h4_pktsin;
     62 	struct in_addr	h4_addr;
     63 };
     64 
     65 struct hostdata6 {
     66 	struct hostdata6	*h6_next;
     67 	char		*h6_hostname;
     68 	int		h6_pktsout;
     69 	int		h6_pktsin;
     70 	struct in6_addr	h6_addr;
     71 };
     72 
     73 static struct hostdata *addhost(int, const void *, const char *, char **);
     74 
     75 static struct hostdata4 *h_table4[MAXHASH];
     76 static struct hostdata6 *h_table6[MAXHASH];
     77 
     78 #define	iphash(e)  ((e) & (MAXHASH-1))
     79 
     80 /* ARGSUSED */
     81 static void
     82 wakeup(int n)
     83 {
     84 	siglongjmp(nisjmp, 1);
     85 }
     86 
     87 extern char *inet_ntoa();
     88 
     89 static struct hostdata *
     90 iplookup(struct in_addr ipaddr)
     91 {
     92 	register struct hostdata4 *h;
     93 	struct hostent *hp = NULL;
     94 	struct netent *np;
     95 	int error_num;
     96 	struct hostdata *retval;
     97 
     98 	for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
     99 		if (h->h4_addr.s_addr == ipaddr.s_addr)
    100 			return ((struct hostdata *)h);
    101 	}
    102 
    103 	/* not found.  Put it in */
    104 
    105 	if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
    106 		return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
    107 	if (ipaddr.s_addr == htonl(INADDR_ANY))
    108 		return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
    109 
    110 	/*
    111 	 * Set an alarm here so we don't get held up by
    112 	 * an unresponsive name server.
    113 	 * Give it 3 sec to do its work.
    114 	 */
    115 	if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
    116 		(void) snoop_alarm(3, wakeup);
    117 		hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
    118 		    AF_INET, &error_num);
    119 		if (hp == NULL && inet_lnaof(ipaddr) == 0) {
    120 			np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
    121 			if (np)
    122 				return (addhost(AF_INET, &ipaddr, np->n_name,
    123 				    np->n_aliases));
    124 		}
    125 		(void) snoop_alarm(0, wakeup);
    126 	}
    127 
    128 	retval = addhost(AF_INET, &ipaddr,
    129 	    hp ? hp->h_name : inet_ntoa(ipaddr),
    130 	    hp ? hp->h_aliases : NULL);
    131 	if (hp != NULL)
    132 		freehostent(hp);
    133 	return (retval);
    134 }
    135 
    136 static struct hostdata *
    137 ip6lookup(const struct in6_addr *ip6addr)
    138 {
    139 	struct hostdata6 *h;
    140 	struct hostent *hp = NULL;
    141 	int error_num;
    142 	char addrstr[INET6_ADDRSTRLEN];
    143 	char *addname;
    144 	struct hostdata *retval;
    145 
    146 	for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
    147 	    h = h->h6_next) {
    148 		if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
    149 			return ((struct hostdata *)h);
    150 	}
    151 
    152 	/* not in the hash table, put it in */
    153 	if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
    154 		return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
    155 
    156 	/*
    157 	 * Set an alarm here so we don't get held up by
    158 	 * an unresponsive name server.
    159 	 * Give it 3 sec to do its work.
    160 	 */
    161 	if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
    162 		(void) snoop_alarm(3, wakeup);
    163 		hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
    164 		    AF_INET6, &error_num);
    165 		(void) snoop_alarm(0, wakeup);
    166 	} else {
    167 		hp = NULL;
    168 	}
    169 
    170 	if (hp != NULL)
    171 		addname = hp->h_name;
    172 	else {
    173 		(void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
    174 		addname = addrstr;
    175 	}
    176 
    177 	retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
    178 	if (hp != NULL)
    179 		freehostent(hp);
    180 	return (retval);
    181 }
    182 
    183 static struct hostdata *
    184 addhost(int family, const void *ipaddr, const char *name, char **aliases)
    185 {
    186 	struct hostdata **hp, *n = NULL;
    187 	extern FILE *namefile;
    188 	int hashval;
    189 	static char aname[128];
    190 	char *np;
    191 	static struct hostdata h;
    192 	int ind;
    193 
    194 	switch (family) {
    195 	case AF_INET:
    196 		n = (struct hostdata *)malloc(sizeof (struct hostdata4));
    197 		if (n == NULL)
    198 			goto alloc_failed;
    199 
    200 		memset(n, 0, sizeof (struct hostdata4));
    201 		n->h_hostname = strdup(name);
    202 		if (n->h_hostname == NULL)
    203 			goto alloc_failed;
    204 
    205 		((struct hostdata4 *)n)->h4_addr =
    206 		    *(const struct in_addr *)ipaddr;
    207 		hashval = ((struct in_addr *)ipaddr)->s_addr;
    208 		hp = (struct hostdata **)&h_table4[iphash(hashval)];
    209 		break;
    210 	case AF_INET6:
    211 		n = (struct hostdata *)malloc(sizeof (struct hostdata6));
    212 		if (n == NULL)
    213 			goto alloc_failed;
    214 
    215 		memset(n, 0, sizeof (struct hostdata6));
    216 		n->h_hostname = strdup(name);
    217 		if (n->h_hostname == NULL)
    218 			goto alloc_failed;
    219 
    220 		memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
    221 		    sizeof (struct in6_addr));
    222 		hashval = ((const int *)ipaddr)[3];
    223 		hp = (struct hostdata **)&h_table6[iphash(hashval)];
    224 		break;
    225 	default:
    226 		fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
    227 		    family);
    228 		exit(1);
    229 	}
    230 
    231 	n->h_next = *hp;
    232 	*hp = n;
    233 
    234 	if (namefile != NULL) {
    235 		if (family == AF_INET) {
    236 			np = inet_ntoa(*(const struct in_addr *)ipaddr);
    237 			if (np) {
    238 				(void) fprintf(namefile, "%s\t%s", np, name);
    239 				if (aliases) {
    240 					for (ind = 0;
    241 					    aliases[ind] != NULL;
    242 					    ind++) {
    243 						(void) fprintf(namefile, " %s",
    244 						    aliases[ind]);
    245 					}
    246 				}
    247 				(void) fprintf(namefile, "\n");
    248 			}
    249 		} else if (family == AF_INET6) {
    250 			np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
    251 			    sizeof (aname));
    252 			if (np) {
    253 				(void) fprintf(namefile, "%s\t%s", np, name);
    254 				if (aliases) {
    255 					for (ind = 0;
    256 					    aliases[ind] != NULL;
    257 					    ind++) {
    258 						(void) fprintf(namefile, " %s",
    259 						    aliases[ind]);
    260 					}
    261 				}
    262 				(void) fprintf(namefile, "\n");
    263 			}
    264 		} else {
    265 			(void) fprintf(stderr, "addhost: unknown family %d\n",
    266 			    family);
    267 		}
    268 	}
    269 	return (n);
    270 
    271 alloc_failed:
    272 	if (n)
    273 		free(n);
    274 	(void) fprintf(stderr, "addhost: no mem\n");
    275 
    276 	aname[0] = '\0';
    277 	memset(&h, 0, sizeof (struct hostdata));
    278 	h.h_hostname = aname;
    279 	return (&h);
    280 }
    281 
    282 char *
    283 addrtoname(int family, const void *ipaddr)
    284 {
    285 	switch (family) {
    286 	case AF_INET:
    287 		return (iplookup(*(const struct in_addr *)ipaddr)->h_hostname);
    288 	case AF_INET6:
    289 		return (ip6lookup((const struct in6_addr *)ipaddr)->h_hostname);
    290 	}
    291 	(void) fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
    292 	    family);
    293 	exit(1);
    294 	/* NOTREACHED */
    295 }
    296 
    297 void
    298 load_names(fname)
    299 	char *fname;
    300 {
    301 	char buf[1024];
    302 	char *addr, *name, *alias;
    303 	FILE *f;
    304 	unsigned int addrv4;
    305 	struct in6_addr addrv6;
    306 	int family;
    307 	void *naddr;
    308 
    309 	(void) fprintf(stderr, "Loading name file %s\n", fname);
    310 	f = fopen(fname, "r");
    311 	if (f == NULL) {
    312 		perror(fname);
    313 		return;
    314 	}
    315 
    316 	while (fgets(buf, 1024, f) != NULL) {
    317 		addr = strtok(buf, SEPARATORS);
    318 		if (addr == NULL || *addr == '#')
    319 			continue;
    320 		if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
    321 			family = AF_INET6;
    322 			naddr = (void *)&addrv6;
    323 		} else if ((addrv4 = inet_addr(addr)) != (ulong_t)-1) {
    324 			family = AF_INET;
    325 			naddr = (void *)&addrv4;
    326 		}
    327 		name = strtok(NULL, SEPARATORS);
    328 		if (name == NULL)
    329 			continue;
    330 		while ((alias = strtok(NULL, SEPARATORS)) != NULL &&
    331 		    (*alias != '#')) {
    332 			(void) addhost(family, naddr, alias, NULL);
    333 		}
    334 		(void) addhost(family, naddr, name, NULL);
    335 		/* Note: certain addresses such as broadcast are skipped */
    336 	}
    337 
    338 	(void) fclose(f);
    339 }
    340 
    341 /*
    342  * lgetipnodebyname: looks up hostname in cached address data. This allows
    343  * filtering on hostnames from the .names file to work properly, and
    344  * avoids name clashes between domains. Note that only the first of the
    345  * ipv4, ipv6, or v4mapped address will be returned, because the
    346  * cache does not contain information on multi-homed hosts.
    347  */
    348 /*ARGSUSED*/
    349 struct hostent *
    350 lgetipnodebyname(const char *name, int af, int flags, int *error_num)
    351 {
    352 	int i;
    353 	struct hostdata4 *h;
    354 	struct hostdata6 *h6;
    355 	static struct hostent he;		/* host entry */
    356 	static struct in6_addr h46_addr[MAXADDRS];	/* v4mapped address */
    357 	static char h_name[MAXHOSTNAMELEN];	/* hostname */
    358 	static char *list[MAXADDRS];		/* addr_list array */
    359 	struct hostent *hp = &he;
    360 	int ind;
    361 
    362 	(void) memset((char *)hp, 0, sizeof (struct hostent));
    363 	hp->h_name = h_name;
    364 	h_name[0] = '\0';
    365 	strcpy(h_name, name);
    366 
    367 	hp->h_addrtype = AF_INET6;
    368 
    369 	hp->h_addr_list = list;
    370 	for (i = 0; i < MAXADDRS; i++)
    371 		hp->h_addr_list[i] = NULL;
    372 	ind = 0;
    373 
    374 	/* ipv6 lookup */
    375 	if (af == AF_INET6) {
    376 		hp->h_length = sizeof (struct in6_addr);
    377 		for (i = 0; i < MAXHASH; i++) {
    378 			for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
    379 				if (strcmp(name, h6->h6_hostname) == 0) {
    380 					if (ind >= MAXADDRS - 1) {
    381 						/* too many addresses */
    382 						return (hp);
    383 					}
    384 					/* found ipv6 addr */
    385 					hp->h_addr_list[ind] =
    386 					    (char *)&h6->h6_addr;
    387 					ind++;
    388 				}
    389 			}
    390 		}
    391 	}
    392 	/* ipv4 or v4mapped lookup */
    393 	if (af == AF_INET || (flags & AI_ALL)) {
    394 		for (i = 0; i < MAXHASH; i++) {
    395 			for (h = h_table4[i]; h; h = h->h4_next) {
    396 				if (strcmp(name, h->h4_hostname) == 0) {
    397 					if (ind >= MAXADDRS - 1) {
    398 						/* too many addresses */
    399 						return (hp);
    400 					}
    401 					if (af == AF_INET) {
    402 						/* found ipv4 addr */
    403 						hp->h_addrtype = AF_INET;
    404 						hp->h_length =
    405 						    sizeof (struct in_addr);
    406 						hp->h_addr_list[ind] =
    407 						    (char *)&h->h4_addr;
    408 						ind++;
    409 					} else {
    410 						/* found v4mapped addr */
    411 						hp->h_length =
    412 						    sizeof (struct in6_addr);
    413 						hp->h_addr_list[ind] =
    414 						    (char *)&h46_addr[ind];
    415 						IN6_INADDR_TO_V4MAPPED(
    416 						    &h->h4_addr,
    417 						    &h46_addr[ind]);
    418 						ind++;
    419 					}
    420 				}
    421 			}
    422 		}
    423 	}
    424 	return (ind > 0 ? hp : NULL);
    425 }
    426