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("descr: region. Please query"), /* AfriNIC */ 121 { NULL, 0 } 122 }; 123 124 /* 125 * We have a list of patterns for RIRs that assert ignorance rather than 126 * providing referrals. If that happens, we guess that ARIN will be more 127 * helpful. But, before following a referral to an RIR, we check if we have 128 * asked that RIR already, and if so we make another guess. 129 */ 130 static const char *actually_arin[] = { 131 "netname: ERX-NETBLOCK\n", /* APNIC */ 132 "netname: NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK\n", 133 NULL 134 }; 135 136 static struct { 137 int loop; 138 const char *host; 139 } try_rir[] = { 140 { 0, ANICHOST }, 141 { 0, RNICHOST }, 142 { 0, PNICHOST }, 143 { 0, FNICHOST }, 144 { 0, LNICHOST }, 145 { 0, NULL } 146 }; 147 148 static void 149 reset_rir(void) { 150 int i; 151 152 for (i = 0; try_rir[i].host != NULL; i++) 153 try_rir[i].loop = 0; 154 } 155 156 static const char *port = DEFAULT_PORT; 157 158 static const char *choose_server(char *); 159 static struct addrinfo *gethostinfo(char const *host, int exitnoname); 160 static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 161 static void usage(void); 162 static void whois(const char *, const char *, int); 163 164 int 165 main(int argc, char *argv[]) 166 { 167 const char *country, *host; 168 int ch, flags; 169 170 #ifdef SOCKS 171 SOCKSinit(argv[0]); 172 #endif 173 174 country = host = NULL; 175 flags = 0; 176 while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) { 177 switch (ch) { 178 case 'a': 179 host = ANICHOST; 180 break; 181 case 'A': 182 host = PNICHOST; 183 break; 184 case 'b': 185 host = ABUSEHOST; 186 break; 187 case 'c': 188 country = optarg; 189 break; 190 case 'f': 191 host = FNICHOST; 192 break; 193 case 'g': 194 host = GNICHOST; 195 break; 196 case 'h': 197 host = optarg; 198 break; 199 case 'i': 200 host = INICHOST; 201 break; 202 case 'I': 203 host = IANAHOST; 204 break; 205 case 'k': 206 host = KNICHOST; 207 break; 208 case 'l': 209 host = LNICHOST; 210 break; 211 case 'm': 212 host = MNICHOST; 213 break; 214 case 'p': 215 port = optarg; 216 break; 217 case 'P': 218 host = PDBHOST; 219 break; 220 case 'Q': 221 flags |= WHOIS_QUICK; 222 break; 223 case 'r': 224 host = RNICHOST; 225 break; 226 case 'R': 227 flags |= WHOIS_RECURSE; 228 break; 229 case 'S': 230 flags |= WHOIS_SPAM_ME; 231 break; 232 case '?': 233 default: 234 usage(); 235 /* NOTREACHED */ 236 } 237 } 238 argc -= optind; 239 argv += optind; 240 241 if (!argc || (country != NULL && host != NULL)) 242 usage(); 243 244 /* 245 * If no host or country is specified, rely on referrals from IANA. 246 */ 247 if (host == NULL && country == NULL) { 248 if ((host = getenv("WHOIS_SERVER")) == NULL && 249 (host = getenv("RA_SERVER")) == NULL) { 250 if (!(flags & WHOIS_QUICK)) 251 flags |= WHOIS_RECURSE; 252 } 253 } 254 while (argc-- > 0) { 255 if (country != NULL) { 256 char *qnichost; 257 s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 258 whois(*argv, qnichost, flags); 259 free(qnichost); 260 } else 261 whois(*argv, host != NULL ? host : 262 choose_server(*argv), flags); 263 reset_rir(); 264 argv++; 265 } 266 exit(0); 267 } 268 269 static const char * 270 choose_server(char *domain) 271 { 272 size_t len = strlen(domain); 273 int i; 274 275 for (i = 0; whoiswhere[i].suffix != NULL; i++) { 276 size_t suffix_len = strlen(whoiswhere[i].suffix); 277 if (len > suffix_len && 278 strcasecmp(domain + len - suffix_len, 279 whoiswhere[i].suffix) == 0) 280 return (whoiswhere[i].server); 281 } 282 errx(EX_SOFTWARE, "no default whois server"); 283 } 284 285 static struct addrinfo * 286 gethostinfo(char const *host, int exit_on_noname) 287 { 288 struct addrinfo hints, *res; 289 int error; 290 291 memset(&hints, 0, sizeof(hints)); 292 hints.ai_flags = AI_CANONNAME; 293 hints.ai_family = AF_UNSPEC; 294 hints.ai_socktype = SOCK_STREAM; 295 res = NULL; 296 error = getaddrinfo(host, port, &hints, &res); 297 if (error && (exit_on_noname || error != EAI_NONAME)) 298 err(EX_NOHOST, "%s: %s", host, gai_strerror(error)); 299 return (res); 300 } 301 302 /* 303 * Wrapper for asprintf(3) that exits on error. 304 */ 305 static void 306 s_asprintf(char **ret, const char *format, ...) 307 { 308 va_list ap; 309 310 va_start(ap, format); 311 if (vasprintf(ret, format, ap) == -1) { 312 va_end(ap); 313 err(EX_OSERR, "vasprintf()"); 314 } 315 va_end(ap); 316 } 317 318 static int 319 connect_to_any_host(struct addrinfo *hostres) 320 { 321 struct addrinfo *res; 322 nfds_t i, j; 323 size_t count; 324 struct pollfd *fds; 325 int timeout = 180, s = -1; 326 327 for (res = hostres, count = 0; res; res = res->ai_next) 328 count++; 329 fds = calloc(count, sizeof(*fds)); 330 if (fds == NULL) 331 err(EX_OSERR, "calloc()"); 332 333 /* 334 * Traverse the result list elements and make non-block 335 * connection attempts. 336 */ 337 count = i = 0; 338 for (res = hostres; res != NULL; res = res->ai_next) { 339 s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, 340 res->ai_protocol); 341 if (s < 0) 342 continue; 343 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 344 if (errno == EINPROGRESS) { 345 /* Add the socket to poll list */ 346 fds[i].fd = s; 347 fds[i].events = POLLERR | POLLHUP | 348 POLLIN | POLLOUT; 349 /* 350 * From here until a socket connects, the 351 * socket fd is owned by the fds[] poll array. 352 */ 353 s = -1; 354 count++; 355 i++; 356 } else { 357 close(s); 358 s = -1; 359 360 /* 361 * Poll only if we have something to poll, 362 * otherwise just go ahead and try next 363 * address 364 */ 365 if (count == 0) 366 continue; 367 } 368 } else 369 goto done; 370 371 /* 372 * If we are at the last address, poll until a connection is 373 * established or we failed all connection attempts. 374 */ 375 if (res->ai_next == NULL) 376 timeout = INFTIM; 377 378 /* 379 * Poll the watched descriptors for successful connections: 380 * if we still have more untried resolved addresses, poll only 381 * once; otherwise, poll until all descriptors have errors, 382 * which will be considered as ETIMEDOUT later. 383 */ 384 do { 385 int n; 386 387 n = poll(fds, i, timeout); 388 if (n == 0) { 389 /* 390 * No event reported in time. Try with a 391 * smaller timeout (but cap at 2-3ms) 392 * after a new host have been added. 393 */ 394 if (timeout >= 3) 395 timeout >>= 1; 396 397 break; 398 } else if (n < 0) { 399 /* 400 * errno here can only be EINTR which we would 401 * want to clean up and bail out. 402 */ 403 s = -1; 404 goto done; 405 } 406 407 /* 408 * Check for the event(s) we have seen. 409 */ 410 for (j = 0; j < i; j++) { 411 if (fds[j].fd == -1 || fds[j].events == 0 || 412 fds[j].revents == 0) 413 continue; 414 if (fds[j].revents & ~(POLLIN | POLLOUT)) { 415 close(fds[j].fd); 416 fds[j].fd = -1; 417 fds[j].events = 0; 418 count--; 419 continue; 420 } else if (fds[j].revents & (POLLIN | POLLOUT)) { 421 /* Connect succeeded. */ 422 s = fds[j].fd; 423 fds[j].fd = -1; 424 425 goto done; 426 } 427 428 } 429 } while (timeout == INFTIM && count != 0); 430 } 431 432 /* All attempts were failed */ 433 s = -1; 434 if (count == 0) 435 errno = ETIMEDOUT; 436 437 done: 438 /* Close all watched fds except the succeeded one */ 439 for (j = 0; j < i; j++) 440 if (fds[j].fd != -1) 441 close(fds[j].fd); 442 free(fds); 443 return (s); 444 } 445 446 static void 447 whois(const char *query, const char *hostname, int flags) 448 { 449 FILE *fp; 450 struct addrinfo *hostres; 451 char *buf, *host, *nhost, *p; 452 int comment, s, f; 453 size_t len, i; 454 455 hostres = gethostinfo(hostname, 1); 456 s = connect_to_any_host(hostres); 457 if (s == -1) 458 err(EX_OSERR, "connect()"); 459 460 /* Restore default blocking behavior. */ 461 if ((f = fcntl(s, F_GETFL)) == -1) 462 err(EX_OSERR, "fcntl()"); 463 f &= ~O_NONBLOCK; 464 if (fcntl(s, F_SETFL, f) == -1) 465 err(EX_OSERR, "fcntl()"); 466 467 fp = fdopen(s, "r+"); 468 if (fp == NULL) 469 err(EX_OSERR, "fdopen()"); 470 471 if (!(flags & WHOIS_SPAM_ME) && 472 (strcasecmp(hostname, DENICHOST) == 0 || 473 strcasecmp(hostname, "de" QNICHOST_TAIL) == 0)) { 474 const char *q; 475 int idn = 0; 476 for (q = query; *q != '\0'; q++) 477 if (!isascii(*q)) 478 idn = 1; 479 fprintf(fp, "-T dn%s %s\r\n", idn ? "" : ",ace", query); 480 } else if (!(flags & WHOIS_SPAM_ME) && 481 (strcasecmp(hostname, DKNICHOST) == 0 || 482 strcasecmp(hostname, "dk" QNICHOST_TAIL) == 0)) 483 fprintf(fp, "--show-handles %s\r\n", query); 484 else if ((flags & WHOIS_SPAM_ME) || 485 strchr(query, ' ') != NULL) 486 fprintf(fp, "%s\r\n", query); 487 else if (strcasecmp(hostname, ANICHOST) == 0) { 488 if (strncasecmp(query, "AS", 2) == 0 && 489 strspn(query+2, "0123456789") == strlen(query+2)) 490 fprintf(fp, "+ a %s\r\n", query+2); 491 else 492 fprintf(fp, "+ %s\r\n", query); 493 } else if (strcasecmp(hostres->ai_canonname, VNICHOST) == 0) 494 fprintf(fp, "domain %s\r\n", query); 495 else 496 fprintf(fp, "%s\r\n", query); 497 fflush(fp); 498 499 comment = 0; 500 if (!(flags & WHOIS_SPAM_ME) && 501 (strcasecmp(hostname, ANICHOST) == 0 || 502 strcasecmp(hostname, RNICHOST) == 0)) { 503 comment = 2; 504 } 505 506 nhost = NULL; 507 while ((buf = fgetln(fp, &len)) != NULL) { 508 /* Nominet */ 509 if (!(flags & WHOIS_SPAM_ME) && 510 len == 5 && strncmp(buf, "-- \r\n", 5) == 0) 511 break; 512 /* RIRs */ 513 if (comment == 1 && buf[0] == '#') 514 break; 515 else if (comment == 2) { 516 if (strchr("#%\r\n", buf[0]) != NULL) 517 continue; 518 else 519 comment = 1; 520 } 521 522 printf("%.*s", (int)len, buf); 523 524 if ((flags & WHOIS_RECURSE) && nhost == NULL) { 525 for (i = 0; whois_referral[i].prefix != NULL; i++) { 526 p = buf; 527 SCAN(p, buf+len, *p == ' '); 528 if (strncasecmp(p, whois_referral[i].prefix, 529 whois_referral[i].len) != 0) 530 continue; 531 p += whois_referral[i].len; 532 SCAN(p, buf+len, *p == ' '); 533 host = p; 534 SCAN(p, buf+len, ishost(*p)); 535 if (p > host) 536 s_asprintf(&nhost, "%.*s", 537 (int)(p - host), host); 538 break; 539 } 540 for (i = 0; actually_arin[i] != NULL; i++) { 541 if (strncmp(buf, actually_arin[i], len) == 0) { 542 s_asprintf(&nhost, "%s", ANICHOST); 543 break; 544 } 545 } 546 } 547 /* Verisign etc. */ 548 if (!(flags & WHOIS_SPAM_ME) && 549 len >= sizeof(CHOPSPAM)-1 && 550 (strncasecmp(buf, CHOPSPAM, sizeof(CHOPSPAM)-1) == 0 || 551 strncasecmp(buf, CHOPSPAM+4, sizeof(CHOPSPAM)-5) == 0)) { 552 printf("\n"); 553 break; 554 } 555 } 556 fclose(fp); 557 freeaddrinfo(hostres); 558 559 f = 0; 560 for (i = 0; try_rir[i].host != NULL; i++) { 561 /* Remember visits to RIRs */ 562 if (try_rir[i].loop == 0 && 563 strcasecmp(try_rir[i].host, hostname) == 0) 564 try_rir[i].loop = 1; 565 /* Do we need to find an alternative RIR? */ 566 if (try_rir[i].loop != 0 && nhost != NULL && 567 strcasecmp(try_rir[i].host, nhost) == 0) { 568 free(nhost); 569 nhost = NULL; 570 f = 1; 571 } 572 } 573 if (f) { 574 /* Find a replacement RIR */ 575 for (i = 0; try_rir[i].host != NULL; i++) { 576 if (try_rir[i].loop == 0) { 577 s_asprintf(&nhost, "%s", 578 try_rir[i].host); 579 break; 580 } 581 } 582 } 583 if (nhost != NULL) { 584 /* Ignore self-referrals */ 585 if (strcasecmp(hostname, nhost) != 0) { 586 printf("# %s\n\n", nhost); 587 whois(query, nhost, flags); 588 } 589 free(nhost); 590 } 591 } 592 593 static void 594 usage(void) 595 { 596 fprintf(stderr, 597 "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] " 598 "[-p port] name ...\n"); 599 exit(EX_USAGE); 600 } 601