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