1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Copyright 2011 Nexenta Systems, Inc. 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 * 4. 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 #include <sys/types.h> 33 #include <sys/socket.h> 34 35 #include <netinet/in.h> 36 #include <arpa/inet.h> 37 #include <ctype.h> 38 #include <err.h> 39 #include <limits.h> 40 #include <netdb.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <sysexits.h> 46 #include <unistd.h> 47 48 #define ABUSEHOST "whois.abuse.net" 49 #define NICHOST "whois.crsnic.net" 50 #define INICHOST "whois.networksolutions.com" 51 #define GNICHOST "whois.nic.gov" 52 #define ANICHOST "whois.arin.net" 53 #define LNICHOST "whois.lacnic.net" 54 #define KNICHOST "whois.krnic.net" 55 #define RNICHOST "whois.ripe.net" 56 #define PNICHOST "whois.apnic.net" 57 #define MNICHOST "whois.ra.net" 58 #define QNICHOST_TAIL ".whois-servers.net" 59 #define BNICHOST "whois.registro.br" 60 #define NORIDHOST "whois.norid.no" 61 #define IANAHOST "whois.iana.org" 62 #define GERMNICHOST "de.whois-servers.net" 63 #define FNICHOST "whois.afrinic.net" 64 #define DEFAULT_PORT "whois" 65 #define WHOIS_SERVER_ID "Whois Server: " 66 #define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:" 67 68 #define WHOIS_RECURSE 0x01 69 #define WHOIS_QUICK 0x02 70 71 #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') 72 73 const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST, 74 FNICHOST, NULL }; 75 const char *port = DEFAULT_PORT; 76 77 static char *choose_server(char *); 78 static struct addrinfo *gethostinfo(char const *host, int exit_on_error); 79 static void s_asprintf(char **ret, const char *format, ...); 80 static void usage(void); 81 static void whois(const char *, const char *, int); 82 static char *getln(FILE *in, size_t *lenp); 83 84 int 85 main(int argc, char *argv[]) 86 { 87 const char *country, *host; 88 char *qnichost; 89 int ch, flags, use_qnichost; 90 91 country = host = qnichost = NULL; 92 flags = use_qnichost = 0; 93 while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:Qr")) != -1) { 94 switch (ch) { 95 case 'a': 96 host = ANICHOST; 97 break; 98 case 'A': 99 host = PNICHOST; 100 break; 101 case 'b': 102 host = ABUSEHOST; 103 break; 104 case 'c': 105 country = optarg; 106 break; 107 case 'f': 108 host = FNICHOST; 109 break; 110 case 'g': 111 host = GNICHOST; 112 break; 113 case 'h': 114 host = optarg; 115 break; 116 case 'i': 117 host = INICHOST; 118 break; 119 case 'I': 120 host = IANAHOST; 121 break; 122 case 'k': 123 host = KNICHOST; 124 break; 125 case 'l': 126 host = LNICHOST; 127 break; 128 case 'm': 129 host = MNICHOST; 130 break; 131 case 'p': 132 port = optarg; 133 break; 134 case 'Q': 135 flags |= WHOIS_QUICK; 136 break; 137 case 'r': 138 host = RNICHOST; 139 break; 140 case '?': 141 default: 142 usage(); 143 /* NOTREACHED */ 144 } 145 } 146 argc -= optind; 147 argv += optind; 148 149 if (!argc || (country != NULL && host != NULL)) 150 usage(); 151 152 /* 153 * If no host or country is specified determine the top level domain 154 * from the query. If the TLD is a number, query ARIN. Otherwise, use 155 * TLD.whois-server.net. If the domain does not contain '.', fall 156 * back to NICHOST. 157 */ 158 if (host == NULL && country == NULL) { 159 use_qnichost = 1; 160 host = NICHOST; 161 if (!(flags & WHOIS_QUICK)) 162 flags |= WHOIS_RECURSE; 163 } 164 while (argc-- > 0) { 165 if (country != NULL) { 166 s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 167 whois(*argv, qnichost, flags); 168 } else if (use_qnichost) 169 if ((qnichost = choose_server(*argv)) != NULL) 170 whois(*argv, qnichost, flags); 171 if (qnichost == NULL) 172 whois(*argv, host, flags); 173 free(qnichost); 174 qnichost = NULL; 175 argv++; 176 } 177 178 return (0); 179 } 180 181 /* 182 * This function will remove any trailing periods from domain, after which it 183 * returns a pointer to newly allocated memory containing the whois server to 184 * be queried, or a NULL if the correct server couldn't be determined. The 185 * caller must remember to free(3) the allocated memory. 186 */ 187 static char * 188 choose_server(char *domain) 189 { 190 char *pos, *retval; 191 192 if (strchr(domain, ':')) { 193 s_asprintf(&retval, "%s", ANICHOST); 194 return (retval); 195 } 196 for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.'; ) 197 *pos = '\0'; 198 if (*domain == '\0') 199 errx(EX_USAGE, "can't search for a null string"); 200 if (strlen(domain) > sizeof ("-NORID")-1 && 201 strcasecmp(domain + strlen(domain) - sizeof ("-NORID") + 1, 202 "-NORID") == 0) { 203 s_asprintf(&retval, "%s", NORIDHOST); 204 return (retval); 205 } 206 while (pos > domain && *pos != '.') 207 --pos; 208 if (pos <= domain) 209 return (NULL); 210 if (isdigit((unsigned char)*++pos)) 211 s_asprintf(&retval, "%s", ANICHOST); 212 else 213 s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL); 214 return (retval); 215 } 216 217 static struct addrinfo * 218 gethostinfo(char const *host, int exit_on_error) 219 { 220 struct addrinfo hints, *res; 221 int error; 222 223 (void) memset(&hints, 0, sizeof (hints)); 224 hints.ai_flags = 0; 225 hints.ai_family = AF_UNSPEC; 226 hints.ai_socktype = SOCK_STREAM; 227 error = getaddrinfo(host, port, &hints, &res); 228 if (error) { 229 warnx("%s: %s", host, gai_strerror(error)); 230 if (exit_on_error) 231 exit(EX_NOHOST); 232 return (NULL); 233 } 234 return (res); 235 } 236 237 /* 238 * Wrapper for asprintf(3) that exits on error. 239 */ 240 /* PRINTFLIKE2 */ 241 static void 242 s_asprintf(char **ret, const char *format, ...) 243 { 244 va_list ap; 245 246 va_start(ap, format); 247 if (vasprintf(ret, format, ap) == -1) { 248 va_end(ap); 249 err(EX_OSERR, "vasprintf()"); 250 } 251 va_end(ap); 252 } 253 254 static void 255 whois(const char *query, const char *hostname, int flags) 256 { 257 FILE *sfi, *sfo; 258 struct addrinfo *hostres, *res; 259 char *buf, *host, *nhost, *p; 260 int i, s; 261 size_t c, len; 262 263 s = -1; 264 hostres = gethostinfo(hostname, 1); 265 for (res = hostres; res; res = res->ai_next) { 266 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 267 if (s < 0) 268 continue; 269 if (connect(s, res->ai_addr, res->ai_addrlen) == 0) 270 break; 271 (void) close(s); 272 } 273 freeaddrinfo(hostres); 274 if (res == NULL) 275 err(EX_OSERR, "connect()"); 276 277 sfi = fdopen(s, "r"); 278 sfo = fdopen(s, "w"); 279 if (sfi == NULL || sfo == NULL) 280 err(EX_OSERR, "fdopen()"); 281 if (strcmp(hostname, GERMNICHOST) == 0) { 282 (void) fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query); 283 } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) { 284 (void) fprintf(sfo, "--show-handles %s\r\n", query); 285 } else { 286 (void) fprintf(sfo, "%s\r\n", query); 287 } 288 (void) fflush(sfo); 289 nhost = NULL; 290 while ((buf = getln(sfi, &len)) != NULL) { 291 while (len > 0 && isspace((unsigned char)buf[len - 1])) 292 buf[--len] = '\0'; 293 (void) printf("%.*s\n", (int)len, buf); 294 295 if ((flags & WHOIS_RECURSE) && nhost == NULL) { 296 host = strnstr(buf, WHOIS_SERVER_ID, len); 297 if (host != NULL) { 298 host += sizeof (WHOIS_SERVER_ID) - 1; 299 for (p = host; p < buf + len; p++) { 300 if (!ishost(*p)) { 301 *p = '\0'; 302 break; 303 } 304 } 305 s_asprintf(&nhost, "%.*s", 306 (int)(buf + len - host), host); 307 } else if ((host = 308 strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) { 309 host += sizeof (WHOIS_ORG_SERVER_ID) - 1; 310 for (p = host; p < buf + len; p++) { 311 if (!ishost(*p)) { 312 *p = '\0'; 313 break; 314 } 315 } 316 s_asprintf(&nhost, "%.*s", 317 (int)(buf + len - host), host); 318 } else if (strcmp(hostname, ANICHOST) == 0) { 319 for (c = 0; c <= len; c++) 320 buf[c] = tolower((unsigned char)buf[c]); 321 for (i = 0; ip_whois[i] != NULL; i++) { 322 if (strnstr(buf, ip_whois[i], len) != 323 NULL) { 324 s_asprintf(&nhost, "%s", 325 ip_whois[i]); 326 break; 327 } 328 } 329 } 330 } 331 } 332 if (nhost != NULL) { 333 whois(query, nhost, 0); 334 free(nhost); 335 } 336 } 337 338 static void 339 usage(void) 340 { 341 (void) fprintf(stderr, 342 "usage: whois [-aAbfgiIklmQr] [-c country-code | -h hostname] " 343 "[-p port] name ...\n"); 344 exit(EX_USAGE); 345 } 346 347 static char * 348 getln(FILE *in, size_t *lenp) 349 { 350 static char *buffer = NULL; 351 static size_t sz = 0; 352 353 size_t len = 0; 354 355 for (;;) { 356 if (sz <= (len + 1)) { 357 char *nb; 358 if ((nb = realloc(buffer, sz + LINE_MAX)) == NULL) { 359 err(1, "realloc"); 360 } 361 buffer = nb; 362 sz += LINE_MAX; 363 } 364 365 buffer[len] = 0; 366 367 if (fgets(buffer + len, sz - len, in) == NULL) { 368 /* END OF FILE */ 369 *lenp = len; 370 if (len == 0) 371 return (NULL); 372 break; 373 } 374 375 len += strlen(buffer + len); 376 377 if (buffer[len - 1] == '\n') { 378 /* got the new line */ 379 *lenp = len; 380 break; 381 } 382 } 383 384 return (buffer); 385 } 386