]>
git.cameronkatri.com Git - apple_cmds.git/blob - adv_cmds/whois/whois.c
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
33 static const char copyright
[] =
34 "@(#) Copyright (c) 1980, 1993\n\
35 The Regents of the University of California. All rights reserved.\n";
40 static char sccsid
[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93";
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
47 #include <sys/types.h>
48 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
66 #define SOCK_NONBLOCK 0
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"
91 #define DEFAULT_PORT "nicname"
93 #define DEFAULT_PORT "whois"
96 #define WHOIS_RECURSE 0x01
97 #define WHOIS_QUICK 0x02
98 #define WHOIS_SPAM_ME 0x04
100 #define CHOPSPAM ">>> Last update of WHOIS database:"
102 #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-')
104 #define SCAN(p, end, check) \
105 while ((p) < (end)) \
110 const char *suffix
, *server
;
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 */
124 #define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 }
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 */
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.
143 static const char *actually_arin
[] = {
144 "netname: ERX-NETBLOCK\n", /* APNIC */
145 "netname: NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK\n",
165 for (i
= 0; try_rir
[i
].host
!= NULL
; i
++)
169 static const char *port
= DEFAULT_PORT
;
171 static const char *choose_server(char *);
172 static struct addrinfo
*gethostinfo(char const *host
, int exitnoname
);
174 static void s_asprintf(char **ret
, const char *format
, ...) __attribute__((__format__(printf
, 2, 3)));
176 static void s_asprintf(char **ret
, const char *format
, ...) __printflike(2, 3);
178 static void usage(void);
179 static void whois(const char *, const char *, int);
182 main(int argc
, char *argv
[])
184 const char *country
, *host
;
191 country
= host
= NULL
;
193 while ((ch
= getopt(argc
, argv
, "aAbc:fgh:iIklmp:PQrRS")) != -1) {
238 flags
|= WHOIS_QUICK
;
244 flags
|= WHOIS_RECURSE
;
247 flags
|= WHOIS_SPAM_ME
;
258 if (!argc
|| (country
!= NULL
&& host
!= NULL
))
262 * If no host or country is specified, rely on referrals from IANA.
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
;
272 if (country
!= NULL
) {
274 s_asprintf(&qnichost
, "%s%s", country
, QNICHOST_TAIL
);
275 whois(*argv
, qnichost
, flags
);
278 whois(*argv
, host
!= NULL
? host
:
279 choose_server(*argv
), flags
);
287 choose_server(char *domain
)
289 size_t len
= strlen(domain
);
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
);
299 errx(EX_SOFTWARE
, "no default whois server");
302 static struct addrinfo
*
303 gethostinfo(char const *host
, int exit_on_noname
)
305 struct addrinfo hints
, *res
;
308 memset(&hints
, 0, sizeof(hints
));
309 hints
.ai_flags
= AI_CANONNAME
;
310 hints
.ai_family
= AF_UNSPEC
;
311 hints
.ai_socktype
= SOCK_STREAM
;
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
));
320 * Wrapper for asprintf(3) that exits on error.
323 s_asprintf(char **ret
, const char *format
, ...)
327 va_start(ap
, format
);
328 if (vasprintf(ret
, format
, ap
) == -1) {
330 err(EX_OSERR
, "vasprintf()");
336 connect_to_any_host(struct addrinfo
*hostres
)
338 struct addrinfo
*res
;
342 int timeout
= 180, s
= -1;
344 for (res
= hostres
, count
= 0; res
; res
= res
->ai_next
)
346 fds
= calloc(count
, sizeof(*fds
));
348 err(EX_OSERR
, "calloc()");
351 * Traverse the result list elements and make non-block
352 * connection attempts.
355 for (res
= hostres
; res
!= NULL
; res
= res
->ai_next
) {
356 s
= socket(res
->ai_family
, res
->ai_socktype
| SOCK_NONBLOCK
,
361 int flags
= fcntl(s
, F_GETFL
, 0);
367 if (fcntl(s
, F_SETFL
, flags
) != 0) {
372 if (connect(s
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
373 if (errno
== EINPROGRESS
) {
374 /* Add the socket to poll list */
376 fds
[i
].events
= POLLERR
| POLLHUP
|
379 * From here until a socket connects, the
380 * socket fd is owned by the fds[] poll array.
390 * Poll only if we have something to poll,
391 * otherwise just go ahead and try next
401 * If we are at the last address, poll until a connection is
402 * established or we failed all connection attempts.
404 if (res
->ai_next
== NULL
)
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.
416 n
= poll(fds
, i
, timeout
);
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.
429 * errno here can only be EINTR which we would
430 * want to clean up and bail out.
437 * Check for the event(s) we have seen.
439 for (j
= 0; j
< i
; j
++) {
440 if (fds
[j
].fd
== -1 || fds
[j
].events
== 0 ||
443 if (fds
[j
].revents
& ~(POLLIN
| POLLOUT
)) {
449 } else if (fds
[j
].revents
& (POLLIN
| POLLOUT
)) {
450 /* Connect succeeded. */
458 } while (timeout
== INFTIM
&& count
!= 0);
461 /* All attempts were failed */
467 /* Close all watched fds except the succeeded one */
468 for (j
= 0; j
< i
; j
++)
476 whois(const char *query
, const char *hostname
, int flags
)
479 struct addrinfo
*hostres
;
480 char *buf
, *host
, *nhost
, *p
;
484 hostres
= gethostinfo(hostname
, 1);
485 s
= connect_to_any_host(hostres
);
487 err(EX_OSERR
, "connect()");
489 /* Restore default blocking behavior. */
490 if ((f
= fcntl(s
, F_GETFL
)) == -1)
491 err(EX_OSERR
, "fcntl()");
493 if (fcntl(s
, F_SETFL
, f
) == -1)
494 err(EX_OSERR
, "fcntl()");
496 fp
= fdopen(s
, "r+");
498 err(EX_OSERR
, "fdopen()");
500 if (!(flags
& WHOIS_SPAM_ME
) &&
501 (strcasecmp(hostname
, DENICHOST
) == 0 ||
502 strcasecmp(hostname
, "de" QNICHOST_TAIL
) == 0)) {
505 for (q
= query
; *q
!= '\0'; q
++)
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);
521 fprintf(fp
, "+ %s\r\n", query
);
522 } else if (strcasecmp(hostres
->ai_canonname
, VNICHOST
) == 0)
523 fprintf(fp
, "domain %s\r\n", query
);
525 fprintf(fp
, "%s\r\n", query
);
529 if (!(flags
& WHOIS_SPAM_ME
) &&
530 (strcasecmp(hostname
, ANICHOST
) == 0 ||
531 strcasecmp(hostname
, RNICHOST
) == 0)) {
536 while ((buf
= fgetln(fp
, &len
)) != NULL
) {
538 if (!(flags
& WHOIS_SPAM_ME
) &&
539 len
== 5 && strncmp(buf
, "-- \r\n", 5) == 0)
542 if (comment
== 1 && buf
[0] == '#')
544 else if (comment
== 2) {
545 if (strchr("#%\r\n", buf
[0]) != NULL
)
551 printf("%.*s", (int)len
, buf
);
553 if ((flags
& WHOIS_RECURSE
) && nhost
== NULL
) {
554 for (i
= 0; whois_referral
[i
].prefix
!= NULL
; i
++) {
556 SCAN(p
, buf
+len
, *p
== ' ');
557 if (strncasecmp(p
, whois_referral
[i
].prefix
,
558 whois_referral
[i
].len
) != 0)
560 p
+= whois_referral
[i
].len
;
561 SCAN(p
, buf
+len
, *p
== ' ');
563 SCAN(p
, buf
+len
, ishost(*p
));
565 s_asprintf(&nhost
, "%.*s",
566 (int)(p
- host
), host
);
569 for (i
= 0; actually_arin
[i
] != NULL
; i
++) {
570 if (strncmp(buf
, actually_arin
[i
], len
) == 0) {
571 s_asprintf(&nhost
, "%s", ANICHOST
);
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)) {
586 freeaddrinfo(hostres
);
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)
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) {
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",
613 /* Ignore self-referrals */
614 if (strcasecmp(hostname
, nhost
) != 0) {
615 printf("# %s\n\n", nhost
);
616 whois(query
, nhost
, flags
);
626 "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] "
627 "[-p port] name ...\n");