1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1980, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #if 0 37 #ifndef lint 38 static char sccsid[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93"; 39 #endif /* not lint */ 40 #endif 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <sys/types.h> 46 #include <sys/socket.h> 47 #include <sys/poll.h> 48 #include <netinet/in.h> 49 #include <arpa/inet.h> 50 #include <ctype.h> 51 #include <err.h> 52 #include <netdb.h> 53 #include <stdarg.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <sysexits.h> 58 #include <unistd.h> 59 #include <fcntl.h> 60 #include <errno.h> 61 62 #define ABUSEHOST "whois.abuse.net" 63 #define ANICHOST "whois.arin.net" 64 #define DENICHOST "whois.denic.de" 65 #define DKNICHOST "whois.dk-hostmaster.dk" 66 #define FNICHOST "whois.afrinic.net" 67 #define GNICHOST "whois.nic.gov" 68 #define IANAHOST "whois.iana.org" 69 #define INICHOST "whois.internic.net" 70 #define KNICHOST "whois.krnic.net" 71 #define LNICHOST "whois.lacnic.net" 72 #define MNICHOST "whois.ra.net" 73 #define PDBHOST "whois.peeringdb.com" 74 #define PNICHOST "whois.apnic.net" 75 #define QNICHOST_TAIL ".whois-servers.net" 76 #define RNICHOST "whois.ripe.net" 77 #define VNICHOST "whois.verisign-grs.com" 78 79 #define DEFAULT_PORT "whois" 80 81 #define WHOIS_RECURSE 0x01 82 #define WHOIS_QUICK 0x02 83 #define WHOIS_SPAM_ME 0x04 84 85 #define CHOPSPAM ">>> Last update of WHOIS database:" 86 87 #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') 88 89 #define SCAN(p, end, check) \ 90 while ((p) < (end)) \ 91 if (check) ++(p); \ 92 else break 93 94 static struct { 95 const char *suffix, *server; 96 } whoiswhere[] = { 97 /* Various handles */ 98 { "-ARIN", ANICHOST }, 99 { "-NICAT", "at" QNICHOST_TAIL }, 100 { "-NORID", "no" QNICHOST_TAIL }, 101 { "-RIPE", RNICHOST }, 102 /* Nominet's whois server doesn't return referrals to JANET */ 103 { ".ac.uk", "ac.uk" QNICHOST_TAIL }, 104 { ".gov.uk", "ac.uk" QNICHOST_TAIL }, 105 { "", IANAHOST }, /* default */ 106 { NULL, NULL } /* safety belt */ 107 }; 108 109 #define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 } 110 static struct { 111 const char *prefix; 112 size_t len; 113 } whois_referral[] = { 114 WHOIS_REFERRAL("whois:"), /* IANA */ 115 WHOIS_REFERRAL("Whois Server:"), 116 WHOIS_REFERRAL("Registrar WHOIS Server:"), /* corporatedomains.com */ 117 WHOIS_REFERRAL("ReferralServer: whois://"), /* ARIN */ 118 WHOIS_REFERRAL("descr: region. Please query"), /* AfriNIC */ 119 { NULL, 0 } 120 }; 121 122 /* 123 * We have a list of patterns for RIRs that assert ignorance rather than 124 * providing referrals. If that happens, we guess that ARIN will be more 125 * helpful. But, before following a referral to an RIR, we check if we have 126 * asked that RIR already, and if so we make another guess. 127 */ 128 static const char *actually_arin[] = { 129 "netname: ERX-NETBLOCK\n", /* APNIC */ 130 "netname: NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK\n", 131 NULL 132 }; 133 134 static struct { 135 int loop; 136 const char *host; 137 } try_rir[] = { 138 { 0, ANICHOST }, 139 { 0, RNICHOST }, 140 { 0, PNICHOST }, 141 { 0, FNICHOST }, 142 { 0, LNICHOST }, 143 { 0, NULL } 144 }; 145 146 static void 147 reset_rir(void) { 148 int i; 149 150 for (i = 0; try_rir[i].host != NULL; i++) 151 try_rir[i].loop = 0; 152 } 153 154 static const char *port = DEFAULT_PORT; 155 156 static const char *choose_server(char *); 157 static struct addrinfo *gethostinfo(char const *host, int exitnoname); 158 static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 159 static void usage(void); 160 static void whois(const char *, const char *, int); 161 162 int 163 main(int argc, char *argv[]) 164 { 165 const char *country, *host; 166 int ch, flags; 167 168 #ifdef SOCKS 169 SOCKSinit(argv[0]); 170 #endif 171 172 country = host = NULL; 173 flags = 0; 174 while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) { 175 switch (ch) { 176 case 'a': 177 host = ANICHOST; 178 break; 179 case 'A': 180 host = PNICHOST; 181 break; 182 case 'b': 183 host = ABUSEHOST; 184 break; 185 case 'c': 186 country = optarg; 187 break; 188 case 'f': 189 host = FNICHOST; 190 break; 191 case 'g': 192 host = GNICHOST; 193 break; 194 case 'h': 195 host = optarg; 196 break; 197 case 'i': 198 host = INICHOST; 199 break; 200 case 'I': 201 host = IANAHOST; 202 break; 203 case 'k': 204 host = KNICHOST; 205 break; 206 case 'l': 207 host = LNICHOST; 208 break; 209 case 'm': 210 host = MNICHOST; 211 break; 212 case 'p': 213 port = optarg; 214 break; 215 case 'P': 216 host = PDBHOST; 217 break; 218 case 'Q': 219 flags |= WHOIS_QUICK; 220 break; 221 case 'r': 222 host = RNICHOST; 223 break; 224 case 'R': 225 flags |= WHOIS_RECURSE; 226 break; 227 case 'S': 228 flags |= WHOIS_SPAM_ME; 229 break; 230 case '?': 231 default: 232 usage(); 233 /* NOTREACHED */ 234 } 235 } 236 argc -= optind; 237 argv += optind; 238 239 if (!argc || (country != NULL && host != NULL)) 240 usage(); 241 242 /* 243 * If no host or country is specified, rely on referrals from IANA. 244 */ 245 if (host == NULL && country == NULL) { 246 if ((host = getenv("WHOIS_SERVER")) == NULL && 247 (host = getenv("RA_SERVER")) == NULL) { 248 if (!(flags & WHOIS_QUICK)) 249 flags |= WHOIS_RECURSE; 250 } 251 } 252 while (argc-- > 0) { 253 if (country != NULL) { 254 char *qnichost; 255 s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 256 whois(*argv, qnichost, flags); 257 free(qnichost); 258 } else 259 whois(*argv, host != NULL ? host : 260 choose_server(*argv), flags); 261 reset_rir(); 262 argv++; 263 } 264 exit(0); 265 } 266 267 static const char * 268 choose_server(char *domain) 269 { 270 size_t len = strlen(domain); 271 int i; 272 273 for (i = 0; whoiswhere[i].suffix != NULL; i++) { 274 size_t suffix_len = strlen(whoiswhere[i].suffix); 275 if (len > suffix_len && 276 strcasecmp(domain + len - suffix_len, 277 whoiswhere[i].suffix) == 0) 278 return (whoiswhere[i].server); 279 } 280 errx(EX_SOFTWARE, "no default whois server"); 281 } 282 283 static struct addrinfo * 284 gethostinfo(char const *host, int exit_on_noname) 285 { 286 struct addrinfo hints, *res; 287 int error; 288 289 memset(&hints, 0, sizeof(hints)); 290 hints.ai_flags = AI_CANONNAME; 291 hints.ai_family = AF_UNSPEC; 292 hints.ai_socktype = SOCK_STREAM; 293 res = NULL; 294 error = getaddrinfo(host, port, &hints, &res); 295 if (error && (exit_on_noname || error != EAI_NONAME)) 296 err(EX_NOHOST, "%s: %s", host, gai_strerror(error)); 297 return (res); 298 } 299 300 /* 301 * Wrapper for asprintf(3) that exits on error. 302 */ 303 static void 304 s_asprintf(char **ret, const char *format, ...) 305 { 306 va_list ap; 307 308 va_start(ap, format); 309 if (vasprintf(ret, format, ap) == -1) { 310 va_end(ap); 311 err(EX_OSERR, "vasprintf()"); 312 } 313 va_end(ap); 314 } 315 316 static int 317 connect_to_any_host(struct addrinfo *hostres) 318 { 319 struct addrinfo *res; 320 nfds_t i, j; 321 size_t count; 322 struct pollfd *fds; 323 int timeout = 180, s = -1; 324 325 for (res = hostres, count = 0; res; res = res->ai_next) 326 count++; 327 fds = calloc(count, sizeof(*fds)); 328 if (fds == NULL) 329 err(EX_OSERR, "calloc()"); 330 331 /* 332 * Traverse the result list elements and make non-block 333 * connection attempts. 334 */ 335 count = i = 0; 336 for (res = hostres; res != NULL; res = res->ai_next) { 337 s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, 338 res->ai_protocol); 339 if (s < 0) 340 continue; 341 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 342 if (errno == EINPROGRESS) { 343 /* Add the socket to poll list */ 344 fds[i].fd = s; 345 fds[i].events = POLLERR | POLLHUP | 346 POLLIN | POLLOUT; 347 /* 348 * From here until a socket connects, the 349 * socket fd is owned by the fds[] poll array. 350 */ 351 s = -1; 352 count++; 353 i++; 354 } else { 355 close(s); 356 s = -1; 357 358 /* 359 * Poll only if we have something to poll, 360 * otherwise just go ahead and try next 361 * address 362 */ 363 if (count == 0) 364 continue; 365 } 366 } else 367 goto done; 368 369 /* 370 * If we are at the last address, poll until a connection is 371 * established or we failed all connection attempts. 372 */ 373 if (res->ai_next == NULL) 374 timeout = INFTIM; 375 376 /* 377 * Poll the watched descriptors for successful connections: 378 * if we still have more untried resolved addresses, poll only 379 * once; otherwise, poll until all descriptors have errors, 380 * which will be considered as ETIMEDOUT later. 381 */ 382 do { 383 int n; 384 385 n = poll(fds, i, timeout); 386 if (n == 0) { 387 /* 388 * No event reported in time. Try with a 389 * smaller timeout (but cap at 2-3ms) 390 * after a new host have been added. 391 */ 392 if (timeout >= 3) 393 timeout >>= 1; 394 395 break; 396 } else if (n < 0) { 397 /* 398 * errno here can only be EINTR which we would 399 * want to clean up and bail out. 400 */ 401 s = -1; 402 goto done; 403 } 404 405 /* 406 * Check for the event(s) we have seen. 407 */ 408 for (j = 0; j < i; j++) { 409 if (fds[j].fd == -1 || fds[j].events == 0 || 410 fds[j].revents == 0) 411 continue; 412 if (fds[j].revents & ~(POLLIN | POLLOUT)) { 413 close(fds[j].fd); 414 fds[j].fd = -1; 415 fds[j].events = 0; 416 count--; 417 continue; 418 } else if (fds[j].revents & (POLLIN | POLLOUT)) { 419 /* Connect succeeded. */ 420 s = fds[j].fd; 421 fds[j].fd = -1; 422 423 goto done; 424 } 425 426 } 427 } while (timeout == INFTIM && count != 0); 428 } 429 430 /* All attempts were failed */ 431 s = -1; 432 if (count == 0) 433 errno = ETIMEDOUT; 434 435 done: 436 /* Close all watched fds except the succeeded one */ 437 for (j = 0; j < i; j++) 438 if (fds[j].fd != -1) 439 close(fds[j].fd); 440 free(fds); 441 return (s); 442 } 443 444 static void 445 whois(const char *query, const char *hostname, int flags) 446 { 447 FILE *fp; 448 struct addrinfo *hostres; 449 char *buf, *host, *nhost, *p; 450 int comment, s, f; 451 size_t len, i; 452 453 hostres = gethostinfo(hostname, 1); 454 s = connect_to_any_host(hostres); 455 if (s == -1) 456 err(EX_OSERR, "connect()"); 457 458 /* Restore default blocking behavior. */ 459 if ((f = fcntl(s, F_GETFL)) == -1) 460 err(EX_OSERR, "fcntl()"); 461 f &= ~O_NONBLOCK; 462 if (fcntl(s, F_SETFL, f) == -1) 463 err(EX_OSERR, "fcntl()"); 464 465 fp = fdopen(s, "r+"); 466 if (fp == NULL) 467 err(EX_OSERR, "fdopen()"); 468 469 if (!(flags & WHOIS_SPAM_ME) && 470 (strcasecmp(hostname, DENICHOST) == 0 || 471 strcasecmp(hostname, "de" QNICHOST_TAIL) == 0)) { 472 const char *q; 473 int idn = 0; 474 for (q = query; *q != '\0'; q++) 475 if (!isascii(*q)) 476 idn = 1; 477 fprintf(fp, "-T dn%s %s\r\n", idn ? "" : ",ace", query); 478 } else if (!(flags & WHOIS_SPAM_ME) && 479 (strcasecmp(hostname, DKNICHOST) == 0 || 480 strcasecmp(hostname, "dk" QNICHOST_TAIL) == 0)) 481 fprintf(fp, "--show-handles %s\r\n", query); 482 else if ((flags & WHOIS_SPAM_ME) || 483 strchr(query, ' ') != NULL) 484 fprintf(fp, "%s\r\n", query); 485 else if (strcasecmp(hostname, ANICHOST) == 0) { 486 if (strncasecmp(query, "AS", 2) == 0 && 487 strspn(query+2, "0123456789") == strlen(query+2)) 488 fprintf(fp, "+ a %s\r\n", query+2); 489 else 490 fprintf(fp, "+ %s\r\n", query); 491 } else if (strcasecmp(hostres->ai_canonname, VNICHOST) == 0) 492 fprintf(fp, "domain %s\r\n", query); 493 else 494 fprintf(fp, "%s\r\n", query); 495 fflush(fp); 496 497 comment = 0; 498 if (!(flags & WHOIS_SPAM_ME) && 499 (strcasecmp(hostname, ANICHOST) == 0 || 500 strcasecmp(hostname, RNICHOST) == 0)) { 501 comment = 2; 502 } 503 504 nhost = NULL; 505 while ((buf = fgetln(fp, &len)) != NULL) { 506 /* Nominet */ 507 if (!(flags & WHOIS_SPAM_ME) && 508 len == 5 && strncmp(buf, "-- \r\n", 5) == 0) 509 break; 510 /* RIRs */ 511 if (comment == 1 && buf[0] == '#') 512 break; 513 else if (comment == 2) { 514 if (strchr("#%\r\n", buf[0]) != NULL) 515 continue; 516 else 517 comment = 1; 518 } 519 520 printf("%.*s", (int)len, buf); 521 522 if ((flags & WHOIS_RECURSE) && nhost == NULL) { 523 for (i = 0; whois_referral[i].prefix != NULL; i++) { 524 p = buf; 525 SCAN(p, buf+len, *p == ' '); 526 if (strncasecmp(p, whois_referral[i].prefix, 527 whois_referral[i].len) != 0) 528 continue; 529 p += whois_referral[i].len; 530 SCAN(p, buf+len, *p == ' '); 531 host = p; 532 SCAN(p, buf+len, ishost(*p)); 533 if (p > host) 534 s_asprintf(&nhost, "%.*s", 535 (int)(p - host), host); 536 break; 537 } 538 for (i = 0; actually_arin[i] != NULL; i++) { 539 if (strncmp(buf, actually_arin[i], len) == 0) { 540 s_asprintf(&nhost, "%s", ANICHOST); 541 break; 542 } 543 } 544 } 545 /* Verisign etc. */ 546 if (!(flags & WHOIS_SPAM_ME) && 547 len >= sizeof(CHOPSPAM)-1 && 548 (strncasecmp(buf, CHOPSPAM, sizeof(CHOPSPAM)-1) == 0 || 549 strncasecmp(buf, CHOPSPAM+4, sizeof(CHOPSPAM)-5) == 0)) { 550 printf("\n"); 551 break; 552 } 553 } 554 fclose(fp); 555 freeaddrinfo(hostres); 556 557 f = 0; 558 for (i = 0; try_rir[i].host != NULL; i++) { 559 /* Remember visits to RIRs */ 560 if (try_rir[i].loop == 0 && 561 strcasecmp(try_rir[i].host, hostname) == 0) 562 try_rir[i].loop = 1; 563 /* Do we need to find an alternative RIR? */ 564 if (try_rir[i].loop != 0 && nhost != NULL && 565 strcasecmp(try_rir[i].host, nhost) == 0) { 566 free(nhost); 567 nhost = NULL; 568 f = 1; 569 } 570 } 571 if (f) { 572 /* Find a replacement RIR */ 573 for (i = 0; try_rir[i].host != NULL; i++) { 574 if (try_rir[i].loop == 0) { 575 s_asprintf(&nhost, "%s", 576 try_rir[i].host); 577 break; 578 } 579 } 580 } 581 if (nhost != NULL) { 582 /* Ignore self-referrals */ 583 if (strcasecmp(hostname, nhost) != 0) { 584 printf("# %s\n\n", nhost); 585 whois(query, nhost, flags); 586 } 587 free(nhost); 588 } 589 } 590 591 static void 592 usage(void) 593 { 594 fprintf(stderr, 595 "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] " 596 "[-p port] name ...\n"); 597 exit(EX_USAGE); 598 } 599