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