xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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