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