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 46 static sigjmp_buf nisjmp; 47 static hrtime_t snoop_lastwarn; /* Last time NS warning fired */ 48 static 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 54 struct hostdata { 55 struct hostdata *h_next; 56 char *h_hostname; 57 int h_pktsout; 58 int h_pktsin; 59 }; 60 61 struct 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 69 struct 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 77 static struct hostdata *addhost(int, const void *, const char *, char **); 78 79 static struct hostdata4 *h_table4[MAXHASH]; 80 static struct hostdata6 *h_table6[MAXHASH]; 81 82 #define iphash(e) ((e) & (MAXHASH-1)) 83 84 /* ARGSUSED */ 85 static void 86 wakeup(int n) 87 { 88 siglongjmp(nisjmp, 1); 89 } 90 91 extern char *inet_ntoa(); 92 93 static void 94 snoop_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 106 static struct hostdata * 107 iplookup(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 157 static struct hostdata * 158 ip6lookup(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 208 static struct hostdata * 209 addhost(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 296 alloc_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 307 char * 308 addrtoname(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 322 void 323 load_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*/ 373 struct hostent * 374 lgetipnodebyname(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