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