]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/whois/whois.c
Merge ACPICA 20150515.
[FreeBSD/FreeBSD.git] / usr.bin / whois / whois.c
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 US-ASCII %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 }