Skip to content
IPv4 cache refreshed 26 hr ago  ◆  IPv6 cache refreshed 25 hr ago  ◆  Prefixes in table 1,107,739 v4 · 261,906 v6  ◆  Peers 91 v4 · 20 v6  ◆  whois.pwhois.org:43 answering  ◆ 
Prefix WhoIs The Prefix
WhoIs Project
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.c4,230 lines · 148,006 bytesraw
/*  *  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