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