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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char copyright[] = 36"@(#) Copyright (c) 1980, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#if 0 41#ifndef lint 42static char sccsid[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93"; 43#endif /* not lint */ 44#endif 45 46#include <sys/cdefs.h> 47#ifndef __APPLE__ 48__FBSDID("$FreeBSD: src/usr.bin/whois/whois.c,v 1.41 2004/08/25 15:34:44 mbr Exp $"); 49#endif 50 51#include <sys/types.h> 52#include <sys/socket.h> 53#include <netinet/in.h> 54#include <arpa/inet.h> 55#include <ctype.h> 56#include <err.h> 57#include <netdb.h> 58#include <stdarg.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <sysexits.h> 63#include <unistd.h> 64 65#define ABUSEHOST "whois.abuse.net" 66#define NICHOST "whois.crsnic.net" 67#define INICHOST "whois.networksolutions.com" 68#define DNICHOST "whois.nic.mil" 69#define GNICHOST "whois.nic.gov" 70#define ANICHOST "whois.arin.net" 71#define LNICHOST "whois.lacnic.net" 72#define RNICHOST "whois.ripe.net" 73#define PNICHOST "whois.apnic.net" 74#define MNICHOST "whois.ra.net" 75#define QNICHOST_TAIL ".whois-servers.net" 76#define SNICHOST "whois.6bone.net" 77#define BNICHOST "whois.registro.br" 78#define NORIDHOST "whois.norid.no" 79#define IANAHOST "whois.iana.org" 80#define GERMNICHOST "de.whois-servers.net" 81#define DEFAULT_PORT "nicname" 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 90const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST, NULL }; 91const char *port = DEFAULT_PORT; 92 93static char *choose_server(char *); 94static struct addrinfo *gethostinfo(char const *host, int exit_on_error); 95#ifdef __APPLE__ 96static void s_asprintf(char **ret, const char *format, ...) __attribute__((__format__(printf, 2, 3))); 97#else 98static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 99#endif 100static void usage(void); 101static void whois(const char *, const char *, int); 102 103int 104main(int argc, char *argv[]) 105{ 106 const char *country, *host; 107 char *qnichost; 108 int ch, flags, use_qnichost; 109 110#ifdef SOCKS 111 SOCKSinit(argv[0]); 112#endif 113 114 country = host = qnichost = NULL; 115 flags = use_qnichost = 0; 116 while ((ch = getopt(argc, argv, "aAbc:dgh:iIlmp:QrR6")) != -1) { 117 switch (ch) { 118 case 'a': 119 host = ANICHOST; 120 break; 121 case 'A': 122 host = PNICHOST; 123 break; 124 case 'b': 125 host = ABUSEHOST; 126 break; 127 case 'c': 128 country = optarg; 129 break; 130 case 'd': 131 host = DNICHOST; 132 break; 133 case 'g': 134 host = GNICHOST; 135 break; 136 case 'h': 137 host = optarg; 138 break; 139 case 'i': 140 host = INICHOST; 141 break; 142 case 'I': 143 host = IANAHOST; 144 break; 145 case 'l': 146 host = LNICHOST; 147 break; 148 case 'm': 149 host = MNICHOST; 150 break; 151 case 'p': 152 port = optarg; 153 break; 154 case 'Q': 155 flags |= WHOIS_QUICK; 156 break; 157 case 'r': 158 host = RNICHOST; 159 break; 160 case 'R': 161 warnx("-R is deprecated; use '-c ru' instead"); 162 country = "ru"; 163 break; 164 case '6': 165 host = SNICHOST; 166 break; 167 case '?': 168 default: 169 usage(); 170 /* NOTREACHED */ 171 } 172 } 173 argc -= optind; 174 argv += optind; 175 176 if (!argc || (country != NULL && host != NULL)) 177 usage(); 178 179 /* 180 * If no host or country is specified determine the top level domain 181 * from the query. If the TLD is a number, query ARIN. Otherwise, use 182 * TLD.whois-server.net. If the domain does not contain '.', fall 183 * back to NICHOST. 184 */ 185 if (host == NULL && country == NULL) { 186 use_qnichost = 1; 187 host = NICHOST; 188 if (!(flags & WHOIS_QUICK)) 189 flags |= WHOIS_RECURSE; 190 } 191 while (argc-- > 0) { 192 if (country != NULL) { 193 s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 194 whois(*argv, qnichost, flags); 195 } else if (use_qnichost) 196 if ((qnichost = choose_server(*argv)) != NULL) 197 whois(*argv, qnichost, flags); 198 if (qnichost == NULL) 199 whois(*argv, host, flags); 200 free(qnichost); 201 qnichost = NULL; 202 argv++; 203 } 204 exit(0); 205} 206 207/* 208 * This function will remove any trailing periods from domain, after which it 209 * returns a pointer to newly allocated memory containing the whois server to 210 * be queried, or a NULL if the correct server couldn't be determined. The 211 * caller must remember to free(3) the allocated memory. 212 */ 213static char * 214choose_server(char *domain) 215{ 216 char *pos, *retval; 217 218 for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';) 219 *pos = '\0'; 220 if (*domain == '\0') 221 errx(EX_USAGE, "can't search for a null string"); 222 if (strlen(domain) > sizeof("-NORID")-1 && 223 strcasecmp(domain + strlen(domain) - sizeof("-NORID") + 1, 224 "-NORID") == 0) { 225 s_asprintf(&retval, "%s", NORIDHOST); 226 return (retval); 227 } 228 while (pos > domain && *pos != '.') 229 --pos; 230 if (pos <= domain) 231 return (NULL); 232 if (isdigit((unsigned char)*++pos)) 233 s_asprintf(&retval, "%s", ANICHOST); 234 else 235 s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL); 236 return (retval); 237} 238 239static struct addrinfo * 240gethostinfo(char const *host, int exit_on_error) 241{ 242 struct addrinfo hints, *res; 243 int error; 244 245 memset(&hints, 0, sizeof(hints)); 246 hints.ai_flags = 0; 247 hints.ai_family = AF_UNSPEC; 248 hints.ai_socktype = SOCK_STREAM; 249 error = getaddrinfo(host, port, &hints, &res); 250 if (error) { 251 warnx("%s: %s", host, gai_strerror(error)); 252 if (exit_on_error) 253 exit(EX_NOHOST); 254 return (NULL); 255 } 256 return (res); 257} 258 259/* 260 * Wrapper for asprintf(3) that exits on error. 261 */ 262static void 263s_asprintf(char **ret, const char *format, ...) 264{ 265 va_list ap; 266 267 va_start(ap, format); 268 if (vasprintf(ret, format, ap) == -1) { 269 va_end(ap); 270 err(EX_OSERR, "vasprintf()"); 271 } 272 va_end(ap); 273} 274 275static void 276whois(const char *query, const char *hostname, int flags) 277{ 278 FILE *sfi, *sfo; 279 struct addrinfo *hostres, *res; 280 char *buf, *host, *nhost, *p; 281 int i, s; 282 size_t c, len; 283 284 hostres = gethostinfo(hostname, 1); 285 for (res = hostres; res; res = res->ai_next) { 286 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 287 if (s < 0) 288 continue; 289 if (connect(s, res->ai_addr, res->ai_addrlen) == 0) 290 break; 291 close(s); 292 } 293 freeaddrinfo(hostres); 294 if (res == NULL) 295 err(EX_OSERR, "connect()"); 296 297 sfi = fdopen(s, "r"); 298 sfo = fdopen(s, "w"); 299 if (sfi == NULL || sfo == NULL) 300 err(EX_OSERR, "fdopen()"); 301 if (strcmp(hostname, GERMNICHOST) == 0) { 302 fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query); 303 } else { 304 fprintf(sfo, "%s\r\n", query); 305 } 306 fflush(sfo); 307 nhost = NULL; 308 while ((buf = fgetln(sfi, &len)) != NULL) { 309 while (len > 0 && isspace((unsigned char)buf[len - 1])) 310 buf[--len] = '\0'; 311 printf("%.*s\n", (int)len, buf); 312 313 if ((flags & WHOIS_RECURSE) && nhost == NULL) { 314 host = strnstr(buf, WHOIS_SERVER_ID, len); 315 if (host != NULL) { 316 host += sizeof(WHOIS_SERVER_ID) - 1; 317 for (p = host; p < buf + len; p++) { 318 if (!ishost(*p)) { 319 *p = '\0'; 320 break; 321 } 322 } 323 s_asprintf(&nhost, "%.*s", 324 (int)(buf + len - host), host); 325 } else if ((host = 326 strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) { 327 host += sizeof(WHOIS_ORG_SERVER_ID) - 1; 328 for (p = host; p < buf + len; p++) { 329 if (!ishost(*p)) { 330 *p = '\0'; 331 break; 332 } 333 } 334 s_asprintf(&nhost, "%.*s", 335 (int)(buf + len - host), host); 336 } else if (strcmp(hostname, ANICHOST) == 0) { 337 for (c = 0; c <= len; c++) 338 buf[c] = tolower((int)buf[c]); 339 for (i = 0; ip_whois[i] != NULL; i++) { 340 if (strnstr(buf, ip_whois[i], len) != 341 NULL) { 342 s_asprintf(&nhost, "%s", 343 ip_whois[i]); 344 break; 345 } 346 } 347 } 348 } 349 } 350 if (nhost != NULL) { 351 whois(query, nhost, 0); 352 free(nhost); 353 } 354} 355 356static void 357usage(void) 358{ 359 fprintf(stderr, 360 "usage: whois [-aAbdgiIlmQrR6] [-c country-code | -h hostname] " 361 "[-p port] name ...\n"); 362 exit(EX_USAGE); 363} 364