libraries · C · implementation
whois.c, in full.
The implementation behind whois.h: server sessions, the Prefix WhoIs protocol, bulk queries, and the RIS, Cymru and RADB fallbacks. Read it here or take it from the LFT tarball, where it is maintained.
whois.craw
/* * Handle communication with whois servers. ** This file is part of the Prefix WhoIs Project.* See http://pwhois.org** The full text of our legal notices is contained in the file called* COPYING, included with this Distribution.* * This software includes:* - simplified access to regular expressions* - tokenizer* - lookup functions for AS, NETNAME, ORGNAME * - works with the following sources:* ARIN, RIPE, APNIC, RADB, CYMRU, PWHOIS, RISWHOIS* - will do recursive lookups* - convenient framework for further whois digging** To compile the standalone client:* cc -o whob whois.c -DSTANDALONE** * Portions (c) Victor Oppleman (lft@oppleman.com)* Portions (c) 2011 Markus Gothe <nietzsche@lysator.liu.se>* Portions (c) 2002 Ugen Antsilevitch (ugen@xonix.com)**/#include "lft_lib.h"#define PORT_WHOIS 43#if defined(WIN32) || defined(_WIN32)#define read(a, b, c) recv(a, b, c, 0)#define write(a, b, c) send(a, b, c, 0)#define close(s) closesocket(s)#define snprintf _snprintf#endif#include "whois.h"#ifndef WIN32#include <signal.h>#include <getopt.h>#include <strings.h>#include <termios.h>#include <poll.h>#endif#if defined(BSD_IP_STACK)#define pcap_snprintf snprintf#endif/*#define ASSERT(x) if (!(x)) { fprintf(stderr, "Assertion ("#x") failed\n"); exit(EXIT_FAILURE); }*//* OPTIONS and variable initialization */static char pwhois_server[] = "whois.pwhois.org";static char myaddress_server[] = "myaddress.today";static char radb_server[] = "whois.ra.net";static char cymru_server[] = "whois.cymru.com";static char arin_server[] = "whois.arin.net";static char apnic_server[] = "whois.apnic.net";static char ripe_server[] = "whois.ripe.net";static char ripe_ris_server[] = "riswhois.ripe.net";#ifdef STANDALONEconst char *version = "4.0"; /* set version string for library and client */static const char *version_date = "(09/2026)"; /* date of this version */const char *appname = "WhoB"; /* set application name */static int go_interactive = 0; /* We'll wait on STDIN unless args suggest otherwise */static int use_cymru = 0; /* Don't use Cymru by default */static int use_riswhois = 0; /* Don't use RIPE NCC RIS by default */static int display_orgname = 1; /* Display orgname by default */static int display_aspath = 0; /* Don't display AS-PATH by default */static int display_netname = 0; /* Don't display netname by default */static int display_radb_as = 0; /* Don't display RADB Origin-AS by default */static int show_routes_byasn = 0; /* Don't show all advertisements by default */static int show_routes_bytransitasn = 0; /* Don't show all routes that transit ASN by default */static int show_networks_byasn = 0; /* Don't show all networks by default */static int show_contacts_byasn = 0; /* Don't show all contact info by default */static int show_routes_byprefix = 0; /* Don't show all routes by prefix by default */static int show_server_status = 0; /* Don't show pwhois server status by default */static int show_cache_date = 0; /* Don't show pwhois cache date by default */static int read_from_file = 0; /* Don't read input from file by default */static int riswhoisfromfile = 0; /* Don't use riswhois by default for readfromfile */static int cymrufromfile = 0; /* Don't use Cymru by default for readfromfile */static int use_gigo = 1; /* Use GIGO feature by default */static int use_stdin = 0; /* Don't use STDIN for bulk file input by default */static const unsigned int max_hostname_input = 200; /* Maximum length of input from user */static const int max_lines = 2500; /* Maximum lines to read from bulk file per query */static const int line_size = 256; /* Maximum line length *//* --- pWhoIs output colorization (STANDALONE / whob only) ------------------ * Colour is applied to stdout only, and only when stdout is an interactive * terminal (never a file or pipe). --no-color forces it off, --color forces * it on. The palette is "Beacon+": the classic ANSI Beacon scheme with the * field-label colour from Cartographer and the geo colour from Teal Ledger. * A dark and a light variant are chosen automatically from the terminal's * background (OSC 11 query, then COLORFGBG, then a dark default). */static int w_color_mode = 0; /* 0 = auto, 1 = --color (force on), 2 = --no-color */static int w_use_color = 0; /* resolved once at startup by w_detect_color() */static int w_truecolor = 0; /* 1 = emit 24-bit SGR, 0 = 256-colour fallback */static int w_bg_light = 0; /* 0 = dark background variant, 1 = light */static char w_highlight_asn[24] = ""; /* queried ASN to bold within AS-Path (-a/-A) *//* field groups, used both to pick a colour and to index the palette */enum { WF_LABEL = 0, WF_ASN, WF_PREFIX, WF_ORG, WF_GEO, WF_DATE, WF_NGROUPS, WF_NONE = -1 };/* one palette entry: exact RGB (truecolor) plus nearest xterm-256 index */typedef struct { unsigned char r, g, b; unsigned char x256; } wc_color;/* Cartographer palette. [0] = dark-background variant, [1] = light. */static const wc_color w_palette[2][WF_NGROUPS] = { /* DARK */ { { 0x7a, 0xa2, 0xc8, 110 }, /* label — steel blue */ { 0xe0, 0xa4, 0x58, 179 }, /* asn — amber */ { 0x6c, 0xc4, 0xa1, 79 }, /* prefix — emerald */ { 0xe8, 0xe6, 0xdf, 254 }, /* org — warm off-white*/ { 0xe0, 0x86, 0x5a, 173 }, /* geo — terracotta */ { 0x7c, 0x84, 0x71, 101 }, /* date — slate */ }, /* LIGHT */ { { 0x35, 0x68, 0x8f, 24 }, /* label — steel blue */ { 0xa8, 0x66, 0x12, 130 }, /* asn — dark amber */ { 0x1f, 0x7a, 0x5a, 29 }, /* prefix — deep emerald */ { 0x17, 0x17, 0x16, 233 }, /* org — near black */ { 0xbf, 0x4a, 0x2a, 166 }, /* geo — burnt sienna */ { 0x8a, 0x8a, 0x80, 245 }, /* date — warm grey */ },};/* Table-only date shades so Create / Modify / Originated columns read apart. * [bg][0=Create,1=Modify,2=Originated]. Modify mirrors the base date colour. */static const wc_color w_date_shade[2][3] = { /* DARK */ { { 0x6c, 0x8a, 0x86, 66 }, /* Create — teal-slate */ { 0x7c, 0x84, 0x71, 101 }, /* Modify — slate */ { 0xa0, 0x8a, 0x5f, 137 } }, /* Originated — tan-slate */ /* LIGHT */ { { 0x3f, 0x74, 0x6e, 23 }, /* Create — teal */ { 0x6a, 0x6a, 0x60, 241 }, /* Modify — grey */ { 0x8a, 0x6a, 0x3a, 94 } }, /* Originated — tan */};#define WC_RESET "\033[0m"#define WC_BOLD "\033[1m"#define WC_DIM "\033[2m"#define OPT_W_COLOR 256 /* --color (long option, force colour on) */#define OPT_W_NO_COLOR 257 /* --no-color (long option, force colour off) */#define OPT_W_ANNOTATE 258 /* --annotate[=FILE]: enrich IPs in text via pWhoIs */#define OPT_W_FIELDS 259 /* -F/--fields LIST: which pWhoIs fields the annotation shows */static void w_print_pwhois_colorized(const char *reply); /* defined below */static void w_print_status_colorized(const char *reply); /* defined below */static char hostname[256];#endif/* END of OPTIONS and variable initialization */#if defined(WIN32) || defined(_WIN32)char *index(char *s, char c){ char *t; if (!s) return NULL; for (t = s; *t; t++) if (*t == c) { return t; } /* Return terminating \0 if specifically requested */ if (c == '\0') return t; return NULL;}#endif#if defined(WIN32) || defined(_WIN32)intinet_aton(const char *cp, struct in_addr *pin){ if (!pin) return -1; pin->s_addr = inet_addr(cp); return (pin->s_addr != -1) ? 1 : 0;}#endiftypedef struct token_s { char *ptr;} token_t;static token_t *tokens(char *buf, const char *sep){ char *c, *c1; int size, cur; token_t *rt; if (!buf || !sep) return NULL; size = 1; for (c = buf; *c ; c++) if (index(sep, *c)) { size++; while (*c && index(sep, *c)) c++; } size++; /* for the NULL */ if (!(rt = (token_t *)malloc(size * sizeof(token_t)))) return NULL; memset(rt, 0, size * sizeof(token_t)); rt[0].ptr = buf; cur = 0; for (c = buf; *c ; c++) { if (index(sep, *c)) { c1 = c; while (*c && index(sep, *c)) c++; if (*c) rt[++cur].ptr = c; *c1 = '\0'; } } rt[++cur].ptr = NULL; return rt;}typedef struct ip_blk_s { unsigned int start; unsigned int end;} ip_blk_t;static ip_blk_t *w_blk2range(char *s_start, char *s_end){ struct in_addr in; unsigned int s, e; ip_blk_t *r; if (!s_start || !s_end) return NULL; if (!inet_aton(s_start, &in)) return NULL; s = ntohl(in.s_addr); if (!inet_aton(s_end, &in)) return NULL; e = ntohl(in.s_addr); if (!(r = malloc(sizeof(ip_blk_t)))) return NULL; r->start = s; r->end = e; return r;}static ip_blk_t *w_mask2range(char *addr, char *mask){ struct in_addr in; unsigned int s, m; ip_blk_t *r; if (!addr || !mask) return NULL; m = (unsigned int)strtoul(mask, (char **)NULL, 10); if (m > 32) return NULL; if (!inet_aton(addr, &in)) return NULL; s = ntohl(in.s_addr); if (!(r = malloc(sizeof(ip_blk_t)))) return NULL; r->start = s &~ (((unsigned)0xffffffff) >> m); r->end = s | (((unsigned)0xffffffff) >> m); return r;}static int rm_spaces(char* str){ /* Remove spaces (isspace()) from anywhere within a string ONLY operates on a null-terminated (\0) string! */ int j = -1; unsigned int i; if (!str) return 0; for (i=0; i<=strlen(str); i++) if (!(isspace((unsigned char)*(str+i)))) *(str+(++j)) = *(str+i); else continue; return 1; }static char *match_prefix(const char *prefix, const char *target){ /* Target will be something like "origin: AS22773" and prefix will be "origin:" and * we return a pointer to "AS22773" */ while (*prefix) { if (tolower((unsigned char)*prefix) != tolower((unsigned char)*target)) return NULL; prefix++; target++; } while (isspace((unsigned char)*target)) target++; /* strip out the leading AS from the number */ if (strncmp(target,"AS",2) == 0) target += 2; return strdup(target);}static ip_blk_t *match_iprange(char *target){ /* matches something like "1.2.3.4-5.6.7.8" */ char *pos, *dash, *beforedash; /* ip_blk_t *out; */ while (isspace((unsigned char)*target)) target++; pos = target; while (*pos && !isspace((unsigned char)*pos)) pos++; beforedash = strdup(target); beforedash[pos-target] = 0; dash = strchr(target, '-'); if (!dash) return NULL; dash++; while (isspace((unsigned char)*dash)) dash++; return w_blk2range(beforedash, dash);}static ip_blk_t *match_ipprefix(char *target){ /* matches something like 1.2.3.0/24 */ char *slash, *pos; char *beforeslash; /* ip_blk_t *out; */ while (isspace((unsigned char)*target)) target++; pos = target; while (*pos && !isspace((unsigned char)*pos) && *pos != '/') pos++; beforeslash = strdup(target); beforeslash[pos - target] = 0; slash = strchr(target, '/'); if (!slash) return NULL; slash++; return w_mask2range(beforeslash, slash);}static char *match_inparens(char *target){ /* matches something like " (HELLO)" and returns "HELLO" */ char *end, *res; target = strchr(target, '('); if (!target) return NULL; target++; end = strchr(target, ')'); if (!end) return NULL; res = strdup(target); res[end - target] = 0; return res;}static char *match_afterparens(char *target){ /* matches something like " (HELLO) xxx" and returns a pointer to "xxx" */ target = strchr(target, '('); if (!target) return NULL; target = strchr(target, ')'); if (!target) return NULL; target++; while(*target && isspace((unsigned char)*target)) target++; if (*target) return strdup(target); else return NULL;}static int stricontains(const char *haystack, const char *needle) { /* Search for a substring without a string library */ int i, j, match; i = 0, j = 0; while (haystack[i] != '\0') { while (tolower((unsigned char)haystack[i]) != tolower((unsigned char)needle[0]) && haystack[i] != '\0') i++; if (haystack[i] == '\0') return (-1); match = i; while (tolower((unsigned char)haystack[i]) == tolower((unsigned char)needle[j]) && haystack[i] != '\0' && needle[j] != '\0') { i++; j++; } if (needle[j] == '\0') return (match); if (haystack[i] == '\0') return (-1); i = match + 1; j = 0; } return 0;}#ifdef HAVE_CARES#ifdef HAVE_ARES_PROCESS_FDS/* fd-state tracking for w_prefetch()'s private ares_channel (event-model API) */#define W_ARES_MAX_FDS 4struct w_ares_state { struct { ares_socket_t fd; unsigned int events; /* mask of ares_fd_eventflag_t */ } fd_table[W_ARES_MAX_FDS]; int nfds;};static voidw_ares_sock_state_cb(void *data, ares_socket_t fd, int readable, int writable){ struct w_ares_state *st = (struct w_ares_state *)data; int i; for (i = 0; i < st->nfds; i++) { if (st->fd_table[i].fd == fd) { if (!readable && !writable) { st->fd_table[i] = st->fd_table[--st->nfds]; } else { st->fd_table[i].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); } return; } } if (!readable && !writable) return; if (st->nfds >= W_ARES_MAX_FDS) return; st->fd_table[st->nfds].fd = fd; st->fd_table[st->nfds].events = (readable ? (unsigned)ARES_FD_EVENT_READ : 0u) | (writable ? (unsigned)ARES_FD_EVENT_WRITE : 0u); st->nfds++;}#endif /* HAVE_ARES_PROCESS_FDS *//* Callback for w_prefetch() forward (A-record) lookups. */static voidw_ares_callback(void *arg, int status, int timeouts, struct ares_addrinfo *ai){ struct w_dns_entry *entry = (struct w_dns_entry *)arg; (void)timeouts; if (status == ARES_SUCCESS && ai && ai->nodes) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->nodes->ai_addr; memcpy(&entry->addr, &sin->sin_addr, sizeof(struct in_addr)); } if (ai) ares_freeaddrinfo(ai); entry->resolved = 1; /* mark done whether succeeded or failed */}/* Resolve a single whois server hostname on first use. * Finds the matching cache slot, fires one c-ares A-record lookup via * ares_getaddrinfo(), and drains via ares_process_fds() until resolved or * the 1-second deadline expires. No-op if already resolved or the server * is not in the known-server list. */static voidw_prefetch(whois_session_params *wsess, const char *serv){ int i; struct w_dns_entry *entry = NULL; ares_channel wchan; struct ares_options opts;#ifdef HAVE_ARES_PROCESS_FDS struct w_ares_state st;#endif struct ares_addrinfo_hints hints; if (!wsess || !serv) return; /* find the cache slot for this server */ for (i = 0; i < wsess->w_dns_cache_count; i++) { if (wsess->w_dns_cache[i].host && strcmp(wsess->w_dns_cache[i].host, serv) == 0) { entry = &wsess->w_dns_cache[i]; break; } } if (!entry) return; /* not a known whois server; getaddrinfo handles it */ if (entry->resolved) return; /* already resolved on a previous call */ /* fire a single lookup and drain with a 1-second hard deadline */ memset(&opts, 0, sizeof(opts)); opts.timeout = 500; opts.tries = 2;#ifdef HAVE_ARES_PROCESS_FDS memset(&st, 0, sizeof(st)); opts.sock_state_cb = w_ares_sock_state_cb; opts.sock_state_cb_data = &st; if (ares_init_options(&wchan, &opts, ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB) != ARES_SUCCESS) return;#else if (ares_init_options(&wchan, &opts, ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES) != ARES_SUCCESS) return;#endif memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; ares_getaddrinfo(wchan, serv, NULL, &hints, w_ares_callback, entry); { struct timeval deadline, now; gettimeofday(&deadline, NULL); deadline.tv_sec += 1; for (;;) { struct timeval tv, remaining; int nfds = 0; fd_set rfd, wfd;#ifdef HAVE_ARES_PROCESS_FDS ares_fd_events_t events[W_ARES_MAX_FDS]; size_t nevents = 0; int j;#endif if (entry->resolved) break; gettimeofday(&now, NULL); remaining.tv_sec = deadline.tv_sec - now.tv_sec; remaining.tv_usec = deadline.tv_usec - now.tv_usec; if (remaining.tv_usec < 0) { remaining.tv_sec--; remaining.tv_usec += 1000000; } if (remaining.tv_sec < 0) break; tv.tv_sec = 0; tv.tv_usec = (remaining.tv_sec == 0 && remaining.tv_usec < 10000) ? remaining.tv_usec : 10000; FD_ZERO(&rfd); FD_ZERO(&wfd);#ifdef HAVE_ARES_PROCESS_FDS if (st.nfds == 0) { /* No fds yet — drive timeout processing only, then stop */ ares_process_fds(wchan, NULL, 0, 0); break; } for (j = 0; j < st.nfds; j++) { if (st.fd_table[j].events & ARES_FD_EVENT_READ) FD_SET(st.fd_table[j].fd, &rfd); if (st.fd_table[j].events & ARES_FD_EVENT_WRITE) FD_SET(st.fd_table[j].fd, &wfd); if ((int)st.fd_table[j].fd + 1 > nfds) nfds = (int)st.fd_table[j].fd + 1; } if (nfds > 0) select(nfds, &rfd, &wfd, NULL, &tv); for (j = 0; j < st.nfds; j++) { unsigned int ev = ARES_FD_EVENT_NONE; if (FD_ISSET(st.fd_table[j].fd, &rfd)) ev |= ARES_FD_EVENT_READ; if (FD_ISSET(st.fd_table[j].fd, &wfd)) ev |= ARES_FD_EVENT_WRITE; if (ev != ARES_FD_EVENT_NONE) { events[nevents].fd = st.fd_table[j].fd; events[nevents].events = ev; nevents++; } } ares_process_fds(wchan, events, nevents, 0);#else /* classic ares_fds()/ares_process() poll API (c-ares < 1.34) */ /* Intentional for older c-ares; a newer shadowing header marks * these deprecated — expected in this branch, so silence it. */#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wdeprecated-declarations" nfds = ares_fds(wchan, &rfd, &wfd); if (nfds == 0) { ares_process(wchan, &rfd, &wfd); break; } select(nfds, &rfd, &wfd, NULL, &tv); ares_process(wchan, &rfd, &wfd);#pragma GCC diagnostic pop#endif } } ares_destroy(wchan);}#endif /* HAVE_CARES *//*---------------------------------------------------------------------------*//* Timed getaddrinfo() wrapper for whois server hostname resolution. * On Windows: spawns a thread and waits up to 1 s; kills if it doesn't finish. * On POSIX: arms SIGALRM for 1 s, restores on return. * Keeps the call-sites in w_ask() clean and platform-independent. */#if defined(WIN32) || defined(_WIN32)struct w_gai_args { const char *serv; struct addrinfo hints; /* copy — thread must not touch caller's stack */ struct addrinfo **res; int rc;};static DWORD WINAPI w_gai_thread(LPVOID arg){ struct w_gai_args *a = (struct w_gai_args *)arg; a->rc = getaddrinfo(a->serv, NULL, &a->hints, a->res); return 0;}static intw_getaddrinfo_timed(const char *serv, const struct addrinfo *hints, struct addrinfo **res){ struct w_gai_args a; HANDLE th; DWORD tid; a.serv = serv; a.hints = *hints; /* copy the hints struct into our local */ a.res = res; a.rc = EAI_AGAIN; th = CreateThread(NULL, 0, w_gai_thread, &a, 0, &tid); if (!th) return EAI_AGAIN; if (WaitForSingleObject(th, 1000) == WAIT_TIMEOUT) TerminateThread(th, 0); CloseHandle(th); return a.rc;}#else /* POSIX */static volatile sig_atomic_t w_dns_timed_out;static void w_sigalrm(int sig) { (void)sig; w_dns_timed_out = 1; }static intw_getaddrinfo_timed(const char *serv, const struct addrinfo *hints, struct addrinfo **res){ int rc; w_dns_timed_out = 0; signal(SIGALRM, w_sigalrm); alarm(1); rc = getaddrinfo(serv, NULL, hints, res); alarm(0); signal(SIGALRM, SIG_DFL); if (w_dns_timed_out && rc != 0) rc = EAI_AGAIN; return rc;}#endif /* WIN32 *//*---------------------------------------------------------------------------*/whois_session_params *w_init(void){ /* int e; */ whois_session_params *wsess = (whois_session_params *)malloc(sizeof(whois_session_params)); wsess->w_noisy = 0; /* Don't show debug msgs by default */ memset(&(wsess->pw_serv), 0, sizeof(wsess->pw_serv)); wsess->consolidated_asn[0] = wsess->consolidated_asp[0] = wsess->consolidated_route[0] = wsess->consolidated_orgname[0] = wsess->consolidated_netname[0] ='?'; wsess->consolidated_asn[1] = wsess->consolidated_asp[1] = wsess->consolidated_route[1] = wsess->consolidated_orgname[1] = wsess->consolidated_netname[1] =0; memset(&wsess->tbuf, 0, sizeof(wsess->tbuf)); wsess->logprintfCookie = 0;#ifdef HAVE_CARES { /* Register all known whois server hostnames in the DNS cache so * w_prefetch() can find the right slot when w_ask() is first called * for each server. No lookups are fired here; resolution happens * lazily on first use via w_prefetch(). */ static const char *servers[] = { pwhois_server, myaddress_server, radb_server, cymru_server, arin_server, apnic_server, ripe_server, ripe_ris_server }; int ns = (int)(sizeof(servers) / sizeof(servers[0])); int i; memset(wsess->w_dns_cache, 0, sizeof(wsess->w_dns_cache)); wsess->w_dns_cache_count = 0; for (i = 0; i < ns && i < 8; i++) { wsess->w_dns_cache[i].host = servers[i]; wsess->w_dns_cache[i].resolved = 0; memset(&wsess->w_dns_cache[i].addr, 0, sizeof(wsess->w_dns_cache[i].addr)); } wsess->w_dns_cache_count = (ns < 8) ? ns : 8; }#endif /* HAVE_CARES */ return wsess;}whois_session_params * w_reinit(whois_session_params * wsess){ /* int e; */ wsess->w_noisy = 0; /* Don't show debug msgs by default */ memset(&(wsess->pw_serv), 0, sizeof(wsess->pw_serv)); wsess->consolidated_asn[0] = wsess->consolidated_asp[0] = wsess->consolidated_route[0] = wsess->consolidated_orgname[0] = wsess->consolidated_netname[0] = '?'; wsess->consolidated_asn[1] = wsess->consolidated_asp[1] = wsess->consolidated_route[1] = wsess->consolidated_orgname[1] = wsess->consolidated_netname[1] = 0; memset(&wsess->tbuf, 0, sizeof(wsess->tbuf)); wsess->logprintfCookie = 0; return wsess;}__inline__ void w_close(whois_session_params * wsess){ free(wsess);}static char *w_ask(whois_session_params *wsess, const char *serv, const char *q, const char *port){ int s; struct sockaddr_in sin4; struct addrinfo ai_hints, *ai_res; char *br; int q_s, br_s, cur, n, myport; char buf[128], *sendbuf;#ifdef USE_WHOIS_TIMEOUT#if defined(WIN32) || defined(_WIN32) int whreadtimeout = 20000;#else struct timeval whreadtimeout; whreadtimeout.tv_sec = 20; whreadtimeout.tv_usec = 0;#endif#endif if (!serv || !q) return NULL; memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET;#ifdef HAVE_CARES { /* Resolve this server on first use (no-op if already cached). */ w_prefetch(wsess, serv); /* Check DNS cache to avoid a blocking getaddrinfo() call; * falls through to getaddrinfo() for unknown/custom servers. */ int _i, _found = 0; for (_i = 0; wsess && _i < wsess->w_dns_cache_count; _i++) { if (wsess->w_dns_cache[_i].host && strcmp(wsess->w_dns_cache[_i].host, serv) == 0 && wsess->w_dns_cache[_i].resolved && wsess->w_dns_cache[_i].addr.s_addr != 0) { memcpy(&sin4.sin_addr, &wsess->w_dns_cache[_i].addr, sizeof(sin4.sin_addr)); _found = 1; break; } } if (!_found) { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (w_getaddrinfo_timed(serv, &ai_hints, &ai_res) != 0) return NULL; memcpy(&sin4.sin_addr, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(sin4.sin_addr)); freeaddrinfo(ai_res); } }#else memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (w_getaddrinfo_timed(serv, &ai_hints, &ai_res) != 0) return NULL; memcpy(&sin4.sin_addr, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(sin4.sin_addr)); freeaddrinfo(ai_res);#endif /* HAVE_CARES */ if (port) { if (!(myport = (int)strtol(port, (char **)NULL, 10))) return NULL; sin4.sin_port = htons(myport); } else { sin4.sin_port = htons(PORT_WHOIS); } if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) return NULL;#ifdef USE_WHOIS_TIMEOUT setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &whreadtimeout, sizeof(whreadtimeout));#endif if (connect(s, (const struct sockaddr *)(const void *)&sin4, sizeof (sin4)) < 0) return NULL; br_s = 512; if (!(br = (char *)malloc(br_s))) return NULL; q_s = strlen(q); sendbuf = (char *)malloc(q_s+2); if(q[q_s-1]=='\r' || q[q_s-1]=='\n') { strncpy(sendbuf, q, q_s+1); } else { strncpy(sendbuf, q, q_s+1); sendbuf[q_s]='\n'; q_s++; sendbuf[q_s]=0; } if (write(s, sendbuf, q_s) != q_s) { /* || write(s, "\r\n", 2) != 2)*/ free(sendbuf); free(br); close(s); return NULL; } cur = 0; while ((n = read(s, buf, sizeof(buf))) > 0) { if ((cur + n) >= br_s) { char *tmp; br_s = br_s * 2; if (!(tmp = realloc(br, br_s))) { free(br); free(sendbuf); close(s); return NULL; } br = tmp; } strncpy((char *)&br[cur], buf, n); cur += n; } br[cur] = 0; close(s); free(sendbuf); return br;}int w_lookup_all_pwhois(whois_session_params * wsess, char *addr){ token_t *ls; char *serv, *reply; const char *format; int i; if (!addr) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return -1; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value = NULL; if ((value = match_prefix("origin-as:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asn,"?",1) == 0) strncpy(wsess->consolidated_asn,value,255); } else if ((value = match_prefix("prefix:", ls[i].ptr))) { if (strncmp(wsess->consolidated_route,"?",1) == 0) strncpy(wsess->consolidated_route,value,255); } else if ((value = match_prefix("as-path:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asp,"?",1) == 0) strncpy(wsess->consolidated_asp,value,255); } else if ((value = match_prefix("org-name:", ls[i].ptr))) { if (strncmp(wsess->consolidated_orgname,"?",1) == 0) strncpy(wsess->consolidated_orgname,value,255); } else if ((value = match_prefix("net-name:", ls[i].ptr))) { if (strncmp(wsess->consolidated_netname,"?",1) == 0) strncpy(wsess->consolidated_netname,value,255); } else if ((value = match_prefix("cache-date:", ls[i].ptr))) { if ((wsess->tval = atol(value)) != 0) { format = "%d-%b-%y %H:%M:%S %Z"; (void)strftime(wsess->tbuf, sizeof(wsess->tbuf), format, localtime(&wsess->tval)); } } if(value) free(value); } free(ls); free(reply); return 0;}int w_lookup_all_riswhois(whois_session_params * wsess, char *addr){ token_t *ls=NULL; char *serv=NULL, *reply=NULL; const char *risopts = "-1 -M "; /* 1 object/prefix, Most specific */ char *risquery = malloc((strlen(risopts)* sizeof(char)) + (strlen(addr) * sizeof(char)) + 1); unsigned int i; if (!addr) { free(risquery); return -1; } /* prepare the text-string-based query */ risquery[0]=0; strcat(risquery,risopts); strcat(risquery,addr); reply = w_ask(wsess, ripe_ris_server, risquery, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",ripe_ris_server); else fprintf(stderr,"No reply from %s.\n",ripe_ris_server); } free(risquery); return -1; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value = NULL; if ((value = match_prefix("origin:", ls[i].ptr))) { if (strncmp(wsess->consolidated_asn,"?",1) == 0) strncpy(wsess->consolidated_asn,value,255); } else if ((value = match_prefix("route:", ls[i].ptr))) { if (strncmp(wsess->consolidated_route,"?",1) == 0) strncpy(wsess->consolidated_route,value,255); } else if ((value = match_prefix("descr:", ls[i].ptr))) { if (strncmp(wsess->consolidated_orgname,"?",1) == 0) strncpy(wsess->consolidated_orgname,value,255); if (strncmp(wsess->consolidated_netname,"?",1) == 0) strncpy(wsess->consolidated_netname,value,255); } if(value) free(value); } free(ls); free(reply); free(risquery); return 0;}intw_lookup_as_pwhois(whois_session_params * wsess, char *addr){ token_t *ls; char *reply = NULL, *value = NULL; unsigned int i; int ans = 0; if (!addr) return 0; reply = w_ask(wsess, pwhois_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { if ((value = match_prefix("origin-as:", ls[i].ptr))) break; } free(ls); free(reply); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit((unsigned char)value[i])) { free(value); return 0; } } ans = strtol(value, (char **)NULL, 10); free(value); return ans;}intw_lookup_as_riswhois(whois_session_params * wsess, char *addr){ token_t *ls; char *reply = NULL, *value = NULL; const char *risopts = "-1 -M "; /* 1 object/prefix, Most specific */ char *risquery = malloc((strlen(risopts) * sizeof(char)) + (strlen(addr) * sizeof(char)) + 1); unsigned int i; int ans = 0; if (!addr) { free(risquery); return 0; } /* prepare the text-string-based query */ risquery[0]=0; strcat(risquery,risopts); strcat(risquery,addr); reply = w_ask(wsess, ripe_ris_server, risquery, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",ripe_ris_server); else fprintf(stderr,"No reply from %s.\n",ripe_ris_server); } free(risquery); return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { if ((value = match_prefix("origin:", ls[i].ptr))) break; } free(ls); free(reply); free(risquery); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit((unsigned char)value[i])) { free(value); return 0; } } ans = atoi(value); free(value); return ans;}intw_lookup_all_riswhois_bulk(whois_session_params * wsess, struct ip_list_array *iplist){ token_t *responses=0; char *reply=0; const char *bulk_begin = "-k -1 -M\n"; /* Keepalive, 1 object/prefix, Most specific */ const char *bulk_end = "-k"; char *bulk_ip_query = malloc((strlen(bulk_begin) * sizeof(char)) + ((strlen(bulk_end)+1) * sizeof(char)) + (16 * (*iplist).numItems)); int i = 0; unsigned int j = 0; int k = 0; int entity_id = 0; unsigned int until = 0; char *value = NULL; bulk_ip_query[0]=0; if (!iplist) { free(bulk_ip_query); return -1; } /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, ripe_ris_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",ripe_ris_server); else fprintf(stderr,"No reply from %s.\n",ripe_ris_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; free(bulk_ip_query); return -1; } responses = tokens(reply, "\n"); for (i = 0; responses[i].ptr; i++) { value = NULL; if ((value = match_prefix("origin:", responses[i].ptr)) != NULL) { if (k > 0) { entity_id++; k = 0; } rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit((unsigned char)value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); else printf("Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); } break; } } if(strtol(value, (char **) NULL, 10)) { (*iplist).asn[(entity_id)] = strtol(value, (char **)NULL, 10); k++; } else if (wsess->w_noisy > 2) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Skipping additional object for same query.\n"); else fprintf(stderr,"Skipping additional object for same query.\n"); } } else if ((value = match_prefix("descr:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,sizeof((*iplist).orgName[entity_id])-1); (*iplist).orgName[entity_id][sizeof((*iplist).orgName[entity_id])-1] = '\0'; /* yes, this is duplicated. riswhois adds a netname attribute here, so we reuse 'descr' */ for (until = 0; until < strlen(value); until++) { if (isspace((unsigned char)value[until])) break; } /* clamp the server-controlled descr length to the field width and * NUL-terminate (netName rows are fixed-size). */ if (until > sizeof((*iplist).netName[entity_id]) - 1) until = sizeof((*iplist).netName[entity_id]) - 1; strncpy((*iplist).netName[entity_id],value,(until)); (*iplist).netName[entity_id][until] = '\0'; k++; } else if ((value = match_prefix("% ", responses[i].ptr)) != NULL) { if (i > 5 && k < 1) { /* Weed out up to 5 leading lines from RIPE NCC RIS */ if (wsess->w_noisy > 2) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"%% MATCHED on '%s'\n",responses[i].ptr); else printf("%% MATCHED on '%s'\n",responses[i].ptr); } /* (*iplist).asn[(entity_id)] = 0; */ k++; } /* else printf("'%s'\n",responses[i].ptr); */ } /* else printf("'%s'\n",responses[i].ptr); */ if(value) free(value); if ((entity_id) >= (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0;}intw_lookup_as(whois_session_params * wsess, char *addr){ token_t *ls; ip_blk_t *a = NULL, *b = NULL; /* char *sa, *sb; */ char *reply, *value = NULL; unsigned int i; int ans = 0; int use_this = 1; if (!addr) return 0; reply = w_ask(wsess, radb_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",radb_server); else fprintf(stderr,"No reply from %s.\n",radb_server); } return 0; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { value = NULL; if ((value = match_prefix("local-as:", ls[i].ptr)) != NULL) { break; } else if ((value = match_prefix("route:", ls[i].ptr)) != NULL || (value = match_prefix("route6:", ls[i].ptr)) != NULL) { /* route6 prefixes yield a NULL range from the v4 mask helper; * the size-refinement below is simply skipped for them */ a = match_ipprefix(value); if (b && a) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; if (a) { if (b) free(b); b = a; } a = NULL; } } else if (use_this && (value = match_prefix("origin:", ls[i].ptr))) { break; } if(value != NULL) free(value); } free(ls); free(reply); if(b != NULL) free(b); if (!value) return 0; rm_spaces(value); for (i = 0; i < strlen(value); i++) { if (!isdigit((unsigned char)value[i])) { return 0; } } ans = strtol(value, (char **)NULL, 10); free(value); return ans;}intw_lookup_as_cymru(whois_session_params * wsess, char *addr){ /* * Look up the ASN at the prefix-based Cymru whois server */ token_t *ls; char *reply; unsigned int i; char value[6]; memset(&value, 0, sizeof(value)); if (!addr) return 0; reply = w_ask(wsess, cymru_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",cymru_server); else fprintf(stderr,"No reply from %s.\n",cymru_server); } return 0; } ls = tokens(reply, "\n"); /* Set i to 1 to skip the first/header line of reply from cymru */ strncpy(value,ls[1].ptr,5); rm_spaces(value); /* strip out any spaces from the ASN */ for (i = 0; i < strlen(value); i++) { if (!isdigit((unsigned char)value[i])) { return 0; } } free(ls); free(reply); return (strtol(value, (char **)NULL, 10));}intw_lookup_as_cymru_bulk(whois_session_params * wsess, struct ip_list_array *iplist){ token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = malloc((strlen(bulk_begin) * sizeof(char)) + (strlen(bulk_end)* sizeof(char)) + (16 * (*iplist).numItems)); int i; unsigned int j; char value[6]; memset(&value, 0, sizeof(value)); bulk_ip_query[0]=0; if (!iplist) { free(bulk_ip_query); return -1; } /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, cymru_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",cymru_server); else fprintf(stderr,"No reply from %s.\n",cymru_server); } free(bulk_ip_query); return -1; } responses = tokens(reply, "\n"); /* Set i to 1 to skip the first/header line of reply from cymru */ for (i = 1; responses[i].ptr; i++) { strncpy(value,responses[i].ptr,5); rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit((unsigned char)value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); } break; } } if(strtol(value, (char **)NULL, 10)) { (*iplist).asn[(i-1)] = strtol(value, (char **)NULL, 10); } else { (*iplist).asn[(i-1)] = 0; } if ((i+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0;}#ifdef STANDALONEstatic int w_display_rvbyasn_pwhois(whois_session_params * wsess, char *asn){ char *reply; const char *query_begin = "routeview source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( (strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } snprintf(w_highlight_asn, sizeof(w_highlight_asn), "%s", asn); w_print_pwhois_colorized(reply); w_highlight_asn[0] = '\0'; free(reply); free(whob_query); return 0;}static int w_display_rvbytransitasn_pwhois(whois_session_params * wsess, char *asn){ char *reply; const char *query_begin = "routeview transit-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( (strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } snprintf(w_highlight_asn, sizeof(w_highlight_asn), "%s", asn); w_print_pwhois_colorized(reply); w_highlight_asn[0] = '\0'; free(reply); free(whob_query); return 0;}static int w_display_contactsbyasn_pwhois(whois_session_params * wsess, char *asn){ char *reply; const char *query_begin = "registry source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc( ((strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char)) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } w_print_pwhois_colorized(reply); free(reply); free(whob_query); return 0;}static int w_display_networksbyasn_pwhois(whois_session_params * wsess, char *asn){ char *reply; const char *query_begin = "netblock source-as="; char *whob_query = NULL; char *serv; if (!asn) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query =(char *)malloc(((strlen(appname) + strlen(version) + strlen(query_begin) + strlen(asn)) * sizeof(char)) + 10); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,asn); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } w_print_pwhois_colorized(reply); free(reply); free(whob_query); return 0;}static int w_display_rvbyprefix_pwhois(whois_session_params * wsess, char *prefix){ char *reply; const char *query_begin = "routeview prefix="; char *whob_query = NULL; char *serv; if (!prefix) return -1; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; whob_query = malloc(((strlen(appname) * sizeof(char))+10) + (strlen(version) * sizeof(char)) + (strlen(query_begin) * sizeof(char)) + (strlen(prefix))* sizeof(char)); whob_query[0]=0; /* prepare the text-string-based query */ strcat(whob_query,"app=\""); strcat(whob_query,appname); strcat(whob_query," "); strcat(whob_query,version); strcat(whob_query,"\" "); strcat(whob_query,query_begin); strcat(whob_query,prefix); strcat(whob_query,"\n"); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } w_print_pwhois_colorized(reply); free(reply); free(whob_query); return 0;}static int w_display_bulkfromfile_pwhois(whois_session_params * wsess, char *filespec){ const char *query_begin = "begin\n"; const char *query_end = "end\n"; const char *appname_extras = "BULK_FILE"; const char *format_instructions = "type=cymru\n"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = malloc(line_size * max_lines); char *this_line = malloc(line_size); size_t whob_query_len = ((strlen(appname) * sizeof(char) +10) + (strlen(appname_extras) * sizeof(char)) + (strlen(version) * sizeof(char))) + (strlen(query_begin) * sizeof(char)) + (strlen(format_instructions) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line,line_size - 1,bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines,this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if (feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if (ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, "app=\""); strcat(whob_query, appname); strcat(whob_query, " "); strcat(whob_query, version); strcat(whob_query, " "); strcat(whob_query, appname_extras); strcat(whob_query, "\"\n"); if (use_cymru) strcat(whob_query, format_instructions); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } w_print_pwhois_colorized(reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0;}static int w_display_bulkfromfile_riswhois(whois_session_params * wsess, char *filespec){ const char *query_begin = "-k -1 -M\n"; /* Keepalive, 1 object/prefix, Most specific */ const char *query_end = "-k"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = (char *)malloc(line_size * max_lines); char *this_line = (char *)malloc(line_size); size_t whob_query_len = 10 + (strlen(query_begin) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = ripe_ris_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line,line_size - 1,bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines,this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if(feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if (ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } printf("%s",reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0;}static int w_display_bulkfromfile_cymru(whois_session_params * wsess, char *filespec){ const char *query_begin = "begin\n"; const char *query_end = "end"; char *reply; char *serv; FILE *bulkFile; int num_lines = 0, i = 0; char *lines = (char *)malloc(line_size * max_lines); char *this_line = (char *)malloc(line_size); size_t whob_query_len = 10 + (strlen(query_begin) * sizeof(char)) + (strlen(query_end) * sizeof(char)) + (line_size * max_lines); char *whob_query = (char *)malloc(whob_query_len); reply = NULL; *whob_query = '\0'; if (!filespec) { fprintf(stderr,"You must specify a file to use (or '-' for stdin) for bulk query input.\n"); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (!strncmp(filespec,"-",1) || use_stdin == 1) { bulkFile = stdin; } else bulkFile = fopen(filespec, "r"); if (!bulkFile) { fprintf(stderr,"%s: Unable to open \'%s\' for reading.\n",appname,filespec); free(lines); free(this_line); free(whob_query); exit(EXIT_FAILURE); } if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = cymru_server; while (!feof(bulkFile)) { if (num_lines >= max_lines && wsess->w_noisy >= 2) fprintf(stderr,"Processing next batch of %d beginning at line %d.\n",max_lines,(num_lines+1)); memset(lines, 0, line_size * max_lines); memset(whob_query, 0, whob_query_len); for (i = 0; i < max_lines; i++) { if (fgets(this_line, line_size - 1, bulkFile)) { if (strncmp(this_line, "#", 1) && strncmp(this_line, ";", 1)) { strcat(lines, this_line); num_lines++; if (wsess->w_noisy >= 4) fprintf(stderr,"Line %d: %s",num_lines,this_line); } } else if(feof(bulkFile)) { if (wsess->w_noisy >= 2) fprintf(stderr,"End of file reached after reading %d lines.\n",num_lines); break; } else if(ferror(bulkFile)) { if (wsess->w_noisy >= 1) fprintf(stderr,"Error in stream on line %d.\n",num_lines+1); break; } } /* prepare the text-string-based query */ strcat(whob_query, query_begin); strcat(whob_query, lines); strcat(whob_query, query_end); reply = w_ask(wsess, serv, whob_query, NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); free(this_line); free(lines); free(whob_query); free(reply); return -1; } printf("%s",reply); } free(this_line); free(lines); free(whob_query); free(reply); fclose(bulkFile); return 0;}static int w_display_pwhois_version(whois_session_params * wsess){ char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for version/status.\n",serv); reply = w_ask(wsess, serv, "version", NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } w_print_status_colorized(reply); free(reply); return 0;}static int w_display_myaddress_http(whois_session_params * wsess){ char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = myaddress_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for my public address.\n",serv); reply = w_ask(wsess, serv, "Accept:*/*\nHost: myaddress.today\nUser-Agent: WhoB\nGET /\n\n", "80"); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0;}static int w_display_myaddress(whois_session_params * wsess){ char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) fprintf(stderr,"Querying '%s' for my public address.\n",serv); reply = w_ask(wsess, serv, "whoami", NULL); if (!reply) { if(wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } printf("%s",reply); free(reply); return 0;}/* Query the terminal's background colour via OSC 11 and decide light vs dark. * Returns 1 (light), 0 (dark), or -1 (unknown / not a terminal / no answer). * The round-trip runs ONLY when both stdout and stdin are real terminals, so * a redirect or pipe on either side never triggers it. Terminal raw-mode is * saved and restored; a ~150 ms poll bounds the wait so an unresponsive * terminal (e.g. macOS Terminal.app) can't stall whob. */static int w_query_bg_is_light(void){#if defined(WIN32) || defined(_WIN32) return -1;#else struct termios saved, raw; char buf[64]; int n = 0, got = 0, light = -1; unsigned long r = 0, g = 0, b = 0, mx = 0; /* Only probe an interactive terminal on BOTH ends. */ if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) return -1; if (tcgetattr(STDIN_FILENO, &saved) != 0) return -1; raw = saved; raw.c_lflag &= ~(ICANON | ECHO); raw.c_cc[VMIN] = 0; raw.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) != 0) return -1; /* Ask: OSC 11 ; ? BEL — reply is ESC ] 11 ; rgb:RRRR/GGGG/BBBB (ST|BEL) */ if (write(STDOUT_FILENO, "\033]11;?\007", 7) == 7) { struct pollfd pfd; pfd.fd = STDIN_FILENO; pfd.events = POLLIN; /* Drain the reply, allowing a short gap between bursts. */ while (n < (int)sizeof(buf) - 1 && poll(&pfd, 1, 150) > 0) { ssize_t k = read(STDIN_FILENO, buf + n, sizeof(buf) - 1 - n); if (k <= 0) break; n += (int)k; if (memchr(buf, '\007', n) || (n >= 2 && buf[n-2] == '\033' && buf[n-1] == '\\')) break; /* terminator seen */ } } tcsetattr(STDIN_FILENO, TCSANOW, &saved); buf[n] = '\0'; /* Parse "rgb:RRRR/GGGG/BBBB" (1-4 hex digits per channel). */ { char *p = strstr(buf, "rgb:"); if (p) { char cs[3][8]; if (sscanf(p + 4, "%7[0-9a-fA-F]/%7[0-9a-fA-F]/%7[0-9a-fA-F]", cs[0], cs[1], cs[2]) == 3) { int L0 = (int)strlen(cs[0]); r = strtoul(cs[0], NULL, 16); g = strtoul(cs[1], NULL, 16); b = strtoul(cs[2], NULL, 16); mx = (1UL << (4 * L0)) - 1; /* full-scale for that width */ got = (mx > 0); } } } if (got) { /* Perceived luminance on 0..1; > 0.5 => light background. */ double lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / (double)mx; light = (lum > 0.5) ? 1 : 0; } return light;#endif}/* COLORFGBG fallback ("fg;bg", e.g. "15;0"): bg 0-6 or 8 => dark, else light. */static int w_colorfgbg_is_light(void){ const char *e = getenv("COLORFGBG"); const char *bg; int v; if (!e) return -1; bg = strrchr(e, ';'); bg = bg ? bg + 1 : e; if (bg[0] < '0' || bg[0] > '9') /* "default" etc. */ return -1; v = atoi(bg); if (v == 8) return 0; return (v >= 7) ? 1 : 0;}/* Resolve colour state once, after option parsing and before any output. * Mirrors lft's -o detection (isatty + TERM + NO_COLOR); --color / --no-color * override. Also resolves truecolor capability and the light/dark variant. */static void w_detect_color(void){ w_use_color = 0; w_truecolor = 0; w_bg_light = 0; if (w_color_mode == 2) /* --no-color: hard off */ return;#if !defined(WIN32) && !defined(_WIN32) { const char *term = getenv("TERM"); const char *colorterm = getenv("COLORTERM"); if (w_color_mode == 1) { /* --color: force on */ w_use_color = 1; } else if (isatty(fileno(stdout)) && term && strncmp(term, "dumb", 4) != 0 && strncmp(term, "unknown", 7) != 0 && !getenv("NO_COLOR") && (colorterm || strncmp(term, "xterm", 5) == 0 || strncmp(term, "screen", 6) == 0 || strncmp(term, "tmux", 4) == 0 || strncmp(term, "rxvt", 4) == 0)) { w_use_color = 1; } if (w_use_color) { int bg; if (colorterm && (strstr(colorterm, "truecolor") || strstr(colorterm, "24bit"))) w_truecolor = 1; /* Background probe is self-gated to TTY-on-both-ends. */ bg = w_query_bg_is_light(); if (bg < 0) bg = w_colorfgbg_is_light(); w_bg_light = (bg == 1) ? 1 : 0; /* default: dark */ } }#else if (w_color_mode == 1) w_use_color = 1;#endif}/* Colour is allowed on the verbose/diagnostic stream (stderr) only when colour * is enabled AND stderr is itself a terminal, so redirected logs stay clean. */static int w_ecolor(void){#if defined(WIN32) || defined(_WIN32) return 0;#else return w_use_color && isatty(fileno(stderr));#endif}/* SGR sequence for a field group, honouring truecolor vs 256-colour and the * resolved light/dark variant. Per-group static buffers so several calls can * coexist in one printf. */static const char *w_field_seq(int field){ static char buf[WF_NGROUPS][24]; const wc_color *c; if (field < 0 || field >= WF_NGROUPS) return ""; c = &w_palette[w_bg_light][field]; if (w_truecolor) snprintf(buf[field], sizeof(buf[field]), "\033[38;2;%u;%u;%um", c->r, c->g, c->b); else snprintf(buf[field], sizeof(buf[field]), "\033[38;5;%um", c->x256); return buf[field];}/* SGR for one of the three table date shades (0=Create,1=Modify,2=Originated). */static const char *w_date_seq(int shade){ static char buf[3][24]; const wc_color *c; if (shade < 0 || shade > 2) return ""; c = &w_date_shade[w_bg_light][shade]; if (w_truecolor) snprintf(buf[shade], sizeof(buf[shade]), "\033[38;2;%u;%u;%um", c->r, c->g, c->b); else snprintf(buf[shade], sizeof(buf[shade]), "\033[38;5;%um", c->x256); return buf[shade];}/* Case-insensitive "does s end with suf?" */static int w_ends(const char *s, const char *suf){ size_t ls = strlen(s), lf = strlen(suf); return ls >= lf && strcasecmp(s + ls - lf, suf) == 0;}/* Map a pWhoIs field label to a colour group. Pattern-based (suffix/exact) * rather than an exhaustive table, so it covers the compact v4 record, the * larger v6 / type=all record (Geo-*, Abuse-0-*, Admin-0-*, NOC-0-*, Tech-0-* * contact blocks, Route-Prefix, Create-/Modify-Date, …) and future fields * uniformly. Order matters: more specific tests come first. */static int w_pwhois_field(const char *s){ if (w_ends(s, "-Date") || strcasecmp(s, "Route-Originated-TS") == 0) return WF_DATE; if (w_ends(s, "Latitude") || w_ends(s, "Longitude")) return WF_GEO; if (w_ends(s, "-City") || w_ends(s, "-Country") || w_ends(s, "-State") || w_ends(s, "-Postal-Code") || w_ends(s, "-Street-1") || w_ends(s, "-Street-2") || w_ends(s, "-Region") || w_ends(s, "-CC") || strcasecmp(s, "City") == 0 || strcasecmp(s, "Region") == 0 || strcasecmp(s, "Country") == 0 || strcasecmp(s, "Country-Code") == 0 || strcasecmp(s, "State") == 0 || strcasecmp(s, "Postal-Code") == 0 || strcasecmp(s, "Street-1") == 0 || strcasecmp(s, "Street-2") == 0) return WF_GEO; if (w_ends(s, "-Prefix") || strcasecmp(s, "IP") == 0 || strcasecmp(s, "Prefix") == 0 || strcasecmp(s, "Net-Range") == 0 || strcasecmp(s, "Next-Hop") == 0 || strcasecmp(s, "Router-ID") == 0) return WF_PREFIX; if (strcasecmp(s, "Origin-AS") == 0 || strcasecmp(s, "Transit-AS") == 0 || strcasecmp(s, "AS-Path") == 0 || strcasecmp(s, "ASN") == 0) return WF_ASN; if (w_ends(s, "-Name") || w_ends(s, "-Handle") || w_ends(s, "-Org-ID") || strcasecmp(s, "Org-ID") == 0 || strcasecmp(s, "Org-Name") == 0 || strcasecmp(s, "Net-Name") == 0) return WF_ORG; return WF_NONE;}/* True if the value is purely a count/number (digits, optional grouping * commas/spaces) — used to give unlabelled numeric values (e.g. the server * statistics counts) the ASN/number colour. */static int w_value_numeric(const char *s, size_t len){ size_t i; int digits = 0; if (!len) return 0; for (i = 0; i < len; i++) { if (s[i] >= '0' && s[i] <= '9') digits = 1; else if (s[i] != ',' && s[i] != ' ') return 0; } return digits;}/* Per-column render kinds for the -a / -A route tables. */enum { CK_NONE = 0, CK_PREFIX, CK_ASN, CK_ASPATH, CK_ORG_HI, CK_ORG_LO, CK_GEO, CK_DATE_C, CK_DATE_M, CK_DATE_O };#define W_MAXCOL 24/* Map a (trimmed) table column header name to a render kind. */static int w_col_kind(const char *name){ static const struct { const char *n; int k; } map[] = { { "Prefix", CK_PREFIX }, { "Next-Hop", CK_PREFIX }, { "Origin-AS", CK_ASN }, { "AS-Path", CK_ASPATH }, { "Org-Name", CK_ORG_HI }, /* more specific -> higher-viz */ { "AS-Org-Name", CK_ORG_LO }, { "CC", CK_GEO }, { "Country-Code", CK_GEO }, { "Create-Date", CK_DATE_C }, { "Modify-Date", CK_DATE_M }, { "Originated-Date", CK_DATE_O }, { "Route-Originated-Date",CK_DATE_O }, { NULL, CK_NONE } }; int i; for (i = 0; map[i].n; i++) if (strcasecmp(name, map[i].n) == 0) return map[i].k; return CK_NONE;}/* Copy [p,end) with surrounding spaces trimmed into a bounded buffer. */static void w_trim_copy(const char *p, const char *end, char *out, size_t outsz){ size_t n; while (p < end && *p == ' ') p++; while (end > p && end[-1] == ' ') end--; n = (size_t)(end - p); if (n >= outsz) n = outsz - 1; memcpy(out, p, n); out[n] = '\0';}/* Emit the leading BGP status flag of a data row's first column: the best-path * '>' takes the prefix colour, '*' is dimmed, everything else prints as-is. */static void w_emit_flag(const char *s, size_t len){ size_t i; for (i = 0; i < len; i++) { if (s[i] == '>') printf("%s>%s", w_field_seq(WF_PREFIX), WC_RESET); else if (s[i] == '*') printf("%s*%s", WC_DIM, WC_RESET); else putchar(s[i]); }}/* Emit an AS-Path cell in the ASN colour, bolding whole-token occurrences of * the queried ASN so it can be traced through every path. */static void w_emit_aspath(const char *s, size_t len){ const char *seq = w_field_seq(WF_ASN); size_t hllen = strlen(w_highlight_asn); size_t i = 0; printf("%s", seq); while (i < len) { size_t j; if (s[i] == ' ' || s[i] == '\t') { putchar(s[i]); i++; continue; } j = i; while (j < len && s[j] != ' ' && s[j] != '\t') j++; if (hllen && (j - i) == hllen && strncmp(s + i, w_highlight_asn, hllen) == 0) printf("%s%.*s\033[22m", WC_BOLD, (int)(j - i), s + i); /* bold, keep colour */ else printf("%.*s", (int)(j - i), s + i); i = j; } printf("%s", WC_RESET);}/* Emit one table data cell (not the flag/prefix column 0) by its render kind. */static void w_emit_cell(const char *s, size_t len, int kind){ switch (kind) { case CK_PREFIX: printf("%s%.*s%s", w_field_seq(WF_PREFIX), (int)len, s, WC_RESET); break; case CK_ASN: printf("%s%.*s%s", w_field_seq(WF_ASN), (int)len, s, WC_RESET); break; case CK_GEO: printf("%s%.*s%s", w_field_seq(WF_GEO), (int)len, s, WC_RESET); break; case CK_ORG_HI: printf("%s%s%.*s%s", WC_BOLD, w_field_seq(WF_ORG), (int)len, s, WC_RESET); break; case CK_ORG_LO: printf("%s%s%.*s%s", WC_DIM, w_field_seq(WF_ORG), (int)len, s, WC_RESET); break; case CK_DATE_C: printf("%s%.*s%s", w_date_seq(0), (int)len, s, WC_RESET); break; case CK_DATE_M: printf("%s%.*s%s", w_date_seq(1), (int)len, s, WC_RESET); break; case CK_DATE_O: printf("%s%.*s%s", w_date_seq(2), (int)len, s, WC_RESET); break; case CK_ASPATH: w_emit_aspath(s, len); break; default: printf("%.*s", (int)len, s); break; }}/* Split a '|'-delimited line into cells and invoke cb(field, end, col). * Separators are emitted dimmed. Preserves every space so column alignment * (which the server already produced) is untouched — only zero-width SGR * codes are inserted. */static void w_render_row(const char *line, size_t len, void (*cb)(const char *, const char *, int)){ const char *p = line, *end = line + len; int col = 0; while (p <= end) { const char *bar = (const char *)memchr(p, '|', (size_t)(end - p)); const char *fend = bar ? bar : end; cb(p, fend, col); if (!bar) break; printf("%s|%s", WC_DIM, WC_RESET); p = bar + 1; col++; }}/* Header/data column kinds shared between the header row and the rows below it. * A reply carries one header then its rows, so file-scope state is sufficient. */static int w_col_kinds[W_MAXCOL];static int w_col_ncols = 0;static void w_header_cb(const char *p, const char *fend, int col){ char name[40]; w_trim_copy(p, fend, name, sizeof(name)); if (col < W_MAXCOL) { w_col_kinds[col] = w_col_kind(name); w_col_ncols = col + 1; } printf("%s%s%.*s%s", WC_BOLD, w_field_seq(WF_LABEL), (int)(fend - p), p, WC_RESET);}static void w_data_cb(const char *p, const char *fend, int col){ int kind = (col < w_col_ncols) ? w_col_kinds[col] : CK_NONE; if (col == 0) { /* Column 0 = BGP flag(s) + spaces + prefix (+ trailing spaces). The * prefix is the last whitespace-delimited token, so a v6 prefix that * starts with a hex letter (e.g. fe80::/10) is not mis-split; the flag * region is everything before it, trailing spaces are preserved. */ const char *pe = fend, *ps; while (pe > p && pe[-1] == ' ') pe--; ps = pe; while (ps > p && ps[-1] != ' ') ps--; w_emit_flag(p, (size_t)(ps - p)); if (pe > ps) printf("%s%.*s%s", w_field_seq(WF_PREFIX), (int)(pe - ps), ps, WC_RESET); if (fend > pe) printf("%.*s", (int)(fend - pe), pe); } else { w_emit_cell(p, (size_t)(fend - p), kind); }}/* Print a raw pWhoIs reply with the Cartographer colour scheme. Handles the * default "Label: value" output and the tabular -a / -A route views (header * row learns the columns, rows below are coloured per column). Unrecognised * lines pass through; with colour off this is a straight fputs. *//* Colourise a JSON / JSONP (type=json, type=jsonp) reply. Keys take the label * colour, values the colour of their field group (numeric values of unknown * keys read as numbers), structural punctuation is dimmed, and whitespace is * emitted verbatim so both the pretty and compact forms keep their shape. */static void w_print_json_colorized(const char *reply){ const char *p = reply; int expect_key = 1; char key[64]; key[0] = '\0'; while (*p) { unsigned char c = (unsigned char)*p; if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { putchar(c); p++; continue; } if (c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') { printf("%s%c%s", WC_DIM, c, WC_RESET); if (c == ':') expect_key = 0; else if (c == ',') expect_key = 1; else if (c == '{') expect_key = 1; else if (c == '[') expect_key = 0; p++; continue; } if (c == '"') { /* a quoted string: key or value */ const char *s = p + 1, *q = s; while (*q && *q != '"') { if (*q == '\\' && q[1]) q += 2; else q++; } if (expect_key) { size_t klen = (size_t)(q - s); if (klen < sizeof(key)) { memcpy(key, s, klen); key[klen] = '\0'; } else key[0] = '\0'; printf("%s\"%.*s\"%s", w_field_seq(WF_LABEL), (int)(q - s), s, WC_RESET); } else { int f = w_pwhois_field(key); if (f != WF_NONE) printf("%s\"%.*s\"%s", w_field_seq(f), (int)(q - s), s, WC_RESET); else printf("\"%.*s\"", (int)(q - s), s); } p = *q ? q + 1 : q; continue; } { /* bare value: number/true/false/null */ const char *s = p; int f; size_t tl; while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ':' && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') p++; tl = (size_t)(p - s); f = w_pwhois_field(key); if (f != WF_NONE) printf("%s%.*s%s", w_field_seq(f), (int)tl, s, WC_RESET); else if (tl && (s[0] == '-' || (s[0] >= '0' && s[0] <= '9'))) printf("%s%.*s%s", w_field_seq(WF_ASN), (int)tl, s, WC_RESET); else printf("%.*s", (int)tl, s); /* true / false / null */ } }}static void w_print_pwhois_colorized(const char *reply){ const char *p = reply; if (!w_use_color) { fputs(reply, stdout); return; } /* JSON / JSONP replies (type=json, type=jsonp) begin with '{' or '['. */ { const char *j = reply; while (*j == ' ' || *j == '\t' || *j == '\n' || *j == '\r') j++; if (*j == '{' || *j == '[') { w_print_json_colorized(reply); return; } } w_col_ncols = 0; while (*p) { const char *eol = strchr(p, '\n'); size_t linelen = eol ? (size_t)(eol - p) : strlen(p); const char *nl = eol ? "\n" : ""; const char *bar = (const char *)memchr(p, '|', linelen); if (bar) { /* A table line: header if the first non-space char is a letter * (column names), otherwise a data row ('*' flag or a digit). */ const char *q = p; while (q < p + linelen && *q == ' ') q++; int is_hdr = (q < p + linelen) && (((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z'))); if (is_hdr) w_render_row(p, linelen, w_header_cb); else w_render_row(p, linelen, w_data_cb); fputs(nl, stdout); } else { const char *colon = memchr(p, ':', linelen); if (colon && colon > p) { char label[64]; size_t llen = (size_t)(colon - p); const char *val = colon + 1; size_t vlen; while (val < p + linelen && *val == ' ') val++; vlen = (size_t)(p + linelen - val); if (llen < sizeof(label)) { int f; const char *lab; memcpy(label, p, llen); label[llen] = '\0'; lab = label; while (*lab == ' ') lab++; /* stats labels are indented */ f = w_pwhois_field(lab); printf("%s%s:%s ", w_field_seq(WF_LABEL), label, WC_RESET); if (f != WF_NONE) printf("%s%.*s%s%s", w_field_seq(f), (int)vlen, val, WC_RESET, nl); else if (w_value_numeric(val, vlen)) printf("%s%.*s%s%s", w_field_seq(WF_ASN), (int)vlen, val, WC_RESET, nl); else printf("%.*s%s", (int)vlen, val, nl); if (!eol) break; p = eol + 1; continue; } } printf("%.*s%s", (int)linelen, p, nl); /* passthrough */ } if (!eol) break; p = eol + 1; }}/* --- free-form status colouring (whob -s) --------------------------------- */static int w_tok_is_month(const char *t, size_t len){ static const char *m[] = { "jan","feb","mar","apr","may","jun", "jul","aug","sep","oct","nov","dec" }; int i; if (len != 3) return 0; for (i = 0; i < 12; i++) if (strncasecmp(t, m[i], 3) == 0) return 1; return 0;}/* A date component after a month: a run of digits with optional ':' / '-' / ','. */static int w_tok_is_datepart(const char *t, size_t len){ size_t i; int digit = 0; if (!len) return 0; for (i = 0; i < len; i++) { char c = t[i]; if (c >= '0' && c <= '9') digit = 1; else if (c != ':' && c != '-' && c != ',') return 0; } return digit;}static int w_tok_is_number(const char *t, size_t len){ size_t i; if (!len) return 0; for (i = 0; i < len; i++) if (!(t[i] >= '0' && t[i] <= '9')) return 0; return 1;}/* Colour the pWhoIs server status/version banner (-s): counts in the number * colour, "Mon DD YYYY HH:MM:SS" date runs in the date colour, a leading * "(version)" token in the label colour; descriptive prose passes through. */static void w_print_status_colorized(const char *reply){ const char *p = reply; if (!w_use_color) { fputs(reply, stdout); return; } while (*p) { const char *eol = strchr(p, '\n'); const char *end = eol ? eol : p + strlen(p); const char *q = p; while (q < end) { const char *t; size_t tl; if (*q == ' ') { putchar(' '); q++; continue; } t = q; while (q < end && *q != ' ') q++; tl = (size_t)(q - t); if (w_tok_is_month(t, tl)) { /* month + following date-part tokens share the date colour */ printf("%s%.*s", w_field_seq(WF_DATE), (int)tl, t); while (q < end) { const char *sp = q, *t2; size_t tl2; while (q < end && *q == ' ') q++; t2 = q; while (q < end && *q != ' ') q++; tl2 = (size_t)(q - t2); if (tl2 && w_tok_is_datepart(t2, tl2)) { fwrite(sp, 1, (size_t)(t2 - sp), stdout); /* gap */ printf("%.*s", (int)tl2, t2); } else { printf("%s", WC_RESET); q = sp; /* re-handle from the gap */ break; } } printf("%s", WC_RESET); } else if (w_tok_is_number(t, tl)) { printf("%s%.*s%s", w_field_seq(WF_ASN), (int)tl, t, WC_RESET); } else if (tl > 2 && t[0] == '(' && memchr(t, '.', tl) && strchr("0123456789", t[1])) { /* a version like "(2.2.2.3-fix)" — not "(c)" */ printf("%s%.*s%s", w_field_seq(WF_LABEL), (int)tl, t, WC_RESET); } else { printf("%.*s", (int)tl, t); } } if (!eol) break; putchar('\n'); p = eol + 1; }}/* ==== --annotate: enrich IP addresses found in arbitrary text ============== * A Unix filter (`traceroute x | whob --annotate`, `grep denied fw.log | ...`). * Reads stdin (or --annotate=FILE) in chunks, finds IPv4/IPv6 literals with * inet_pton validation (so version numbers, times and domain names are never * mistaken for addresses; ip:port, CIDR and trailing punctuation are handled), * resolves the DISTINCT addresses through batched pWhoIs bulk lookups, and * re-emits every line verbatim with " [ASn Org-Name]" after each address * ("[?]" when pWhoIs has nothing). Colourised on a terminal like the rest. */static int annotate_mode = 0;static char annotate_file[1024] = "";#define ANN_BATCH 500 /* addresses per pWhoIs bulk request */#define ANN_CHUNK 2000 /* input lines buffered per pass */struct ann_ent { char ip[INET6_ADDRSTRLEN]; int asn; int have; char org[100]; char asorg[100]; char net[32]; char prefix[48]; char city[50]; char region[50]; char country[50]; char cc[4]; };/* -F/--fields: which pWhoIs fields the annotation carries, printed in this order */enum { AF_ASN=1, AF_ORG=2, AF_ASORG=4, AF_NET=8, AF_PREFIX=16, AF_CITY=32, AF_REGION=64, AF_COUNTRY=128, AF_CC=256, AF_PLACE=512 };#define AF_DEFAULT (AF_ASN|AF_ORG)#define AF_ALL (AF_ASN|AF_ORG|AF_ASORG|AF_NET|AF_PREFIX|AF_CITY|AF_REGION|AF_COUNTRY|AF_CC|AF_PLACE)static unsigned ann_fields = AF_DEFAULT;static int ann_parse_fields(const char *spec) /* 0 = bad token */{ static const struct { const char *name; unsigned bit; } tok[] = { {"asn",AF_ASN},{"org",AF_ORG},{"asorg",AF_ASORG},{"net",AF_NET},{"prefix",AF_PREFIX}, {"city",AF_CITY},{"region",AF_REGION},{"country",AF_COUNTRY},{"cc",AF_CC},{"place",AF_PLACE},{"all",AF_ALL} }; unsigned f = (*spec == '+' || *spec == '-') ? AF_DEFAULT : 0; char buf[256], *p, *save = NULL; snprintf(buf, sizeof buf, "%s", spec); for (p = strtok_r(buf, ",", &save); p; p = strtok_r(NULL, ",", &save)) { int sign = 1; size_t i; unsigned bit = 0; if (*p == '+') p++; else if (*p == '-') { sign = 0; p++; } for (i = 0; i < sizeof tok / sizeof tok[0]; i++) if (strcasecmp(p, tok[i].name) == 0) bit = tok[i].bit; if (!bit) return 0; if (sign) f |= bit; else f &= ~bit; } if (!f) return 0; ann_fields = f; return 1;}static struct ann_ent *ann_cache = NULL;static int ann_n = 0, ann_cap = 0, ann_sorted = 0; /* [0,ann_sorted) is sorted */static int ann_cmp(const void *a, const void *b){ return strcmp(((const struct ann_ent *)a)->ip, ((const struct ann_ent *)b)->ip);}static int ann_find(const char *ip) /* index or -1 */{ struct ann_ent key, *e; int i; memset(&key, 0, sizeof key); snprintf(key.ip, sizeof key.ip, "%s", ip); if (ann_sorted && (e = bsearch(&key, ann_cache, (size_t)ann_sorted, sizeof *ann_cache, ann_cmp))) return (int)(e - ann_cache); for (i = ann_sorted; i < ann_n; i++) /* unsorted tail */ if (strcmp(ann_cache[i].ip, ip) == 0) return i; return -1;}static int ann_add(const char *ip) /* index or -1 */{ if (ann_n >= ann_cap) { int nc = ann_cap ? ann_cap * 2 : 512; struct ann_ent *p = realloc(ann_cache, (size_t)nc * sizeof *p); if (!p) return -1; ann_cache = p; ann_cap = nc; } memset(&ann_cache[ann_n], 0, sizeof ann_cache[ann_n]); snprintf(ann_cache[ann_n].ip, sizeof ann_cache[ann_n].ip, "%s", ip); return ann_n++;}static void ann_sort(void){ if (ann_n > 1) qsort(ann_cache, (size_t)ann_n, sizeof *ann_cache, ann_cmp); ann_sorted = ann_n;}/* canonical text of an address literal in tok[0..len): 4, 6, or 0 if none */static int ann_canon(const char *tok, size_t len, char *out, size_t outsz){ char buf[INET6_ADDRSTRLEN + 8]; struct in_addr a4; struct in6_addr a6; if (!len || len >= sizeof buf) return 0; memcpy(buf, tok, len); buf[len] = '\0'; if (strchr(buf, ':')) { if (inet_pton(AF_INET6, buf, &a6) == 1 && inet_ntop(AF_INET6, &a6, out, (socklen_t)outsz)) return 6; return 0; } if (inet_pton(AF_INET, buf, &a4) == 1 && inet_ntop(AF_INET, &a4, out, (socklen_t)outsz)) return 4; return 0;}static int ann_ischar(char c){ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == '.' || c == ':';}/* move the insertion point past an attached "/len" (CIDR) or "]" [+ ":port"] * so the annotation never lands inside a prefix or a bracketed address */static size_t ann_extend(const char *s, size_t n, size_t ins){ size_t k; if (ins < n && s[ins] == '/') { for (k = ins + 1; k < n && s[k] >= '0' && s[k] <= '9'; k++) ; if (k > ins + 1) ins = k; } else if (ins < n && s[ins] == ']') { ins++; if (ins < n && s[ins] == ':') { for (k = ins + 1; k < n && s[k] >= '0' && s[k] <= '9'; k++) ; if (k > ins + 1) ins = k; } } return ins;}/* Next address literal in s at/after *pos. Returns the family and sets * *ins (where the annotation goes: right after the address, or after an * attached :port / /len / ]) and canon; advances *pos past the token. */static int ann_next(const char *s, size_t *pos, size_t *ins, char *canon, size_t csz){ size_t i = *pos, n = strlen(s); while (i < n) { size_t b, e, t, k; int fam, dig = 0, sep = 0; while (i < n && !ann_ischar(s[i])) i++; if (i >= n) break; b = i; while (i < n && ann_ischar(s[i])) i++; e = i; for (t = b; t < e; t++) { if (s[t] >= '0' && s[t] <= '9') dig = 1; if (s[t] == '.' || s[t] == ':') sep = 1; } if (!dig || !sep) continue; if ((fam = ann_canon(s + b, e - b, canon, csz))) { *ins = ann_extend(s, n, e); *pos = *ins > e ? *ins : e; return fam; } for (t = e; t > b && t + 2 > e && (s[t-1] == '.' || s[t-1] == ':'); t--) /* trailing punctuation */ if ((fam = ann_canon(s + b, t - 1 - b, canon, csz))) { *ins = t - 1; *pos = e; return fam; } k = e; /* v4 with :port */ while (k > b && s[k-1] != ':') k--; if (k > b && ann_canon(s + b, k - 1 - b, canon, csz) == 4) { *ins = ann_extend(s, n, e); *pos = *ins > e ? *ins : e; return 4; } { /* BSD/macOS netstat "a.b.c.d.port" */ int dots = 0; size_t p4 = b; for (t = b; t < e; t++) if (s[t] == '.') { if (++dots == 4) p4 = t; } if (dots == 4 && p4 > b && ann_canon(s + b, p4 - b, canon, csz) == 4) { *ins = ann_extend(s, n, e); *pos = *ins > e ? *ins : e; return 4; } } } *pos = n; return 0;}static int ann_prefix(const char *line, const char *pfx, const char **val){ size_t l = strlen(pfx); if (strncasecmp(line, pfx, l) != 0) return 0; line += l; while (*line == ' ' || *line == '\t') line++; *val = line; return 1;}/* one pWhoIs bulk request for the cache entries whose indices are in pend[] *//* copy a reply value, treating pWhoIs's literal "NULL" as absent */static void ann_val(char *dst, size_t sz, const char *v){ size_t n = strcspn(v, "\r"); if (n == 4 && strncasecmp(v, "NULL", 4) == 0) { dst[0] = '\0'; return; } snprintf(dst, sz, "%.*s", (int)n, v);}static void ann_resolve(whois_session_params *wsess, const int *pend, int np){ size_t qlen = 96 + (size_t)np * (INET6_ADDRSTRLEN + 1); char *q, *reply, *serv; token_t *ls; int i, cur = -1; if (np <= 0) return; serv = (strlen(wsess->pw_serv) > 0) ? wsess->pw_serv : pwhois_server; if (!(q = malloc(qlen))) return; snprintf(q, qlen, "begin\napp=\"%s %s ANNOTATE\"\n", appname, version); for (i = 0; i < np; i++) { strcat(q, ann_cache[pend[i]].ip); strcat(q, "\n"); } strcat(q, "end\n"); reply = w_ask(wsess, serv, q, NULL); free(q); if (!reply) return; if (strncasecmp(reply, "Error:", 6) == 0) { /* e.g. daily query limit exceeded */ static int shown = 0; const char *m = reply; while (strncasecmp(m, "Error:", 6) == 0) { m += 6; while (*m == ' ') m++; } /* server doubles the prefix */ if (!shown) { fflush(stdout); fprintf(stderr, "%s: pWhoIs: %.*s\n", appname, (int)strcspn(m, "\r\n"), m); shown = 1; } free(reply); return; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { const char *v; char canon[INET6_ADDRSTRLEN]; if (ann_prefix(ls[i].ptr, "IP:", &v)) { int k; cur = -1; if (!ann_canon(v, strcspn(v, " \t\r"), canon, sizeof canon)) continue; for (k = 0; k < np; k++) if (strcmp(ann_cache[pend[k]].ip, canon) == 0) { cur = pend[k]; ann_cache[cur].have = 1; break; } } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Origin-AS:", &v)) { ann_cache[cur].asn = atoi(v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Org-Name:", &v)) { ann_val(ann_cache[cur].org, sizeof ann_cache[cur].org, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Net-Name:", &v)) { ann_val(ann_cache[cur].net, sizeof ann_cache[cur].net, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "AS-Org-Name:", &v)) { ann_val(ann_cache[cur].asorg, sizeof ann_cache[cur].asorg, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Prefix:", &v)) { ann_val(ann_cache[cur].prefix, sizeof ann_cache[cur].prefix, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "City:", &v)) { ann_val(ann_cache[cur].city, sizeof ann_cache[cur].city, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Region:", &v)) { ann_val(ann_cache[cur].region, sizeof ann_cache[cur].region, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Country-Code:", &v)) { ann_val(ann_cache[cur].cc, sizeof ann_cache[cur].cc, v); } else if (cur >= 0 && ann_prefix(ls[i].ptr, "Country:", &v)) { ann_val(ann_cache[cur].country, sizeof ann_cache[cur].country, v); } } free(ls); free(reply);}/* one selected field, coloured on a terminal; returns 1 if something was printed */static int ann_emit_field(const char *v, int kind, int first){ if (!v || !v[0]) return 0; if (!first) putchar(' '); if (w_use_color) printf("%s%s%s", w_field_seq(kind), v, WC_RESET); else fputs(v, stdout); return 1;}/* emit one input line with an annotation after each address */static void ann_emit_line(const char *s){ size_t pos = 0, ins, last = 0; char canon[INET6_ADDRSTRLEN]; while (ann_next(s, &pos, &ins, canon, sizeof canon)) { int idx = ann_find(canon), n = 0; const struct ann_ent *e = (idx >= 0) ? &ann_cache[idx] : NULL; fwrite(s + last, 1, ins - last, stdout); if (w_use_color) printf(" %s[%s", WC_DIM, WC_RESET); else fputs(" [", stdout); if (e && e->have) { char asn[16] = "", place[200] = ""; if (e->asn) snprintf(asn, sizeof asn, "AS%d", e->asn); if (ann_fields & AF_PLACE) { size_t l = 0; if (e->city[0]) { snprintf(place + l, sizeof place - l, "%s", e->city); l = strlen(place); } if (e->region[0] && strcmp(e->region, e->city) != 0) { snprintf(place + l, sizeof place - l, "%s%s", l ? ", " : "", e->region); l = strlen(place); } if (e->country[0]) { snprintf(place + l, sizeof place - l, "%s%s", l ? ", " : "", e->country); l = strlen(place); } if (e->cc[0]) snprintf(place + l, sizeof place - l, "%s(%s)", l ? " " : "", e->cc); } if (ann_fields & AF_ASN) n += ann_emit_field(asn, WF_ASN, n == 0); if (ann_fields & AF_ORG) n += ann_emit_field(e->org, WF_ORG, n == 0); if (ann_fields & AF_ASORG) n += ann_emit_field(e->asorg, WF_ORG, n == 0); if (ann_fields & AF_NET) n += ann_emit_field(e->net, WF_LABEL, n == 0); if (ann_fields & AF_PREFIX) n += ann_emit_field(e->prefix, WF_PREFIX, n == 0); if (ann_fields & AF_CITY) n += ann_emit_field(e->city, WF_GEO, n == 0); if (ann_fields & AF_REGION) n += ann_emit_field(e->region, WF_GEO, n == 0); if (ann_fields & AF_COUNTRY) n += ann_emit_field(e->country, WF_GEO, n == 0); if (ann_fields & AF_CC) n += ann_emit_field(e->cc, WF_GEO, n == 0); if (ann_fields & AF_PLACE) n += ann_emit_field(place, WF_GEO, n == 0); } if (!n) { if (w_use_color) printf("%s?%s", WC_DIM, WC_RESET); else putchar('?'); } if (w_use_color) printf("%s]%s", WC_DIM, WC_RESET); else putchar(']'); last = ins; } fputs(s + last, stdout);}/* chunked driver: collect -> resolve (batched) -> emit, repeat to EOF */static void w_annotate_stream(whois_session_params *wsess, FILE *in){ char **lines = malloc(sizeof(char *) * ANN_CHUNK); char buf[8192]; int nl, i; if (!lines) return; for (;;) { int pend[ANN_BATCH], np = 0; for (nl = 0; nl < ANN_CHUNK && fgets(buf, sizeof buf, in); ) { if ((lines[nl] = strdup(buf))) nl++; } if (nl == 0) break; for (i = 0; i < nl; i++) { /* pass 1: new addresses */ size_t pos = 0, ins; char canon[INET6_ADDRSTRLEN]; while (ann_next(lines[i], &pos, &ins, canon, sizeof canon)) { int idx; if (ann_find(canon) >= 0) continue; if ((idx = ann_add(canon)) < 0) continue; pend[np++] = idx; if (np == ANN_BATCH) { ann_resolve(wsess, pend, np); np = 0; ann_sort(); } } } if (np) ann_resolve(wsess, pend, np); ann_sort(); for (i = 0; i < nl; i++) { ann_emit_line(lines[i]); free(lines[i]); } /* pass 2 */ fflush(stdout); } free(lines);}static int w_display_pwhois_gigo(whois_session_params * wsess, char *user_query){ char *reply; char *serv; if (strlen(wsess->pw_serv) > 0) serv = wsess->pw_serv; else serv = pwhois_server; if (wsess->w_noisy) { if (w_ecolor()) fprintf(stderr, "Querying '%s%s%s' for: '%s%s%s'\n", w_field_seq(WF_LABEL), serv, WC_RESET, w_field_seq(WF_ASN), user_query, WC_RESET); else fprintf(stderr, "Querying '%s' for: '%s'\n", serv, user_query); } reply = w_ask(wsess, serv, user_query, NULL); if (!reply) { if (wsess->w_noisy) fprintf(stderr,"No reply from %s.\n",serv); return -1; } w_print_pwhois_colorized(reply); free(reply); return 0;}#endifintw_lookup_all_pwhois_bulk(whois_session_params * wsess, struct ip_list_array *iplist){ token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = NULL; int i = 0, k = 0, entity_id = 0; unsigned int j = 0; char *value = NULL; if (!iplist) return -1; if ((uintptr_t)(*iplist).application) { bulk_ip_query = (char *)malloc(((strlen((*iplist).application) * sizeof(char)) +10) + ((strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); } else bulk_ip_query = (char *)malloc(((strlen(appname) * sizeof(char)) +10) + ((strlen(version) + strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); *bulk_ip_query = '\0'; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); if ((uintptr_t)(*iplist).application) { strcat(bulk_ip_query, "app=\""); strcat(bulk_ip_query, (*iplist).application); strcat(bulk_ip_query, "\"\n"); } else { strcat(bulk_ip_query, "app=\""); strcat(bulk_ip_query, appname); strcat(bulk_ip_query, " "); strcat(bulk_ip_query, version); strcat(bulk_ip_query, "\"\n"); } for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query, inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query, "\n"); } strcat(bulk_ip_query, bulk_end); reply = w_ask(wsess, pwhois_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; free(bulk_ip_query); return -1; } responses = tokens(reply, "\n"); for(i = 0; responses[i].ptr; i++){ value = NULL; // printf("LINE %d: '%s'\n",i, responses[i].ptr); if((value = match_prefix("IP:", responses[i].ptr)) != NULL){ /* if any keys matched, increment the id of the array */ if(k > 0){ entity_id++; k = 0; } } else if((value = match_prefix("origin-as:", responses[i].ptr)) != NULL) { rm_spaces(value); /* strip out any spaces from the ASN */ for(j = 0; j < strlen(value); j++) { if(!isdigit((unsigned char)value[j])){ if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); } break; } } if ((int)strtol(value, (char **)NULL, 10)) { (*iplist).asn[(entity_id)] = strtol(value, (char **)NULL, 10); k++; } else { (*iplist).asn[(entity_id)] = 0; k++; } } else if ((value = match_prefix("org-name:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,sizeof((*iplist).orgName[entity_id])-1); (*iplist).orgName[entity_id][sizeof((*iplist).orgName[entity_id])-1] = '\0'; k++; } else if ((value = match_prefix("net-name:", responses[i].ptr))) { strncpy((*iplist).netName[entity_id],value,sizeof((*iplist).netName[entity_id])-1); (*iplist).netName[entity_id][sizeof((*iplist).netName[entity_id])-1] = '\0'; k++; } if(value) free(value); if ((entity_id+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0;}intw_lookup_all_pwhois_bulk_ext(whois_session_params * wsess, struct ext_ip_list_array *iplist){ token_t *responses; char *reply; const char *bulk_begin = "begin\n"; const char *bulk_end = "end\n"; char *bulk_ip_query = NULL; int i=0; unsigned int j=0; int k=0; int pntcnt; int entity_id = 0; char *value = NULL; if (!iplist) return -1; iplist->geoavailable=0; if ((uintptr_t)(*iplist).application) { bulk_ip_query = malloc(((strlen((*iplist).application) * sizeof(char)) +10) + ((strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); } else bulk_ip_query = malloc(((strlen(appname) * sizeof(char)) +10) + ((strlen(version) + strlen(bulk_begin) + strlen(bulk_end) + 1) * sizeof(char)) + (16 * (*iplist).numItems)); bulk_ip_query[0]=0; /* clean up the response data set in case the caller doesn't (and we return error) */ for (i = 0; i < (*iplist).numItems; i++) { (*iplist).asn[(i)] = 0; memset((*iplist).netName[i],0,sizeof((*iplist).netName[i])); memset((*iplist).orgName[i],0,sizeof((*iplist).orgName[i])); } /* prepare the text-string-based query */ strcat(bulk_ip_query,bulk_begin); if ((uintptr_t)(*iplist).application) { strcat(bulk_ip_query,"app=\""); strcat(bulk_ip_query,(*iplist).application); strcat(bulk_ip_query,"\"\n"); } else { strcat(bulk_ip_query,"app=\""); strcat(bulk_ip_query,appname); strcat(bulk_ip_query," "); strcat(bulk_ip_query,version); strcat(bulk_ip_query,"\"\n"); } for (i = 0; i < ((*iplist).numItems); i++) { strcat(bulk_ip_query,inet_ntoa((*iplist).ipaddr[i])); strcat(bulk_ip_query,"\n"); } strcat(bulk_ip_query,bulk_end); reply = w_ask(wsess, pwhois_server, bulk_ip_query, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",pwhois_server); else fprintf(stderr,"No reply from %s.\n",pwhois_server); } /* clean up the response data set in case the caller doesn't */ for (i = 0; i < (*iplist).numItems; i++) (*iplist).asn[(i)] = 0; free(bulk_ip_query); return -1; } responses = tokens(reply, "\n"); for (i = 0; responses[i].ptr; i++) { value = NULL; // printf("LINE %d: '%s'\n",i, responses[i].ptr); if ((value = match_prefix("IP:", responses[i].ptr)) != NULL) { /* if any keys matched, increment the id of the array */ if (k > 0) { entity_id++; k = 0; } } else if ((value = match_prefix("origin-as:", responses[i].ptr)) != NULL) { rm_spaces(value); /* strip out any spaces from the ASN */ for (j = 0; j < strlen(value); j++) { if (!isdigit((unsigned char)value[j])) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); else fprintf(stderr,"Parse error at \'%c\': non-numeric value at position %d of %s).\n",value[j],j,value); } break; } } if(atoi(value)) { (*iplist).asn[(entity_id)] = atoi(value); k++; } else { (*iplist).asn[(entity_id)] = 0; k++; } } else if ((value = match_prefix("as-org-name-source:", responses[i].ptr))) { strncpy((*iplist).asOrgNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("org-name-source:", responses[i].ptr))) { strncpy((*iplist).orgNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("net-name-source:", responses[i].ptr))) { strncpy((*iplist).netNameSource[entity_id],value,20); k++; } else if ((value = match_prefix("prefix:", responses[i].ptr))) { strncpy((*iplist).prefix[entity_id],value,sizeof((*iplist).prefix[entity_id])-1); (*iplist).prefix[entity_id][sizeof((*iplist).prefix[entity_id])-1] = '\0'; k++; } else if ((value = match_prefix("org-name:", responses[i].ptr))) { strncpy((*iplist).orgName[entity_id],value,sizeof((*iplist).orgName[entity_id])-1); (*iplist).orgName[entity_id][sizeof((*iplist).orgName[entity_id])-1] = '\0'; k++; } else if ((value = match_prefix("net-name:", responses[i].ptr))) { strncpy((*iplist).netName[entity_id],value,sizeof((*iplist).netName[entity_id])-1); (*iplist).netName[entity_id][sizeof((*iplist).netName[entity_id])-1] = '\0'; k++; } else if ((value = match_prefix("longitude:", responses[i].ptr))) { rm_spaces(value); /* strip out any spaces from the LONGITUDE */ for (j = 0, pntcnt = 0; j < strlen(value); j++) { if(value[j]=='.') pntcnt++; if (!isdigit((unsigned char)value[j]) && (value[j]!='.' || pntcnt>1) && !(j==0 && value[j]=='-')) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[j],j,value); else fprintf(stderr,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[j],j,value); } break; } } (*iplist).longitude[(entity_id)]=atof(value); iplist->geoavailable++; k++; } else if ((value = match_prefix("latitude:", responses[i].ptr))) { rm_spaces(value); /* strip out any spaces from the LONGITUDE */ for (j = 0, pntcnt = 0; j < strlen(value); j++) { if(value[j]=='.') pntcnt++; if (!isdigit((unsigned char)value[j]) && (value[j]!='.' || pntcnt>1) && !(j==0 && value[j]=='-')) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[j],j,value); else fprintf(stderr,"Parse error at \'%c\': can't parse value at position %d of %s).\n",value[j],j,value); } break; } } (*iplist).latitude[(entity_id)]=atof(value); iplist->geoavailable++; k++; } else if ((value = match_prefix("city:", responses[i].ptr))) { strncpy((*iplist).city[entity_id],value,sizeof((*iplist).city[entity_id])-1); (*iplist).city[entity_id][sizeof((*iplist).city[entity_id])-1] = '\0'; iplist->geoavailable++; k++; } else if ((value = match_prefix("country-code:", responses[i].ptr))) { strncpy((*iplist).country_code[entity_id],value,sizeof((*iplist).country_code[entity_id])-1); (*iplist).country_code[entity_id][sizeof((*iplist).country_code[entity_id])-1] = '\0'; k++; } else if ((value = match_prefix("country:", responses[i].ptr))) { strncpy((*iplist).country[entity_id],value,sizeof((*iplist).country[entity_id])-1); (*iplist).country[entity_id][sizeof((*iplist).country[entity_id])-1] = '\0'; iplist->geoavailable++; k++; } else if ((value = match_prefix("region:", responses[i].ptr))) { strncpy((*iplist).state[entity_id],value,sizeof((*iplist).state[entity_id])-1); (*iplist).state[entity_id][sizeof((*iplist).state[entity_id])-1] = '\0'; iplist->geoavailable++; k++; } if(value) free(value); if ((entity_id+1) > (*iplist).numItems) break; } free(responses); free(reply); free(bulk_ip_query); return 0;}static char *w_lookup_netname_other(whois_session_params * wsess, char *addr, char *serv){ token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *reply, *ans = NULL; int i; int use_this = 1; if (!addr || !serv) return NULL; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value=NULL; if ((value = match_prefix("inetnum:", ls[i].ptr))) { a = match_ipprefix(value); if (b) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; b = a; a = NULL; } free(value); } else if (use_this && (value = match_prefix("netname:", ls[i].ptr))) { if (ans) free(ans); ans = value; } } free(ls); free(reply); if (b) free(b); return ans;}char *w_lookup_netname(whois_session_params * wsess, char *addr){ token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *na = NULL, *nb = NULL; char *reply, *ans = NULL; int i; int have_new, have_old; if (!addr) return NULL; reply = w_ask(wsess, arin_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); ans = NULL; for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_inparens(ls[i].ptr))) { char *after = match_afterparens(ls[i].ptr); if (after) { na = value; a = match_iprange(after); } else { na = value; if (ls[i+1].ptr && (a = match_iprange(ls[i+1].ptr))) { /* successful match */ } else { /* Bad format */ free(na); na = NULL; continue; } } } have_new = (na && a); have_old = (nb && b); if (have_new) { if (have_old) { if (((b->end - b->start) > (a->end - a->start))) { /* keep new, discard old */ free(nb); free(b); nb = na; na = NULL; b = a; a = NULL; } else { /* keep old, discard new */ free(na); free(a); na = NULL; a = NULL; } } else { /* nothing old, save new */ nb = na; na = NULL; b = a; a = NULL; } } } /* loop */ if (na) free(na); if (a) free(a); if (b) free(b); free(ls); free(reply); if (!nb) return NULL; /* Add "!" to the beginning of the question */ na = malloc(strlen(nb) + 2); strcpy(&na[1], nb); na[0] = '!'; free(nb); reply = w_ask(wsess, arin_server, na, NULL); free(na); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); } for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } free(ls); free(reply); { char *other = NULL; if (ans && strstr(ans, "RIPE")) { other = w_lookup_netname_other(wsess, addr, ripe_server); } if (ans && !strncmp(ans, "APNIC", 5)) { other = w_lookup_netname_other(wsess, addr, apnic_server); } if (other) { char *together = malloc(strlen(ans) + strlen(other) + 2); together[0]=0; strcpy(together, ans); strcat(together, "/"); strcat(together, other); free(ans); ans = together; } } return ans;}static char *w_lookup_orgname_other(whois_session_params * wsess, char *addr, char *serv){ token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *reply, *ans = NULL; int i; int use_this = 1; if (!addr || !serv) return NULL; reply = w_ask(wsess, serv, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",serv); else fprintf(stderr,"No reply from %s.\n",serv); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value=NULL; if ((value = match_prefix("inetnum:", ls[i].ptr))) { a = match_ipprefix(value); if (b) { if (((b->end - b->start) > (a->end - a->start))) { use_this = 1; free(b); b = a; a = NULL; } else { use_this = 0; free(a); a = NULL; } } else { use_this = 1; b = a; a = NULL; } free(value); }else if (use_this && (value = match_prefix("orgname:", ls[i].ptr))) { if (ans) free(ans); ans = value; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if (use_this && (value = match_prefix("descr:", ls[i].ptr))) { if (ans) free(ans); ans = value; break; } } } free(ls); free(reply); if (b) free(b); return ans;}char *w_lookup_orgname(whois_session_params * wsess, char *addr){ token_t *ls; ip_blk_t *a = NULL, *b = NULL; char *na = NULL, *nb = NULL; char *reply, *ans = NULL; int i; int have_new, have_old; if (!addr) return NULL; reply = w_ask(wsess, arin_server, addr, NULL); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("netname:", ls[i].ptr))) { ans = value; break; } } if (!ans) { for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_inparens(ls[i].ptr))) { char *after = match_afterparens(ls[i].ptr); if (after) { na = value; a = match_iprange(after); } else { na = value; if (ls[i+1].ptr && (a = match_iprange(ls[i+1].ptr))) { /* successful match */ } else { /* Bad format */ free(na); na = NULL; continue; } } } have_new = (na && a); have_old = (nb && b); if (have_new) { if (have_old) { if (((b->end - b->start) > (a->end - a->start))) { /* keep new, discard old */ free(nb); free(b); nb = na; na = NULL; b = a; a = NULL; } else { /* keep old, discard new */ free(na); free(a); na = NULL; a = NULL; } } else { /* nothing old, save new */ nb = na; na = NULL; b = a; a = NULL; } } } /* loop */ if (na) free(na); if (a) free(a); if (b) free(b); free(ls); free(reply); if (!nb) return NULL; /* Add "!" to the beginning of the question */ na = malloc(strlen(nb) + 2); strcpy(&na[1], nb); na[0] = '!'; free(nb); reply = w_ask(wsess, arin_server, na, NULL); free(na); if (!reply) { if (wsess->w_noisy) { if(wsess->logprintfCookie) lft_printf(wsess->logprintfCookie,"No reply from %s.\n",arin_server); else fprintf(stderr,"No reply from %s.\n",arin_server); } return NULL; } ls = tokens(reply, "\n"); } for (i = 0; ls[i].ptr; i++) { char *value; if ((value = match_prefix("orgname:", ls[i].ptr))) { if(ans) free(ans); ans = value; break; } } free(ls); free(reply); { char *other = NULL; if (ans && strstr(ans, "RIPE")) { other = w_lookup_orgname_other(wsess, addr, ripe_server); } if (ans && (!strncmp(ans, "APNIC", 5) || strstr(ans, "Asia Pacific Net") )) { other = w_lookup_orgname_other(wsess, addr, apnic_server); } if (other) { /* char *together = malloc(strlen(ans) + strlen(other) + 4); strcpy(together, other); strcat(together, " ("); strcat(together, ans); strcat(together, ")"); */ free(ans); ans = other; } } return ans;}#ifdef STANDALONE/*---------------------------------------------------------------------------*/void lft_printf(lft_session_params * sess, const char *templ, ...){ va_list ap; char buf[1024]; (void)sess; va_start (ap, templ); vsprintf(buf, templ, ap); va_end (ap); printf("%s",buf);}/*---------------------------------------------------------------------------*/static voidusage (char *prog){ fprintf (stderr, "\nWhoB - version %s %s\n\n" " - a likable whois client from the Prefix WhoIs project\n" " visit http://www.pwhois.org\n" "\nUsage: %s [-g] [<options>] <target>\n" "\nMainstream Options:\n" " -g Disable GIGO mode; enable other options then submit query\n" " -R Display the Origin-AS on file at the RADB/IRR also\n" " -p Display the AS-Path learned by the pWhoIs server (pWhoIs-only)\n" " -n Display the network name on file at the registrar\n" " -t Display the date the route was last updated (pWhoIs-only)\n" " -u Display the date the route was last updated in GMT (pWhoIs-only)\n" " -o Disable display of the organization\'s name on file at the registrar\n" "\nAdvanced Options:\n" " -h host Specify your own pWhoIs-compatible server to query\n" " -f file Read from 'file' (or '-' as stdin) as bulk input to pWhoIs\n" " -c Use Cymru\'s whois server instead of pWhoIs\n" " -r Use RIPE NCC\'s RIS whois server instead of pWhoIs\n" "\nPrefix WhoIs Advanced Options:\n" " -A Display all routes transiting the target ASN (pWhoIs-only)\n" " -a Display all routes announced by the target ASN (pWhoIs-only)\n" " -P Display all routes respective to a target prefix (pWhoIs-only)\n" " -N Display all networks registered to the target ASN (pWhoIs-only)\n" " -O Display organizational contact info for the target ASN (pWhoIs-only)\n" "\nVerbosity Options and Status:\n" " -s Display the version and status of the pWhoIs server (pWhoIs-only)\n" " -V Display verbose/debug output. Use more \'V\'s for additional detail\n" " -v Display WhoB\'s version information and exit\n" " --color Force colorized output even when not writing to a terminal\n" " --no-color Disable colorized output (default: auto-detect the terminal)\n" " -e, --enrich Annotate IP addresses in text from stdin with pWhoIs ASN and org\n" " --annotate[=FILE] Same as -e; =FILE reads a file instead of stdin\n" " -F, --fields=LIST Fields for -e: asn,org,asorg,net,prefix,city,region,country,cc,place,all\n" " (default asn,org; a leading + or - adjusts the default)\n" " me|whoami Display the public IP address of this host\n" "\n" ,version,version_date,prog); fprintf(stderr,"Example: %s -gnp 1.2.3.4\n",prog); fprintf(stderr,"Returns: IP Address | ASN-by-prefix (prefix) | AS-Path | NetName | OrgName\n\n"); exit(EXIT_FAILURE);}static voidshow_startup_msgs (whois_session_params * wsess){ if (wsess->w_noisy) { fprintf(stderr,"WhoB version %s %s firing up...",version,version_date); if (wsess->w_noisy > 1) fprintf(stderr," (verbosity level %d)\n",wsess->w_noisy); else printf ("\n"); if (wsess->w_noisy > 1) { const char *L = "", *R = ""; if (w_ecolor()) { L = w_field_seq(WF_LABEL); R = WC_RESET; } fprintf(stderr,"Data sources:"); if ((strlen(wsess->pw_serv) > 0) && (!use_cymru || read_from_file)) fprintf(stderr," %s%s%s (pWhoIs)",L,wsess->pw_serv,R); else if ((!use_cymru || read_from_file) && !use_riswhois) fprintf(stderr," %s%s%s (pWhoIs)",L, pwhois_server,R); else if (!use_cymru) fprintf(stderr," %s%s%s (RIPE NCC)",L, ripe_ris_server,R); if (use_cymru && !read_from_file) fprintf(stderr," %s%s%s (Cymru)",L,cymru_server,R); if (display_radb_as) fprintf(stderr,", %s%s%s (RADB)",L,radb_server,R); fprintf(stderr,".\n"); if (read_from_file) { if (cymrufromfile) fprintf(stderr,"Using Cymru for bulk file resolution.\n"); else if (riswhoisfromfile) fprintf(stderr,"Using RIPE NCC for bulk file resolution.\n"); else fprintf(stderr,"Using Prefix WhoIs for bulk file resolution.\n"); } } if (show_routes_byprefix == 1 && (show_routes_byasn == 1 || show_routes_bytransitasn == 1)) { fprintf(stderr,"You may only perform routeviews one at a time. Using by-ASN.\n"); show_routes_byprefix = 0; } }}int main(int ac, char **av){ struct addrinfo ai_hints, *ai_res; char *addr = NULL; char addrbuf[INET6_ADDRSTRLEN]; struct in_addr in, pws; int ch; int user_asn = 0; char user_asn_buf[12]; /* fits full 32-bit ASN (10 digits) + NUL */ whois_session_params * wsess; memset(&hostname, 0, sizeof(hostname)); wsess = w_init(); setbuf(stdout, NULL); { static struct option w_long_options[] = { { "color", no_argument, 0, OPT_W_COLOR }, { "no-color", no_argument, 0, OPT_W_NO_COLOR }, { "no-colour",no_argument, 0, OPT_W_NO_COLOR }, { "annotate", optional_argument, 0, OPT_W_ANNOTATE }, { "enrich", optional_argument, 0, OPT_W_ANNOTATE }, { "fields", required_argument, 0, OPT_W_FIELDS }, { 0, 0, 0, 0 } }; int w_optidx = 0; while ((ch = getopt_long (ac, av, "AaCcefgNnOopPRrstuVvh:w:F:", w_long_options, &w_optidx)) != EOF) switch (ch) { case OPT_W_COLOR: w_color_mode = 1; break; case OPT_W_NO_COLOR: w_color_mode = 2; break; case 'e': /* -e / --enrich / --annotate are one feature */ case OPT_W_ANNOTATE: annotate_mode = 1; use_gigo = 0; if (optarg) snprintf(annotate_file, sizeof annotate_file, "%s", optarg); break; case 'F': case OPT_W_FIELDS: if (!ann_parse_fields(optarg)) { fprintf(stderr, "%s: -F expects a comma list of asn,org,asorg,net,prefix,city,region,country,cc,place,all (+/- adjust the default)\n", appname); exit(EXIT_FAILURE); } break; case 'a': use_gigo = 0; show_routes_byasn = 1; go_interactive = 1; break; case 'A': use_gigo = 0; show_routes_bytransitasn = 1; go_interactive = 1; break; case 'N': use_gigo = 0; show_networks_byasn = 1; go_interactive = 1; break; case 'O': use_gigo = 0; show_contacts_byasn = 1; go_interactive = 1; break; case 's': use_gigo = 0; show_server_status = 1; go_interactive = 1; break; case 'P': use_gigo = 0; show_routes_byprefix = 1; go_interactive = 1; break; case 'v': usage(av[0]); break; case 'u': /* show all times in UTC */#if defined(sun) if(putenv("TZ=GMT0") == -1) { fprintf(stderr, "%s: Unable to set TZ to UTC.",appname); } #else#if !defined(WIN32) && !defined(_WIN32) if (setenv("TZ", "GMT0", 1) == -1) { fprintf(stderr, "%s: Unable to set TZ to UTC.",appname); }#endif#endif show_cache_date = 1; break; case 't': show_cache_date = 1; break; case 'w': if (strlen(optarg) > max_hostname_input) { fprintf(stderr,"Sorry, the server name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } if (inet_aton(optarg, &pws)) { strncpy(wsess->pw_serv, optarg, strlen(optarg)); } else { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(optarg, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\' to use as your pWhoIs server.\n", optarg); exit(EXIT_FAILURE); } memcpy(&pws, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(pws)); freeaddrinfo(ai_res); strncpy(wsess->pw_serv,inet_ntoa(pws),strlen(inet_ntoa(pws))); } break; case 'h': if (strlen(optarg) > max_hostname_input) { fprintf(stderr,"Sorry, the server name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } if (inet_aton(optarg, &pws)) { strncpy(wsess->pw_serv,optarg,strlen(optarg)); } else { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_INET; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(optarg, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\' to use as your pWhoIs server.\n", optarg); exit(EXIT_FAILURE); } memcpy(&pws, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, sizeof(pws)); freeaddrinfo(ai_res); strncpy(wsess->pw_serv,inet_ntoa(pws),strlen(inet_ntoa(pws))); } break; case 'c': use_cymru = 1; /* cymrufromfile = 1; */ /* Use pwhois Cymru compatibility mode by default */ break; case 'C': use_cymru = 1; cymrufromfile = 1; break; case 'n': display_netname = 1; go_interactive = 1; break; case 'r': use_riswhois = 1; riswhoisfromfile = 1; break; case 'R': display_radb_as = 1; go_interactive = 1; break; case 'o': display_orgname = 0; go_interactive = 1; break; case 'p': display_aspath = 1; go_interactive = 1; break; case 'f': use_gigo = 0; read_from_file = 1; break; case 'V': wsess->w_noisy++; break; case 'g': use_gigo = 0; go_interactive = 1; break; default: usage (av[0]); } } /* end w_long_options scope */ /* Resolve stdout colourization exactly once, now that options are parsed * and before any output. Self-gates to an interactive terminal. */ w_detect_color(); /* --annotate: a pure filter — enrich addresses in the text and exit. */ if (annotate_mode) { FILE *in = stdin; if (annotate_file[0] && strcmp(annotate_file, "-") != 0) { in = fopen(annotate_file, "r"); if (!in) { fprintf(stderr, "%s: unable to open '%s' for reading.\n", appname, annotate_file); exit(EXIT_FAILURE); } } w_annotate_stream(wsess, in); if (in != stdin) fclose(in); exit(EXIT_SUCCESS); } /* Catch hostname input without any arguments */ if ((ac - optind) > 0) go_interactive = 1; if (go_interactive > 0) { /* Quickly check stdin for input to avoid David having to type */ /* '-f -' to pipe into to whob, even if it has args :-) */ fd_set rfds; struct timeval timev; int selretval; /* Watch stdin (fd 0) briefly for input. */ FD_ZERO(&rfds); FD_SET(0, &rfds); timev.tv_sec = 0; timev.tv_usec = 100; selretval = select(1, &rfds, NULL, NULL, &timev); if (selretval > 0) { /* select() also reports fd 0 readable at EOF / on a closed or * redirected-from-empty stdin (e.g. `whob target </dev/null`, or * any script/cron run with stdin redirected). Peek one byte to * tell real piped data from EOF: only switch to stdin-bulk mode * when data is actually present, otherwise fall through and honor * the argv target. The peek is on the same stdin FILE* the bulk * reader consumes, and ungetc() restores the byte, so nothing is * lost. Preserves `echo x | whob [target]` and `-f -`. */ int peek = getc(stdin); if (peek != EOF) { ungetc(peek, stdin); /* There's input in them there hills, treat it as the input file */ use_gigo = 0; read_from_file = 1; use_stdin = 1; } } /* else if (wsess->w_noisy) fprintf(stderr,"No input found on stdin.\n"); */ } else if (read_from_file < 1) { /* No indication we should be interactive based on arguments, so wait for STDIN */ use_gigo = 0; read_from_file = 1; use_stdin = 1; } if (((ac - optind) < 1) && (show_server_status != 1) && (go_interactive > 0)) usage (av[0]); /* Show the verbose startup information if verbosity is enabled/requested */ show_startup_msgs(wsess); if (show_server_status == 1) { w_display_pwhois_version(wsess); exit(EXIT_FAILURE); } else if ((read_from_file > 0 && use_stdin < 1) || go_interactive > 0) { if (strlen(av[optind]) > max_hostname_input) { fprintf(stderr,"Sorry, the subject name you supplied was unreasonably long.\n"); exit(EXIT_FAILURE); } else { strncpy(hostname,av[optind],strlen(av[optind])); optind++; } } if (read_from_file) { if (riswhoisfromfile) w_display_bulkfromfile_riswhois(wsess, hostname); else if (cymrufromfile) w_display_bulkfromfile_cymru(wsess, hostname); else w_display_bulkfromfile_pwhois(wsess, hostname); exit(EXIT_SUCCESS); } if (use_gigo > 0) { if (strncmp(hostname,"me",2) == 0 || strncmp(hostname,"whoami",6) == 0) { w_display_myaddress(wsess); exit(EXIT_SUCCESS); } else { w_display_pwhois_gigo(wsess, hostname); exit(EXIT_SUCCESS); } } if ((show_routes_byasn || show_routes_bytransitasn || show_contacts_byasn || show_networks_byasn) && (strlen(hostname) <= 10) && atoi(hostname) && stricontains(hostname,".") < 1) { user_asn = atoi(hostname); if (wsess->w_noisy > 1) fprintf(stderr,"Using user-supplied ASN %d for lookup.\n",user_asn); } else if (show_routes_byprefix == 1) { printf("Displaying all routes for prefix %s.\n",hostname); } else { if (strncmp(hostname,"me",2) == 0 || strncmp(hostname,"whoami",6) == 0) { w_display_myaddress(wsess); exit(EXIT_SUCCESS); } if (inet_aton(hostname, &in)) { addr = hostname; } else if (strchr(hostname, ':')) { /* IPv6 literal: pass the text form through to the lookup */ addr = hostname; } else { /* Resolve as either family; a v6 answer is used verbatim */ memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(hostname, NULL, &ai_hints, &ai_res) != 0) { fprintf(stderr,"Sorry, I cannot resolve \'%s\'\n", hostname); exit(EXIT_FAILURE); } if (ai_res->ai_family == AF_INET6) inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ai_res->ai_addr)->sin6_addr, addrbuf, sizeof(addrbuf)); else inet_ntop(AF_INET, &((struct sockaddr_in *)ai_res->ai_addr)->sin_addr, addrbuf, sizeof(addrbuf)); freeaddrinfo(ai_res); addr = addrbuf; } } if (show_routes_byasn) { if (user_asn > 0) { printf("Displaying all routes whose Origin-AS is %d.\n", user_asn); snprintf(user_asn_buf,sizeof(user_asn_buf),"%d",user_asn); w_display_rvbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all routes whose Origin-AS is %s.\n", wsess->consolidated_asn); w_display_rvbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_routes_bytransitasn) { if (user_asn > 0) { printf("Displaying all routes whose Transit-AS is %d.\n", user_asn); snprintf(user_asn_buf,sizeof(user_asn_buf),"%d",user_asn); w_display_rvbytransitasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all routes whose Transit-AS is %s.\n", wsess->consolidated_asn); w_display_rvbytransitasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_networks_byasn) { if (user_asn > 0) { printf("Displaying all networks registered to the Origin-AS %d.\n", user_asn); snprintf(user_asn_buf,sizeof(user_asn_buf),"%d",user_asn); w_display_networksbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all networks registered to the Origin-AS %s.\n", wsess->consolidated_asn); w_display_networksbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Sorry, unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_contacts_byasn) { if (user_asn > 0) { printf("Displaying all contact info on file for Origin-AS %d.\n", user_asn); snprintf(user_asn_buf,sizeof(user_asn_buf),"%d",user_asn); w_display_contactsbyasn_pwhois(wsess, user_asn_buf); } else { w_lookup_all_pwhois(wsess, addr); if (atoi(wsess->consolidated_asn)) { printf("Displaying all contact info on file for Origin-AS %s.\n", wsess->consolidated_asn); w_display_contactsbyasn_pwhois(wsess, wsess->consolidated_asn); } else { printf("Unable to resolve the ASN for %s (%s) at this time.\n",hostname,addr); } } } else if (show_routes_byprefix) { w_display_rvbyprefix_pwhois(wsess, hostname); } else { printf("%s | Searching...", addr); if (use_cymru) { printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %d ", w_lookup_as_cymru(wsess, addr)); } else if (use_riswhois) { w_lookup_all_riswhois(wsess, addr); printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %s (%s) ", wsess->consolidated_asn, wsess->consolidated_route); } else { w_lookup_all_pwhois(wsess, addr); printf("\b\b\b\b\b\b\b\b\b\b\b\b"); printf("origin-as %s (%s) ", wsess->consolidated_asn, wsess->consolidated_route); if (show_cache_date && (strlen(wsess->tbuf) > 0)) printf("| %s ", wsess->tbuf); } if (display_radb_as) printf("| radb-as %d ", w_lookup_as(wsess, addr)); if ((display_aspath) && (!use_cymru) && (!use_riswhois)) printf("| as-path %s ", wsess->consolidated_asp); if (display_netname) { if (use_cymru) { printf("| %s ", w_lookup_netname(wsess, addr)); } else if (use_riswhois) { if (!display_orgname) printf("| %s ", wsess->consolidated_netname); } else { printf("| %s ", wsess->consolidated_netname); } } if (display_orgname) { if (use_cymru) { printf("| %s ", w_lookup_orgname(wsess, addr)); } else { printf("| %s ", wsess->consolidated_orgname); } } printf("\n"); } w_close(wsess); exit(EXIT_SUCCESS);}#endif