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