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 BNICHOST "whois.registro.br" 65 #define FNICHOST "whois.afrinic.net" 66 #define GERMNICHOST "de" QNICHOST_TAIL 67 #define GNICHOST "whois.nic.gov" 68 #define IANAHOST "whois.iana.org" 69 #define INICHOST "whois.networksolutions.com" 70 #define KNICHOST "whois.krnic.net" 71 #define LNICHOST "whois.lacnic.net" 72 #define MNICHOST "whois.ra.net" 73 #define NICHOST "whois.crsnic.net" 74 #define PDBHOST "whois.peeringdb.com" 75 #define PNICHOST "whois.apnic.net" 76 #define QNICHOST_HEAD "whois.nic." 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 ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') 88 89 static struct { 90 const char *suffix, *server; 91 } whoiswhere[] = { 92 /* Various handles */ 93 { "-ARIN", ANICHOST }, 94 { "-NICAT", "at" QNICHOST_TAIL }, 95 { "-NORID", "no" QNICHOST_TAIL }, 96 { "-RIPE", RNICHOST }, 97 /* Nominet's whois server doesn't return referrals to JANET */ 98 { ".ac.uk", "ac.uk" QNICHOST_TAIL }, 99 { NULL, NULL } 100 }; 101 102 #define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 } 103 static struct { 104 const char *prefix; 105 size_t len; 106 } whois_referral[] = { 107 WHOIS_REFERRAL("Whois Server: "), 108 WHOIS_REFERRAL("WHOIS Server: "), 109 WHOIS_REFERRAL(" Whois Server: "), 110 WHOIS_REFERRAL("refer: "), 111 WHOIS_REFERRAL("Registrant Street1:Whois Server:"), 112 WHOIS_REFERRAL("ReferralServer: whois://"), 113 { NULL, 0 } 114 }; 115 116 static const char *port = DEFAULT_PORT; 117 118 static char *choose_server(char *); 119 static struct addrinfo *gethostinfo(char const *host, int exitnoname); 120 static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 121 static void usage(void); 122 static void whois(const char *, const char *, int); 123 124 int 125 main(int argc, char *argv[]) 126 { 127 const char *country, *host; 128 char *qnichost; 129 int ch, flags, use_qnichost; 130 131 #ifdef SOCKS 132 SOCKSinit(argv[0]); 133 #endif 134 135 country = host = qnichost = NULL; 136 flags = use_qnichost = 0; 137 while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) { 138 switch (ch) { 139 case 'a': 140 host = ANICHOST; 141 break; 142 case 'A': 143 host = PNICHOST; 144 break; 145 case 'b': 146 host = ABUSEHOST; 147 break; 148 case 'c': 149 country = optarg; 150 break; 151 case 'f': 152 host = FNICHOST; 153 break; 154 case 'g': 155 host = GNICHOST; 156 break; 157 case 'h': 158 host = optarg; 159 break; 160 case 'i': 161 host = INICHOST; 162 break; 163 case 'I': 164 host = IANAHOST; 165 break; 166 case 'k': 167 host = KNICHOST; 168 break; 169 case 'l': 170 host = LNICHOST; 171 break; 172 case 'm': 173 host = MNICHOST; 174 break; 175 case 'p': 176 port = optarg; 177 break; 178 case 'P': 179 host = PDBHOST; 180 break; 181 case 'Q': 182 flags |= WHOIS_QUICK; 183 break; 184 case 'r': 185 host = RNICHOST; 186 break; 187 case 'R': 188 flags |= WHOIS_RECURSE; 189 break; 190 case 'S': 191 flags |= WHOIS_SPAM_ME; 192 break; 193 case '?': 194 default: 195 usage(); 196 /* NOTREACHED */ 197 } 198 } 199 argc -= optind; 200 argv += optind; 201 202 if (!argc || (country != NULL && host != NULL)) 203 usage(); 204 205 /* 206 * If no host or country is specified, try to determine the top 207 * level domain from the query, or fall back to NICHOST. 208 */ 209 if (host == NULL && country == NULL) { 210 if ((host = getenv("WHOIS_SERVER")) == NULL && 211 (host = getenv("RA_SERVER")) == NULL) { 212 use_qnichost = 1; 213 host = NICHOST; 214 if (!(flags & WHOIS_QUICK)) 215 flags |= WHOIS_RECURSE; 216 } 217 } 218 while (argc-- > 0) { 219 if (country != NULL) { 220 s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 221 whois(*argv, qnichost, flags); 222 } else if (use_qnichost) 223 if ((qnichost = choose_server(*argv)) != NULL) 224 whois(*argv, qnichost, flags); 225 if (qnichost == NULL) 226 whois(*argv, host, flags); 227 free(qnichost); 228 qnichost = NULL; 229 argv++; 230 } 231 exit(0); 232 } 233 234 /* 235 * This function will remove any trailing periods from domain, after which it 236 * returns a pointer to newly allocated memory containing the whois server to 237 * be queried, or a NULL if the correct server couldn't be determined. The 238 * caller must remember to free(3) the allocated memory. 239 * 240 * If the domain is an IPv6 address or has a known suffix, that determines 241 * the server, else if the TLD is a number, query ARIN, else try a couple of 242 * formulaic server names. Fail if the domain does not contain '.'. 243 */ 244 static char * 245 choose_server(char *domain) 246 { 247 char *pos, *retval; 248 int i; 249 struct addrinfo *res; 250 251 if (strchr(domain, ':')) { 252 s_asprintf(&retval, "%s", ANICHOST); 253 return (retval); 254 } 255 if (strncasecmp(domain, "AS", 2) == 0) { 256 size_t len = strspn(domain + 2, "0123456789"); 257 if (domain[len + 2] == '\0') { 258 s_asprintf(&retval, "%s", ANICHOST); 259 return (retval); 260 } 261 } 262 for (pos = strchr(domain, '\0'); pos > domain && pos[-1] == '.';) 263 *--pos = '\0'; 264 if (*domain == '\0') 265 errx(EX_USAGE, "can't search for a null string"); 266 for (i = 0; whoiswhere[i].suffix != NULL; i++) { 267 size_t suffix_len = strlen(whoiswhere[i].suffix); 268 if (domain + suffix_len < pos && 269 strcasecmp(pos - suffix_len, whoiswhere[i].suffix) == 0) { 270 s_asprintf(&retval, "%s", whoiswhere[i].server); 271 return (retval); 272 } 273 } 274 while (pos > domain && *pos != '.') 275 --pos; 276 if (pos <= domain) 277 return (NULL); 278 if (isdigit((unsigned char)*++pos)) { 279 s_asprintf(&retval, "%s", ANICHOST); 280 return (retval); 281 } 282 /* Try possible alternative whois server name formulae. */ 283 for (i = 0; ; ++i) { 284 switch (i) { 285 case 0: 286 s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL); 287 break; 288 case 1: 289 s_asprintf(&retval, "%s%s", QNICHOST_HEAD, pos); 290 break; 291 default: 292 return (NULL); 293 } 294 res = gethostinfo(retval, 0); 295 if (res) { 296 freeaddrinfo(res); 297 return (retval); 298 } else { 299 free(retval); 300 continue; 301 } 302 } 303 } 304 305 static struct addrinfo * 306 gethostinfo(char const *host, int exit_on_noname) 307 { 308 struct addrinfo hints, *res; 309 int error; 310 311 memset(&hints, 0, sizeof(hints)); 312 hints.ai_flags = AI_CANONNAME; 313 hints.ai_family = AF_UNSPEC; 314 hints.ai_socktype = SOCK_STREAM; 315 res = NULL; 316 error = getaddrinfo(host, port, &hints, &res); 317 if (error && (exit_on_noname || error != EAI_NONAME)) 318 err(EX_NOHOST, "%s: %s", host, gai_strerror(error)); 319 return (res); 320 } 321 322 /* 323 * Wrapper for asprintf(3) that exits on error. 324 */ 325 static void 326 s_asprintf(char **ret, const char *format, ...) 327 { 328 va_list ap; 329 330 va_start(ap, format); 331 if (vasprintf(ret, format, ap) == -1) { 332 va_end(ap); 333 err(EX_OSERR, "vasprintf()"); 334 } 335 va_end(ap); 336 } 337 338 static void 339 whois(const char *query, const char *hostname, int flags) 340 { 341 FILE *fp; 342 struct addrinfo *hostres, *res; 343 char *buf, *host, *nhost, *p; 344 int s = -1, f, antispam; 345 nfds_t i, j; 346 size_t len, count; 347 struct pollfd *fds; 348 int timeout = 180; 349 350 hostres = gethostinfo(hostname, 1); 351 for (res = hostres, count = 0; res; res = res->ai_next) 352 count++; 353 354 antispam = (flags & WHOIS_SPAM_ME) == 0 && 355 strcmp(hostres->ai_canonname, VNICHOST) == 0; 356 357 fds = calloc(count, sizeof(*fds)); 358 if (fds == NULL) 359 err(EX_OSERR, "calloc()"); 360 361 /* 362 * Traverse the result list elements and make non-block 363 * connection attempts. 364 */ 365 count = i = 0; 366 for (res = hostres; res != NULL; res = res->ai_next) { 367 s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, 368 res->ai_protocol); 369 if (s < 0) 370 continue; 371 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 372 if (errno == EINPROGRESS) { 373 /* Add the socket to poll list */ 374 fds[i].fd = s; 375 fds[i].events = POLLERR | POLLHUP | 376 POLLIN | POLLOUT; 377 count++; 378 i++; 379 } else { 380 close(s); 381 s = -1; 382 383 /* 384 * Poll only if we have something to poll, 385 * otherwise just go ahead and try next 386 * address 387 */ 388 if (count == 0) 389 continue; 390 } 391 } else 392 goto done; 393 394 /* 395 * If we are at the last address, poll until a connection is 396 * established or we failed all connection attempts. 397 */ 398 if (res->ai_next == NULL) 399 timeout = INFTIM; 400 401 /* 402 * Poll the watched descriptors for successful connections: 403 * if we still have more untried resolved addresses, poll only 404 * once; otherwise, poll until all descriptors have errors, 405 * which will be considered as ETIMEDOUT later. 406 */ 407 do { 408 int n; 409 410 n = poll(fds, i, timeout); 411 if (n == 0) { 412 /* 413 * No event reported in time. Try with a 414 * smaller timeout (but cap at 2-3ms) 415 * after a new host have been added. 416 */ 417 if (timeout >= 3) 418 timeout <<= 1; 419 420 break; 421 } else if (n < 0) { 422 /* 423 * errno here can only be EINTR which we would want 424 * to clean up and bail out. 425 */ 426 s = -1; 427 goto done; 428 } 429 430 /* 431 * Check for the event(s) we have seen. 432 */ 433 for (j = 0; j < i; j++) { 434 if (fds[j].fd == -1 || fds[j].events == 0 || 435 fds[j].revents == 0) 436 continue; 437 if (fds[j].revents & ~(POLLIN | POLLOUT)) { 438 close(s); 439 fds[j].fd = -1; 440 fds[j].events = 0; 441 count--; 442 continue; 443 } else if (fds[j].revents & (POLLIN | POLLOUT)) { 444 /* Connect succeeded. */ 445 s = fds[j].fd; 446 447 goto done; 448 } 449 450 } 451 } while (timeout == INFTIM && count != 0); 452 } 453 454 /* All attempts were failed */ 455 s = -1; 456 if (count == 0) 457 errno = ETIMEDOUT; 458 459 done: 460 /* Close all watched fds except the succeeded one */ 461 for (j = 0; j < i; j++) 462 if (fds[j].fd != s && fds[j].fd != -1) 463 close(fds[j].fd); 464 465 if (s != -1) { 466 /* Restore default blocking behavior. */ 467 if ((f = fcntl(s, F_GETFL)) != -1) { 468 f &= ~O_NONBLOCK; 469 if (fcntl(s, F_SETFL, f) == -1) 470 err(EX_OSERR, "fcntl()"); 471 } else 472 err(EX_OSERR, "fcntl()"); 473 } 474 475 free(fds); 476 freeaddrinfo(hostres); 477 if (s == -1) 478 err(EX_OSERR, "connect()"); 479 480 fp = fdopen(s, "r+"); 481 if (fp == NULL) 482 err(EX_OSERR, "fdopen()"); 483 if (strcmp(hostname, GERMNICHOST) == 0) { 484 fprintf(fp, "-T dn,ace -C ISO-8859-1 %s\r\n", query); 485 } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) { 486 fprintf(fp, "--show-handles %s\r\n", query); 487 } else if (antispam) { 488 fprintf(fp, "domain %s\r\n", query); 489 } else { 490 fprintf(fp, "%s\r\n", query); 491 } 492 fflush(fp); 493 nhost = NULL; 494 while ((buf = fgetln(fp, &len)) != NULL) { 495 while (len > 0 && isspace((unsigned char)buf[len - 1])) 496 buf[--len] = '\0'; 497 printf("%.*s\n", (int)len, buf); 498 499 if ((flags & WHOIS_RECURSE) && nhost == NULL) { 500 for (i = 0; whois_referral[i].prefix != NULL; i++) { 501 if (strncmp(buf, 502 whois_referral[i].prefix, 503 whois_referral[i].len) != 0) 504 continue; 505 host = buf + whois_referral[i].len; 506 for (p = host; p < buf + len; p++) 507 if (!ishost(*p)) 508 break; 509 s_asprintf(&nhost, "%.*s", 510 (int)(p - host), host); 511 break; 512 } 513 } 514 } 515 fclose(fp); 516 if (nhost != NULL) { 517 whois(query, nhost, 0); 518 free(nhost); 519 } 520 } 521 522 static void 523 usage(void) 524 { 525 fprintf(stderr, 526 "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] " 527 "[-p port] name ...\n"); 528 exit(EX_USAGE); 529 } 530