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
wakeup(int n)82 wakeup(int n)
83 {
84 siglongjmp(nisjmp, 1);
85 }
86
87 extern char *inet_ntoa();
88
89 static struct hostdata *
iplookup(struct in_addr ipaddr)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 *
ip6lookup(const struct in6_addr * ip6addr)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 *
addhost(int family,const void * ipaddr,const char * name,char ** aliases)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 *
addrtoname(int family,const void * ipaddr)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
load_names(fname)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 *
lgetipnodebyname(const char * name,int af,int flags,int * error_num)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