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