xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 1991-2003 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 struct hostdata *addhost();
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(ip6addr)
139 	struct in6_addr *ip6addr;
140 {
141 	struct hostdata6 *h;
142 	struct hostent *hp = NULL;
143 	int error_num;
144 	char addrstr[INET6_ADDRSTRLEN];
145 	const char *addname;
146 	struct hostdata *retval;
147 
148 	for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
149 	    h = h->h6_next) {
150 		if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
151 			return ((struct hostdata *)h);
152 	}
153 
154 	/* not in the hash table, put it in */
155 	if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
156 		return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
157 
158 	/*
159 	 * Set an alarm here so we don't get held up by
160 	 * an unresponsive name server.
161 	 * Give it 3 sec to do its work.
162 	 */
163 	if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
164 		(void) snoop_alarm(3, wakeup);
165 		hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
166 		    AF_INET6, &error_num);
167 		(void) snoop_alarm(0, wakeup);
168 	} else {
169 		hp = NULL;
170 	}
171 
172 	if (hp != NULL)
173 		addname = hp->h_name;
174 	else {
175 		(void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
176 		addname = addrstr;
177 	}
178 
179 	retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
180 	if (hp != NULL)
181 		freehostent(hp);
182 	return (retval);
183 }
184 
185 static struct hostdata *
186 addhost(family, ipaddr, name, aliases)
187 	int family;
188 	void *ipaddr;
189 	char *name;
190 	char **aliases;
191 {
192 	register struct hostdata **hp, *n = NULL;
193 	extern FILE *namefile;
194 	int hashval;
195 	static char aname[128];
196 	char *np;
197 	static struct hostdata h;
198 	int ind;
199 
200 	switch (family) {
201 	case AF_INET:
202 		n = (struct hostdata *)malloc(sizeof (struct hostdata4));
203 		if (n == NULL)
204 			goto alloc_failed;
205 
206 		memset(n, 0, sizeof (struct hostdata4));
207 		n->h_hostname = strdup(name);
208 		if (n->h_hostname == NULL)
209 			goto alloc_failed;
210 
211 		((struct hostdata4 *)n)->h4_addr = *(struct in_addr *)ipaddr;
212 		hashval = ((struct in_addr *)ipaddr)->s_addr;
213 		hp = (struct hostdata **)&h_table4[iphash(hashval)];
214 		break;
215 	case AF_INET6:
216 		n = (struct hostdata *)malloc(sizeof (struct hostdata6));
217 		if (n == NULL)
218 			goto alloc_failed;
219 
220 		memset(n, 0, sizeof (struct hostdata6));
221 		n->h_hostname = strdup(name);
222 		if (n->h_hostname == NULL)
223 			goto alloc_failed;
224 
225 		memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
226 		    sizeof (struct in6_addr));
227 		hashval = ((int *)ipaddr)[3];
228 		hp = (struct hostdata **)&h_table6[iphash(hashval)];
229 		break;
230 	default:
231 		fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
232 		    family);
233 		exit(1);
234 	}
235 
236 	n->h_next = *hp;
237 	*hp = n;
238 
239 	if (namefile != NULL) {
240 		if (family == AF_INET) {
241 			np = inet_ntoa(*(struct in_addr *)ipaddr);
242 			if (np) {
243 				(void) fprintf(namefile, "%s\t%s", np, name);
244 				if (aliases) {
245 					for (ind = 0;
246 					    aliases[ind] != NULL;
247 					    ind++) {
248 						(void) fprintf(namefile, " %s",
249 								aliases[ind]);
250 					}
251 				}
252 				(void) fprintf(namefile, "\n");
253 			}
254 		} else if (family == AF_INET6) {
255 			np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
256 					sizeof (aname));
257 			if (np) {
258 				(void) fprintf(namefile, "%s\t%s", np, name);
259 				if (aliases) {
260 					for (ind = 0;
261 					    aliases[ind] != NULL;
262 					    ind++) {
263 						(void) fprintf(namefile, " %s",
264 								aliases[ind]);
265 					}
266 				}
267 				(void) fprintf(namefile, "\n");
268 			}
269 		} else {
270 			(void) fprintf(stderr, "addhost: unknown family %d\n",
271 				family);
272 		}
273 	}
274 	return (n);
275 
276 alloc_failed:
277 	if (n)
278 		free(n);
279 	(void) fprintf(stderr, "addhost: no mem\n");
280 
281 	aname[0] = '\0';
282 	memset(&h, 0, sizeof (struct hostdata));
283 	h.h_hostname = aname;
284 	return (&h);
285 }
286 
287 char *
288 addrtoname(family, ipaddr)
289 	int family;
290 	void *ipaddr;
291 {
292 	switch (family) {
293 	case AF_INET:
294 		return (iplookup(*(struct in_addr *)ipaddr)->h_hostname);
295 	case AF_INET6:
296 		return (ip6lookup((struct in6_addr *)ipaddr)->h_hostname);
297 	default:
298 		fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
299 		    family);
300 		exit(1);
301 	}
302 }
303 
304 void
305 load_names(fname)
306 	char *fname;
307 {
308 	char buf[1024];
309 	char *addr, *name, *alias;
310 	FILE *f;
311 	unsigned int addrv4;
312 	struct in6_addr addrv6;
313 	int family;
314 	void *naddr;
315 
316 	(void) fprintf(stderr, "Loading name file %s\n", fname);
317 	f = fopen(fname, "r");
318 	if (f == NULL) {
319 		perror(fname);
320 		return;
321 	}
322 
323 	while (fgets(buf, 1024, f) != NULL) {
324 		addr = strtok(buf, SEPARATORS);
325 		if (addr == NULL || *addr == '#')
326 			continue;
327 		if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
328 			family = AF_INET6;
329 			naddr = (void *)&addrv6;
330 		} else if ((addrv4 = inet_addr(addr)) != -1) {
331 			family = AF_INET;
332 			naddr = (void *)&addrv4;
333 		}
334 		name = strtok(NULL, SEPARATORS);
335 		if (name == NULL)
336 			continue;
337 		while ((alias = strtok(NULL, SEPARATORS)) && (*alias != '#')) {
338 			(void) addhost(family, naddr, alias, NULL);
339 		}
340 		(void) addhost(family, naddr, name, NULL);
341 		/* Note: certain addresses such as broadcast are skipped */
342 	}
343 
344 	(void) fclose(f);
345 }
346 
347 /*
348  * lgetipnodebyname: looks up hostname in cached address data. This allows
349  * filtering on hostnames from the .names file to work properly, and
350  * avoids name clashes between domains. Note that only the first of the
351  * ipv4, ipv6, or v4mapped address will be returned, because the
352  * cache does not contain information on multi-homed hosts.
353  */
354 /*ARGSUSED*/
355 struct hostent *
356 lgetipnodebyname(const char *name, int af, int flags, int *error_num)
357 {
358 	int i;
359 	struct hostdata4 *h;
360 	struct hostdata6 *h6;
361 	static struct hostent he;		/* host entry */
362 	static struct in6_addr h46_addr[MAXADDRS];	/* v4mapped address */
363 	static char h_name[MAXHOSTNAMELEN];	/* hostname */
364 	static char *list[MAXADDRS];		/* addr_list array */
365 	struct hostent *hp = &he;
366 	int ind;
367 
368 	(void) memset((char *)hp, 0, sizeof (struct hostent));
369 	hp->h_name = h_name;
370 	h_name[0] = '\0';
371 	strcpy(h_name, name);
372 
373 	hp->h_addrtype = AF_INET6;
374 
375 	hp->h_addr_list = list;
376 	for (i = 0; i < MAXADDRS; i++)
377 		hp->h_addr_list[i] = NULL;
378 	ind = 0;
379 
380 	/* ipv6 lookup */
381 	if (af == AF_INET6) {
382 		hp->h_length = sizeof (struct in6_addr);
383 		for (i = 0; i < MAXHASH; i++) {
384 			for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
385 				if (strcmp(name, h6->h6_hostname) == 0) {
386 					if (ind >= MAXADDRS - 1) {
387 						/* too many addresses */
388 						return (hp);
389 					}
390 					/* found ipv6 addr */
391 					hp->h_addr_list[ind] =
392 						(char *)&h6->h6_addr;
393 					ind++;
394 				}
395 			}
396 		}
397 	}
398 	/* ipv4 or v4mapped lookup */
399 	if (af == AF_INET || (flags & AI_ALL)) {
400 		for (i = 0; i < MAXHASH; i++) {
401 			for (h = h_table4[i]; h; h = h->h4_next) {
402 				if (strcmp(name, h->h4_hostname) == 0) {
403 					if (ind >= MAXADDRS - 1) {
404 						/* too many addresses */
405 						return (hp);
406 					}
407 					if (af == AF_INET) {
408 						/* found ipv4 addr */
409 						hp->h_addrtype = AF_INET;
410 						hp->h_length =
411 						    sizeof (struct in_addr);
412 						hp->h_addr_list[ind] =
413 						    (char *)&h->h4_addr;
414 						ind++;
415 					} else {
416 						/* found v4mapped addr */
417 						hp->h_length =
418 						    sizeof (struct in6_addr);
419 						hp->h_addr_list[ind] =
420 						    (char *)&h46_addr[ind];
421 						IN6_INADDR_TO_V4MAPPED(
422 							&h->h4_addr,
423 							&h46_addr[ind]);
424 						ind++;
425 					}
426 				}
427 			}
428 		}
429 	}
430 	return (ind > 0 ? hp : NULL);
431 }
432