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