1 /* $OpenBSD: whois.c,v 1.53 2015/12/09 19:29:49 mmcc Exp $ */ 2 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 #include <sys/types.h> 33 #include <sys/socket.h> 34 35 #include <netinet/in.h> 36 #include <arpa/inet.h> 37 #include <netdb.h> 38 39 #include <ctype.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #define NICHOST "whois.crsnic.net" 48 #define INICHOST "whois.networksolutions.com" 49 #define CNICHOST "whois.corenic.net" 50 #define DNICHOST "whois.nic.mil" 51 #define GNICHOST "whois.nic.gov" 52 #define ANICHOST "whois.arin.net" 53 #define RNICHOST "whois.ripe.net" 54 #define PNICHOST "whois.apnic.net" 55 #define RUNICHOST "whois.ripn.net" 56 #define MNICHOST "whois.ra.net" 57 #define LNICHOST "whois.lacnic.net" 58 #define AFNICHOST "whois.afrinic.net" 59 #define BNICHOST "whois.registro.br" 60 #define PDBHOST "whois.peeringdb.com" 61 #define IANAHOST "whois.iana.org" 62 #define QNICHOST_TAIL ".whois-servers.net" 63 64 #define WHOIS_PORT "whois" 65 #define WHOIS_SERVER_ID "Whois Server:" 66 67 #define WHOIS_RECURSE 0x01 68 #define WHOIS_QUICK 0x02 69 70 const char *port_whois = WHOIS_PORT; 71 const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST, 72 AFNICHOST, NULL }; 73 74 __dead void usage(void); 75 int whois(const char *, const char *, const char *, int); 76 char *choose_server(const char *, const char *); 77 78 int 79 main(int argc, char *argv[]) 80 { 81 int ch, flags, rval; 82 char *host, *name, *country; 83 84 country = host = NULL; 85 flags = rval = 0; 86 while ((ch = getopt(argc, argv, "aAc:dgh:iIlmp:PqQrR")) != -1) 87 switch (ch) { 88 case 'a': 89 host = ANICHOST; 90 break; 91 case 'A': 92 host = PNICHOST; 93 break; 94 case 'c': 95 country = optarg; 96 break; 97 case 'd': 98 host = DNICHOST; 99 break; 100 case 'g': 101 host = GNICHOST; 102 break; 103 case 'h': 104 host = optarg; 105 break; 106 case 'i': 107 host = INICHOST; 108 break; 109 case 'I': 110 host = IANAHOST; 111 break; 112 case 'l': 113 host = LNICHOST; 114 break; 115 case 'm': 116 host = MNICHOST; 117 break; 118 case 'p': 119 port_whois = optarg; 120 break; 121 case 'P': 122 host = PDBHOST; 123 break; 124 case 'q': 125 /* deprecated, now the default */ 126 break; 127 case 'Q': 128 flags |= WHOIS_QUICK; 129 break; 130 case 'r': 131 host = RNICHOST; 132 break; 133 case 'R': 134 host = RUNICHOST; 135 break; 136 default: 137 usage(); 138 } 139 argc -= optind; 140 argv += optind; 141 142 if (!argc || (country != NULL && host != NULL)) 143 usage(); 144 145 if (pledge("stdio dns inet", NULL) == -1) 146 err(1, "pledge"); 147 148 if (host == NULL && country == NULL && !(flags & WHOIS_QUICK)) 149 flags |= WHOIS_RECURSE; 150 for (name = *argv; (name = *argv) != NULL; argv++) 151 rval += whois(name, host ? host : choose_server(name, country), 152 port_whois, flags); 153 exit(rval); 154 } 155 156 int 157 whois(const char *query, const char *server, const char *port, int flags) 158 { 159 FILE *fp; 160 char *buf, *p, *nhost, *nbuf = NULL; 161 size_t len; 162 int i, s, error; 163 const char *reason = NULL, *fmt; 164 struct addrinfo hints, *res, *ai; 165 166 memset(&hints, 0, sizeof(hints)); 167 hints.ai_flags = 0; 168 hints.ai_family = AF_UNSPEC; 169 hints.ai_socktype = SOCK_STREAM; 170 error = getaddrinfo(server, port, &hints, &res); 171 if (error) { 172 if (error == EAI_SERVICE) 173 warnx("%s: bad port", port); 174 else 175 warnx("%s: %s", server, gai_strerror(error)); 176 return (1); 177 } 178 179 for (s = -1, ai = res; ai != NULL; ai = ai->ai_next) { 180 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 181 if (s == -1) { 182 error = errno; 183 reason = "socket"; 184 continue; 185 } 186 if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) { 187 error = errno; 188 reason = "connect"; 189 close(s); 190 s = -1; 191 continue; 192 } 193 break; /*okay*/ 194 } 195 if (s == -1) { 196 if (reason) { 197 errno = error; 198 warn("%s: %s", server, reason); 199 } else 200 warn("unknown error in connection attempt"); 201 freeaddrinfo(res); 202 return (1); 203 } 204 205 if (strcmp(server, "whois.denic.de") == 0 || 206 strcmp(server, "de" QNICHOST_TAIL) == 0) 207 fmt = "-T dn,ace -C ISO-8859-1 %s\r\n"; 208 else if (strcmp(server, "whois.dk-hostmaster.dk") == 0 || 209 strcmp(server, "dk" QNICHOST_TAIL) == 0) 210 fmt = "--show-handles %s\r\n"; 211 else 212 fmt = "%s\r\n"; 213 214 fp = fdopen(s, "r+"); 215 if (fp == NULL) 216 err(1, "fdopen"); 217 fprintf(fp, fmt, query); 218 fflush(fp); 219 nhost = NULL; 220 while ((buf = fgetln(fp, &len)) != NULL) { 221 p = buf + len - 1; 222 if (isspace((unsigned char)*p)) { 223 do 224 *p = '\0'; 225 while (p > buf && isspace((unsigned char)*--p)); 226 } else { 227 if ((nbuf = malloc(len + 1)) == NULL) 228 err(1, "malloc"); 229 memcpy(nbuf, buf, len); 230 nbuf[len] = '\0'; 231 buf = nbuf; 232 } 233 puts(buf); 234 235 if (nhost != NULL || !(flags & WHOIS_RECURSE)) 236 continue; 237 238 if ((p = strstr(buf, WHOIS_SERVER_ID))) { 239 p += sizeof(WHOIS_SERVER_ID) - 1; 240 while (isblank((unsigned char)*p)) 241 p++; 242 if ((len = strcspn(p, " \t\n\r"))) { 243 if ((nhost = malloc(len + 1)) == NULL) 244 err(1, "malloc"); 245 memcpy(nhost, p, len); 246 nhost[len] = '\0'; 247 } 248 } else if (strcmp(server, ANICHOST) == 0) { 249 for (p = buf; *p != '\0'; p++) 250 *p = tolower((unsigned char)*p); 251 for (i = 0; ip_whois[i] != NULL; i++) { 252 if (strstr(buf, ip_whois[i]) != NULL) { 253 nhost = strdup(ip_whois[i]); 254 if (nhost == NULL) 255 err(1, "strdup"); 256 break; 257 } 258 } 259 } 260 } 261 fclose(fp); 262 free(nbuf); 263 264 if (nhost != NULL) { 265 error = whois(query, nhost, port, 0); 266 free(nhost); 267 } 268 freeaddrinfo(res); 269 return (error); 270 } 271 272 /* 273 * If no country is specified determine the top level domain from the query. 274 * If the TLD is a number, query ARIN, otherwise, use TLD.whois-server.net. 275 * If the domain does not contain '.', check to see if it is an NSI handle 276 * (starts with '!') or a CORE handle (COCO-[0-9]+ or COHO-[0-9]+) or an 277 * ASN (starts with AS). Fall back to NICHOST for the non-handle case. 278 */ 279 char * 280 choose_server(const char *name, const char *country) 281 { 282 static char *server; 283 const char *qhead; 284 char *nserver; 285 char *ep; 286 size_t len; 287 struct addrinfo hints, *res; 288 289 memset(&hints, 0, sizeof(hints)); 290 hints.ai_flags = 0; 291 hints.ai_family = AF_UNSPEC; 292 hints.ai_socktype = SOCK_STREAM; 293 294 if (country != NULL) 295 qhead = country; 296 else if ((qhead = strrchr(name, '.')) == NULL) { 297 if (*name == '!') 298 return (INICHOST); 299 else if ((strncasecmp(name, "COCO-", 5) == 0 || 300 strncasecmp(name, "COHO-", 5) == 0) && 301 strtol(name + 5, &ep, 10) > 0 && *ep == '\0') 302 return (CNICHOST); 303 else if ((strncasecmp(name, "AS", 2) == 0) && 304 strtol(name + 2, &ep, 10) > 0 && *ep == '\0') 305 return (MNICHOST); 306 else 307 return (NICHOST); 308 } else if (isdigit((unsigned char)*(++qhead))) 309 return (ANICHOST); 310 len = strlen(qhead) + sizeof(QNICHOST_TAIL); 311 if ((nserver = realloc(server, len)) == NULL) 312 err(1, "realloc"); 313 server = nserver; 314 315 /* 316 * Post-2003 ("new") gTLDs are all supposed to have "whois.nic.domain" 317 * (per registry agreement), some older gTLDs also support this... 318 */ 319 snprintf(server, len, "whois.nic.%s", qhead); 320 321 /* most ccTLDs don't do this, but QNICHOST/whois-servers mostly works */ 322 if ((strlen(qhead) == 2 || 323 /* and is required for most of the <=2003 TLDs/gTLDs */ 324 strcasecmp(qhead, "org") == 0 || 325 strcasecmp(qhead, "com") == 0 || 326 strcasecmp(qhead, "net") == 0 || 327 strcasecmp(qhead, "cat") == 0 || 328 strcasecmp(qhead, "pro") == 0 || 329 strcasecmp(qhead, "info") == 0 || 330 strcasecmp(qhead, "aero") == 0 || 331 strcasecmp(qhead, "jobs") == 0 || 332 strcasecmp(qhead, "mobi") == 0 || 333 strcasecmp(qhead, "museum") == 0 || 334 /* for others, if whois.nic.TLD doesn't exist, try whois-servers */ 335 getaddrinfo(server, NULL, &hints, &res) != 0)) { 336 strlcpy(server, qhead, len); 337 strlcat(server, QNICHOST_TAIL, len); 338 } 339 340 return (server); 341 } 342 343 __dead void 344 usage(void) 345 { 346 extern char *__progname; 347 348 fprintf(stderr, 349 "usage: %s [-AadgIilmPQRr] [-c country-code | -h host] " 350 "[-p port] name ...\n", __progname); 351 exit(1); 352 } 353