#include <assert.h>
#include <ctype.h>
+#include <sys/fcntl.h>
+
#include "adns.h"
#include "config.h"
+#include "dlist.h"
+#include "tvarith.h"
+
+struct outqueuenode {
+ struct outqueuenode *next, *back;
+ void *buffer;
+ char *textp;
+ int textlen;
+ struct timeval printbefore;
+ struct treething *addr;
+};
+
+static int bracket, forever, timeout=10;
+static adns_rrtype rrt= adns_r_ptr;
+
+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;
+}
+
+static void quit(int exitstatus) NONRETURNING;
+static void quit(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);
+ quit(2);
+}
+
+static void *xmalloc(size_t sz) {
+ void *r;
+ r= malloc(sz); if (r) return r;
+ sysfail("malloc");
}
static void outputerr(void) NONRETURNING;
" adnsresfilter -h|--help\n"
"options: -b|--brackets\n"
" -w|--wait\n"
+ " -t<timeout>|--timeout <timeout>\n"
" -u|--unchecked\n")
== EOF) outputerr();
}
static void usageerr(const char *why) {
fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
usage();
- exit(1);
+ quit(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);
+ quit(2);
}
-static int bracket, forever;
-static adns_rrtype rrt= adns_r_ptr;
-
-static struct sockaddr_in sa;
-static adns_state ads;
-
-static char buf[14];
-static int c, cbyte, inbyte, inbuf;
-static unsigned char bytes[4];
-
-struct treething {
- unsigned char bytes[4];
- adns_query qu;
- adns_answer *ans;
-};
-
-static struct treething *newthing;
-static void *treeroot;
-
static int comparer(const void *a, const void *b) {
return memcmp(a,b,4);
}
-static void restartbuf(void) {
- if (inbuf>0) {
- buf[inbuf++]= 0;
- if (fputs(buf,stdout) < 0) outputerr();
- }
- inbuf= 0;
-}
-
-static void procaddr(void) {
- struct treething *foundthing;
- void *expectreturn, **searchfound;
- int r;
-
- if (!newthing) {
- newthing= malloc(sizeof(struct treething));
- if (!newthing) sysfail("malloc");
- newthing->qu= 0;
- newthing->ans= 0;
- }
-
- memcpy(newthing->bytes,bytes,4);
- searchfound= tsearch(newthing,&treeroot,comparer);
- if (!searchfound) sysfail("tsearch");
- foundthing= *searchfound;
-
- if (foundthing == newthing) {
- newthing= 0;
- memcpy(&sa.sin_addr,bytes,4);
- r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
- 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();
- }
- cbyte= -1;
-}
-
-static void startaddr(void) {
- bytes[cbyte=0]= 0;
- inbyte= 0;
-}
-
-static void mustputchar(int c) {
- if (putchar(c) == EOF) outputerr();
-}
-
-int main(int argc, const char *const *argv) {
+static void parseargs(const char *const *argv) {
const char *arg;
- int nbyte, r;
+ int c;
while ((arg= *++argv)) {
if (arg[0] != '-') usageerr("no non-option arguments are allowed");
} else if (!strcmp(arg,"--wait")) {
forever= 1;
} else if (!strcmp(arg,"--help")) {
- usage(); exit(0);
+ usage(); quit(0);
} else {
usageerr("unknown long option");
}
forever= 1;
break;
case 'h':
- usage(); exit(0);
+ usage(); quit(0);
default:
usageerr("unknown short option");
}
}
}
}
- if (setvbuf(stdout,0,_IOLBF,0)) sysfail("setvbuf stdout");
+}
- memset(&sa,0,sizeof(sa));
- sa.sin_family= AF_INET;
+static void queueoutchar(int c) {
+ struct outqueuenode *entry;
+
+ entry= outqueue.tail;
+ if (!entry->back || entry->addr || entry->textlen >= peroutqueuenode) {
+ entry= xmalloc(sizeof(*entry));
+ peroutqueuenode= !entry->back || entry->addr ? 32 :
+ peroutqueuenode >= 1024 ? 2048 : peroutqueuenode<<1;
+ entry->buffer= xmalloc(peroutqueuenode);
+ entry->textp= entry->buffer;
+ entry->textlen= 0;
+ entry->addr= 0;
+ LIST_LINK_TAIL(outqueue,entry);
+ outqueuelen++;
+ }
+ *entry->textp++= c;
+ entry->textlen++;
+}
- r= adns_init(&ads,0,0); if (r) adnsfail("init",r);
+static void queueoutstr(const char *str, int len) {
+ while (len > 0) queueoutchar(*str++);
+}
- cbyte= -1;
- inbyte= -1;
+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) {
+ free(entry->buffer);
+ entry->buffer= 0;
+ entry->textp= entry->addr->ans->rrs.str[0];
+ entry->textlen= strlen(entry->textp);
+}
+
+static void checkadnsqueries(void) {
+ adns_query qu;
+ adns_answer *ans;
+ void *context;
+ struct treething *foundthing;
+ int r;
+
+ 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) queueoutstr(addrtextbuf,inbuf);
inbuf= 0;
- if (!bracket) startaddr();
- while ((c= getchar()) != EOF) {
+}
+
+static void procaddr(void) {
+ struct treething *foundthing;
+ void **searchfound;
+ struct outqueuenode *entry;
+ int r;
+
+ if (!newthing) {
+ newthing= xmalloc(sizeof(struct treething));
+ newthing->qu= 0;
+ newthing->ans= 0;
+ }
+
+ memcpy(newthing->bytes,bytes,4);
+ searchfound= tsearch(newthing,&treeroot,comparer);
+ if (!searchfound) sysfail("tsearch");
+ foundthing= *searchfound;
+
+ if (foundthing == newthing) {
+ memcpy(&sa.sin_addr,bytes,4);
+ r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
+ rrt,0,foundthing,&foundthing->qu);
+ if (r) adnsfail("submit",r);
+ }
+ entry= xmalloc(sizeof(*entry));
+ entry->buffer= xmalloc(inbuf);
+ entry->textp= entry->buffer;
+ memcpy(entry->textp,addrtextbuf,inbuf);
+ entry->textlen= inbuf;
+ entry->addr= foundthing;
+ LIST_LINK_TAIL(outqueue,entry);
+ outqueuelen++;
+ inbuf= 0;
+ cbyte= -1;
+}
+
+static void startaddr(void) {
+ bytes[cbyte=0]= 0;
+ inbyte= 0;
+}
+
+static void readstdin(void) {
+ char readbuf[512], *p;
+ int r, c, nbyte;
+
+ while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
+ if (r == 0) { inputeof= 1; return; }
+ if (r == EAGAIN) return;
+ if (r != EINTR) sysfail("read stdin");
+ }
+ 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();
+}
+
+static void startup(void) {
+ int r;
+
+ if (setvbuf(stdout,0,_IOLBF,0)) sysfail("setvbuf stdout");
+ 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;
+ r= adns_init(&ads,0,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;
+ } else 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= printbefore.tv_sec - now.tv_sec - 1;
+ tvbuf.tv_usec= 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<1000) 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 (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
if (fclose(stdout)) sysfail("close stdout");
+ if (nonblock(0,0)) sysfail("un-nonblock stdin");
+ if (nonblock(1,0)) sysfail("un-nonblock stdout");
exit(0);
}