X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=adns.git;a=blobdiff_plain;f=client%2Fadnsresfilter.c;h=7632ee3f59b83c34bc9f24786226678909b40cfd;hp=d7150e849519a590798a1a2afa3c5cd702b76903;hb=75884a1571b83afd1bf1ba0e9546521594280254;hpb=8990f99f1e0f9684a7ca8e4c5c0d493fffd527cd diff --git a/client/adnsresfilter.c b/client/adnsresfilter.c index d7150e8..7632ee3 100644 --- a/client/adnsresfilter.c +++ b/client/adnsresfilter.c @@ -3,12 +3,11 @@ * - filter which does resolving, not part of the library */ /* - * This file is - * Copyright (C) 1999 Ian Jackson - * - * It is part of adns, which is - * Copyright (C) 1997-1999 Ian Jackson - * Copyright (C) 1999 Tony Finch + * This file is part of adns, which is + * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1999-2000,2003,2006 Tony Finch + * Copyright (C) 1991 Massachusetts Institute of Technology + * (See the file INSTALL for full details.) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,13 +32,81 @@ #include #include -#include "adns.h" +#include +#include +#include + #include "config.h" +#include "adns.h" +#include "dlist.h" +#include "tvarith.h" +#include "client.h" + +#ifdef ADNS_REGRESS_TEST +# include "hredirect.h" +#endif + +struct outqueuenode { + struct outqueuenode *next, *back; + char *buffer, *textp; + int textlen; + struct timeval printbefore; + struct treething *addr; +}; + +static int bracket, forever, address; +static unsigned long timeout= 1000; +static adns_rrtype rrt= adns_r_ptr; +static adns_initflags initflags= 0; +static const char *config_text; + +static int outblocked, inputeof; +static struct { struct outqueuenode *head, *tail; } outqueue; +static int peroutqueuenode, outqueuelen; + +static struct sockaddr_in sa; +static adns_state ads; + +static char addrtextbuf[14]; +static int cbyte, inbyte, inbuf; +static unsigned char bytes[4]; +static struct timeval printbefore; + +struct treething { + unsigned char bytes[4]; + adns_query qu; + adns_answer *ans; +}; + +static struct treething *newthing; +static void *treeroot; + +static int nonblock(int fd, int isnonblock) { + int r; + + r= fcntl(fd,F_GETFL); + if (r==-1) return -1; + r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK); + if (r==-1) return -1; + return 0; +} + +void quitnow(int exitstatus) { + nonblock(0,0); + nonblock(1,0); + exit(exitstatus); +} static void sysfail(const char *what) NONRETURNING; static void sysfail(const char *what) { fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno)); - exit(2); + quitnow(2); +} + +static void *xmalloc(size_t sz) { + void *r; + r= malloc(sz); if (r) return r; + sysfail("malloc"); } static void outputerr(void) NONRETURNING; @@ -47,65 +114,204 @@ static void outputerr(void) { sysfail("write to stdout"); } static void usage(void) { if (printf("usage: adnsresfilter []\n" - " adnsresfilter -h|--help\n" - "options: -b|--brackets\n" - " -w|--wait\n" - " -u|--unchecked\n") + " adnsresfilter -h|--help | --version\n" + "options: -t|--timeout \n" + " -w|--wait (always wait for queries to time out or fail)\n" + " -b|--brackets (require [...] around IP addresses)\n" + " -a|--address (always include [address] in output)\n" + " -u|--unchecked (do not forward map for checking)\n" + " --config (use this instead of resolv.conf)\n" + " --debug (turn on adns resolver debugging)\n" + "Timeout is the maximum amount to delay any particular bit of output for.\n" + "Lookups will go on in the background. Default timeout = 1000 (ms).\n") == EOF) outputerr(); + if (fflush(stdout)) sysfail("flush stdout"); } static void usageerr(const char *why) NONRETURNING; static void usageerr(const char *why) { fprintf(stderr,"adnsresfilter: bad usage: %s\n",why); usage(); - exit(1); + quitnow(1); } static void adnsfail(const char *what, int e) NONRETURNING; static void adnsfail(const char *what, int e) { fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e)); - exit(2); + quitnow(2); } -static int bracket, forever; -static adns_rrtype rrt= adns_r_ptr; +static void settimeout(const char *arg) { + char *ep; + timeout= strtoul(arg,&ep,0); + if (*ep) usageerr("invalid timeout"); +} -static struct sockaddr_in sa; -static adns_state ads; +static void parseargs(const char *const *argv) { + const char *arg; + int c; -static char buf[14]; -static int c, cbyte, inbyte, inbuf; -static unsigned char bytes[4]; + while ((arg= *++argv)) { + if (arg[0] != '-') usageerr("no non-option arguments are allowed"); + if (arg[1] == '-') { + if (!strcmp(arg,"--timeout")) { + if (!(arg= *++argv)) usageerr("--timeout needs a value"); + settimeout(arg); + forever= 0; + } else if (!strcmp(arg,"--wait")) { + forever= 1; + } else if (!strcmp(arg,"--brackets")) { + bracket= 1; + } else if (!strcmp(arg,"--address")) { + address= 1; + } else if (!strcmp(arg,"--unchecked")) { + rrt= adns_r_ptr_raw; + } else if (!strcmp(arg,"--config")) { + if (!(arg= *++argv)) usageerr("--config needs a value"); + config_text= arg; + } else if (!strcmp(arg,"--debug")) { + initflags |= adns_if_debug; + } else if (!strcmp(arg,"--help")) { + usage(); quitnow(0); + } else if (!strcmp(arg,"--version")) { + VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0); + } else { + usageerr("unknown long option"); + } + } else { + while ((c= *++arg)) { + switch (c) { + case 't': + if (*++arg) settimeout(arg); + else if ((arg= *++argv)) settimeout(arg); + else usageerr("-t needs a value"); + forever= 0; + arg= "\0"; + break; + case 'w': + forever= 1; + break; + case 'b': + bracket= 1; + break; + case 'a': + address= 1; + break; + case 'u': + rrt= adns_r_ptr_raw; + break; + case 'h': + usage(); + quitnow(0); + default: + usageerr("unknown short option"); + } + } + } + } +} -struct treething { - unsigned char bytes[4]; +static void queueoutchar(int c) { + struct outqueuenode *entry; + + entry= outqueue.tail; + if (!entry || entry->addr || + entry->textlen >= peroutqueuenode - (entry->textp - entry->buffer)) { + peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 : + peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2; + entry= xmalloc(sizeof(*entry)); + entry->buffer= xmalloc(peroutqueuenode); + entry->textp= entry->buffer; + entry->textlen= 0; + entry->addr= 0; + LIST_LINK_TAIL(outqueue,entry); + outqueuelen++; + } + entry->textp[entry->textlen++]= c; +} + +static void queueoutstr(const char *str, int len) { + while (len-- > 0) queueoutchar(*str++); +} + +static void writestdout(struct outqueuenode *entry) { + int r; + + while (entry->textlen) { + r= write(1, entry->textp, entry->textlen); + if (r < 0) { + if (errno == EINTR) continue; + if (errno == EAGAIN) { outblocked= 1; break; } + sysfail("write stdout"); + } + assert(r <= entry->textlen); + entry->textp += r; + entry->textlen -= r; + } + if (!entry->textlen) { + LIST_UNLINK(outqueue,entry); + free(entry->buffer); + free(entry); + outqueuelen--; + } +} + +static void replacetextwithname(struct outqueuenode *entry) { + char *name, *newbuf; + int namelen, newlen; + + name= entry->addr->ans->rrs.str[0]; + namelen= strlen(name); + if (!address) { + free(entry->buffer); + entry->buffer= 0; + entry->textp= name; + entry->textlen= namelen; + } else { + newlen= entry->textlen + namelen + (bracket ? 0 : 2); + newbuf= xmalloc(newlen + 1); + sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp); + free(entry->buffer); + entry->buffer= entry->textp= newbuf; + entry->textlen= newlen; + } +} + +static void checkadnsqueries(void) { adns_query qu; adns_answer *ans; -}; - -static struct treething *newthing; -static void *treeroot; + void *context; + struct treething *foundthing; + int r; -static int comparer(const void *a, const void *b) { - return memcmp(a,b,4); + for (;;) { + qu= 0; context= 0; ans= 0; + r= adns_check(ads,&qu,&ans,&context); + if (r == ESRCH || r == EAGAIN) break; + assert(!r); + foundthing= context; + foundthing->ans= ans; + foundthing->qu= 0; + } } static void restartbuf(void) { - if (inbuf>0) { - buf[inbuf++]= 0; - if (fputs(buf,stdout) < 0) outputerr(); - } + if (inbuf>0) queueoutstr(addrtextbuf,inbuf); inbuf= 0; } +static int comparer(const void *a, const void *b) { + return memcmp(a,b,4); +} + static void procaddr(void) { struct treething *foundthing; - void *expectreturn, **searchfound; + void **searchfound; + struct outqueuenode *entry; int r; if (!newthing) { - newthing= malloc(sizeof(struct treething)); - if (!newthing) sysfail("malloc"); + newthing= xmalloc(sizeof(struct treething)); newthing->qu= 0; newthing->ans= 0; } @@ -122,22 +328,16 @@ static void procaddr(void) { rrt,0,foundthing,&foundthing->qu); if (r) adnsfail("submit",r); } - if (!forever && foundthing->ans && foundthing->ans->status == adns_s_timeout) { - free(foundthing->ans); - foundthing->ans= 0; - } - if (!foundthing->ans) { - expectreturn= foundthing; - r= (forever ? adns_wait : adns_check) - (ads,&foundthing->qu,&foundthing->ans,&expectreturn); - assert(r==EAGAIN || (!r && foundthing->ans && expectreturn==foundthing)); - } - if (foundthing->ans && foundthing->ans->nrrs > 0) { - if (fputs(foundthing->ans->rrs.str[0],stdout) < 0) outputerr(); - inbuf= 0; - } else { - restartbuf(); - } + entry= xmalloc(sizeof(*entry)); + entry->buffer= xmalloc(inbuf); + entry->textp= entry->buffer; + memcpy(entry->textp,addrtextbuf,inbuf); + entry->textlen= inbuf; + entry->addr= foundthing; + entry->printbefore= printbefore; + LIST_LINK_TAIL(outqueue,entry); + outqueuelen++; + inbuf= 0; cbyte= -1; } @@ -146,91 +346,129 @@ static void startaddr(void) { inbyte= 0; } -static void mustputchar(int c) { - if (putchar(c) == EOF) outputerr(); -} +static void readstdin(void) { + char readbuf[512], *p; + int r, c, nbyte; -int main(int argc, const char *const *argv) { - const char *arg; - int nbyte, r; - - while ((arg= *++argv)) { - if (arg[0] != '-') usageerr("no non-option arguments are allowed"); - if (arg[1] == '-') { - if (!strcmp(arg,"--brackets")) { - bracket= 1; - } else if (!strcmp(arg,"--unchecked")) { - rrt= adns_r_ptr_raw; - } else if (!strcmp(arg,"--wait")) { - forever= 1; - } else if (!strcmp(arg,"--help")) { - usage(); exit(0); - } else { - usageerr("unknown long option"); - } - } else { - while ((c= *++arg)) { - switch (c) { - case 'b': - bracket= 1; - break; - case 'u': - rrt= adns_r_ptr_raw; - break; - case 'w': - forever= 1; - break; - case 'h': - usage(); exit(0); - default: - usageerr("unknown short option"); - } - } - } + while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) { + if (r == 0) { inputeof= 1; return; } + if (r == EAGAIN) return; + if (r != EINTR) sysfail("read stdin"); } - if (setvbuf(stdout,0,_IOLBF,0)) sysfail("setvbuf stdout"); - - memset(&sa,0,sizeof(sa)); - sa.sin_family= AF_INET; - - r= adns_init(&ads,0,0); if (r) adnsfail("init",r); - - cbyte= -1; - inbyte= -1; - inbuf= 0; - if (!bracket) startaddr(); - while ((c= getchar()) != EOF) { + for (p=readbuf; r>0; r--,p++) { + c= *p; if (cbyte==-1 && bracket && c=='[') { - buf[inbuf++]= c; + addrtextbuf[inbuf++]= c; startaddr(); } else if (cbyte==-1 && !bracket && !isalnum(c)) { - mustputchar(c); + queueoutchar(c); startaddr(); } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' && (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) { bytes[cbyte]= nbyte; - buf[inbuf++]= c; + addrtextbuf[inbuf++]= c; inbyte++; } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') { bytes[++cbyte]= 0; - buf[inbuf++]= c; + addrtextbuf[inbuf++]= c; inbyte= 0; } else if (cbyte==3 && inbyte>0 && bracket && c==']') { - buf[inbuf++]= c; + addrtextbuf[inbuf++]= c; procaddr(); } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) { procaddr(); - mustputchar(c); + queueoutchar(c); startaddr(); } else { restartbuf(); - mustputchar(c); + queueoutchar(c); cbyte= -1; if (!bracket && !isalnum(c)) startaddr(); } } - if (ferror(stdin) || fclose(stdin)) sysfail("read stdin"); - if (cbyte==3 && inbyte>0 && !bracket) procaddr(); - if (fclose(stdout)) sysfail("close stdout"); +} + +static void startup(void) { + int r; + + if (nonblock(0,1)) sysfail("set stdin to nonblocking mode"); + if (nonblock(1,1)) sysfail("set stdout to nonblocking mode"); + memset(&sa,0,sizeof(sa)); + sa.sin_family= AF_INET; + if (config_text) { + r= adns_init_strcfg(&ads,initflags,stderr,config_text); + } else { + r= adns_init(&ads,initflags,0); + } + if (r) adnsfail("init",r); + cbyte= -1; + inbyte= -1; + inbuf= 0; + if (!bracket) startaddr(); +} + +int main(int argc, const char *const *argv) { + int r, maxfd; + fd_set readfds, writefds, exceptfds; + struct outqueuenode *entry; + struct timeval *tv, tvbuf, now; + + parseargs(argv); + startup(); + + while (!inputeof || outqueue.head) { + maxfd= 2; + tv= 0; + FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); + if ((entry= outqueue.head) && !outblocked) { + if (!entry->addr) { + writestdout(entry); + continue; + } + if (entry->addr->ans) { + if (entry->addr->ans->nrrs) + replacetextwithname(entry); + entry->addr= 0; + continue; + } + r= gettimeofday(&now,0); if (r) sysfail("gettimeofday"); + if (forever) { + tv= 0; + } else if (!timercmp(&now,&entry->printbefore,<)) { + entry->addr= 0; + continue; + } else { + tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1; + tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000; + tvbuf.tv_sec += tvbuf.tv_usec / 1000000; + tvbuf.tv_usec %= 1000000; + tv= &tvbuf; + } + adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds, + &tv,&tvbuf,&now); + } + if (outblocked) FD_SET(1,&writefds); + if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds); + + r= select(maxfd,&readfds,&writefds,&exceptfds,tv); + if (r < 0) { if (r == EINTR) continue; else sysfail("select"); } + + r= gettimeofday(&now,0); if (r) sysfail("gettimeofday"); + adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now); + checkadnsqueries(); + + if (FD_ISSET(0,&readfds)) { + if (!forever) { + printbefore= now; + timevaladd(&printbefore,timeout); + } + readstdin(); + } else if (FD_ISSET(1,&writefds)) { + outblocked= 0; + } + } + if (nonblock(0,0)) sysfail("un-nonblock stdin"); + if (nonblock(1,0)) sysfail("un-nonblock stdout"); + adns_finish(ads); exit(0); }