xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c (revision 2e3b64671f0fdac42d7fb21a8fa7e3ce9fce3359)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS */
28 
29 #include <stdio.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 
44 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, void *, char *, char **);
74 
75 struct hostdata4 *h_table4[MAXHASH];
76 struct hostdata6 *h_table6[MAXHASH];
77 
78 #define	iphash(e)  ((e) & (MAXHASH-1))
79 
80 static void
81 wakeup(n)
82 	int n;
83 {
84 	siglongjmp(nisjmp, 1);
85 }
86 
87 extern char *inet_ntoa();
88 extern boolean_t rflg;
89 
90 static struct hostdata *
91 iplookup(struct in_addr ipaddr)
92 {
93 	register struct hostdata4 *h;
94 	struct hostent *hp = NULL;
95 	struct netent *np;
96 	int error_num;
97 	struct hostdata *retval;
98 
99 	for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
100 		if (h->h4_addr.s_addr == ipaddr.s_addr)
101 			return ((struct hostdata *)h);
102 	}
103 
104 	/* not found.  Put it in */
105 
106 	if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
107 		return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
108 	if (ipaddr.s_addr == htonl(INADDR_ANY))
109 		return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
110 
111 	/*
112 	 * Set an alarm here so we don't get held up by
113 	 * an unresponsive name server.
114 	 * Give it 3 sec to do its work.
115 	 */
116 	if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
117 		(void) snoop_alarm(3, wakeup);
118 		hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
119 			AF_INET, &error_num);
120 		if (hp == NULL && inet_lnaof(ipaddr) == 0) {
121 			np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
122 			if (np)
123 				return (addhost(AF_INET, &ipaddr, np->n_name,
124 					np->n_aliases));
125 		}
126 		(void) snoop_alarm(0, wakeup);
127 	}
128 
129 	retval = addhost(AF_INET, &ipaddr,
130 	    hp ? hp->h_name : inet_ntoa(ipaddr),
131 	    hp ? hp->h_aliases : NULL);
132 	if (hp != NULL)
133 		freehostent(hp);
134 	return (retval);
135 }
136 
137 static struct hostdata *
138 ip6lookup(struct in6_addr *ip6addr)
139 {
140 	struct hostdata6 *h;
141 	struct hostent *hp = NULL;
142 	int error_num;
143 	char addrstr[INET6_ADDRSTRLEN];
144 	char *addname;
145 	struct hostdata *retval;
146 
147 	for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
148 	    h = h->h6_next) {
149 		if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
150 			return ((struct hostdata *)h);
151 	}
152 
153 	/* not in the hash table, put it in */
154 	if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
155 		return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
156 
157 	/*
158 	 * Set an alarm here so we don't get held up by
159 	 * an unresponsive name server.
160 	 * Give it 3 sec to do its work.
161 	 */
162 	if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
163 		(void) snoop_alarm(3, wakeup);
164 		hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
165 		    AF_INET6, &error_num);
166 		(void) snoop_alarm(0, wakeup);
167 	} else {
168 		hp = NULL;
169 	}
170 
171 	if (hp != NULL)
172 		addname = hp->h_name;
173 	else {
174 		(void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
175 		addname = addrstr;
176 	}
177 
178 	retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
179 	if (hp != NULL)
180 		freehostent(hp);
181 	return (retval);
182 }
183 
184 static struct hostdata *
185 addhost(int family, void *ipaddr, char *name, char **aliases)
186 {
187 	register struct hostdata **hp, *n = NULL;
188 	extern FILE *namefile;
189 	int hashval;
190 	static char aname[128];
191 	char *np;
192 	static struct hostdata h;
193 	int ind;
194 
195 	switch (family) {
196 	case AF_INET:
197 		n = (struct hostdata *)malloc(sizeof (struct hostdata4));
198 		if (n == NULL)
199 			goto alloc_failed;
200 
201 		memset(n, 0, sizeof (struct hostdata4));
202 		n->h_hostname = strdup(name);
203 		if (n->h_hostname == NULL)
204 			goto alloc_failed;
205 
206 		((struct hostdata4 *)n)->h4_addr = *(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 = ((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(*(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(family, ipaddr)
284 	int family;
285 	void *ipaddr;
286 {
287 	switch (family) {
288 	case AF_INET:
289 		return (iplookup(*(struct in_addr *)ipaddr)->h_hostname);
290 	case AF_INET6:
291 		return (ip6lookup((struct in6_addr *)ipaddr)->h_hostname);
292 	default:
293 		fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
294 		    family);
295 		exit(1);
296 	}
297 	/* Never reached... */
298 	return (NULL);
299 }
300 
301 void
302 load_names(fname)
303 	char *fname;
304 {
305 	char buf[1024];
306 	char *addr, *name, *alias;
307 	FILE *f;
308 	unsigned int addrv4;
309 	struct in6_addr addrv6;
310 	int family;
311 	void *naddr;
312 
313 	(void) fprintf(stderr, "Loading name file %s\n", fname);
314 	f = fopen(fname, "r");
315 	if (f == NULL) {
316 		perror(fname);
317 		return;
318 	}
319 
320 	while (fgets(buf, 1024, f) != NULL) {
321 		addr = strtok(buf, SEPARATORS);
322 		if (addr == NULL || *addr == '#')
323 			continue;
324 		if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
325 			family = AF_INET6;
326 			naddr = (void *)&addrv6;
327 		} else if ((addrv4 = inet_addr(addr)) != -1) {
328 			family = AF_INET;
329 			naddr = (void *)&addrv4;
330 		}
331 		name = strtok(NULL, SEPARATORS);
332 		if (name == NULL)
333 			continue;
334 		while ((alias = strtok(NULL, SEPARATORS)) && (*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