3 * - filter which does resolving, not part of the library
7 * Copyright (C) 1999 Ian Jackson <ian@davenant.greenend.org.uk>
9 * It is part of adns, which is
10 * Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
11 * Copyright (C) 1999 Tony Finch <dot@dotat.at>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software Foundation,
25 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <sys/fcntl.h>
44 struct outqueuenode *next, *back;
48 struct timeval printbefore;
49 struct treething *addr;
52 static int bracket, forever, address;
53 static unsigned long timeout=100;
54 static adns_rrtype rrt= adns_r_ptr;
56 static int outblocked, inputeof;
57 static struct { struct outqueuenode *head, *tail; } outqueue;
58 static int peroutqueuenode, outqueuelen;
60 static struct sockaddr_in sa;
61 static adns_state ads;
63 static char addrtextbuf[14];
64 static int cbyte, inbyte, inbuf;
65 static unsigned char bytes[4];
66 static struct timeval printbefore;
69 unsigned char bytes[4];
74 static struct treething *newthing;
75 static void *treeroot;
77 static int nonblock(int fd, int isnonblock) {
82 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
87 static void quit(int exitstatus) NONRETURNING;
88 static void quit(int exitstatus) {
94 static void sysfail(const char *what) NONRETURNING;
95 static void sysfail(const char *what) {
96 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
100 static void *xmalloc(size_t sz) {
102 r= malloc(sz); if (r) return r;
106 static void outputerr(void) NONRETURNING;
107 static void outputerr(void) { sysfail("write to stdout"); }
109 static void usage(void) {
110 if (printf("usage: adnsresfilter [<options ...>]\n"
111 " adnsresfilter -h|--help\n"
112 "options: -t<milliseconds>|--timeout <milliseconds>\n"
113 " -w|--wait (always wait for queries to time out or fail)\n"
114 " -b|--brackets (require [...] around IP addresses)\n"
115 " -a|--address (always include [address] in output)\n"
116 " -u|--unchecked (do not forward map for checking)\n"
117 "Timeout is the maximum amount to delay any particular bit of output for.\n"
118 "Lookups will go on in the background. Default timeout = 100 (ms).\n")
122 static void usageerr(const char *why) NONRETURNING;
123 static void usageerr(const char *why) {
124 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
129 static void adnsfail(const char *what, int e) NONRETURNING;
130 static void adnsfail(const char *what, int e) {
131 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
135 static void settimeout(const char *arg) {
137 timeout= strtoul(arg,&ep,0);
138 if (*ep) usageerr("invalid timeout");
141 static void parseargs(const char *const *argv) {
145 while ((arg= *++argv)) {
146 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
148 if (!strcmp(arg,"--brackets")) {
150 } else if (!strcmp(arg,"--unchecked")) {
152 } else if (!strcmp(arg,"--wait")) {
154 } else if (!strcmp(arg,"--address")) {
156 } else if (!strcmp(arg,"--help")) {
158 } else if (!strcmp(arg,"--timeout")) {
159 if (!(arg= *++argv)) usageerr("--timeout needs a value");
163 usageerr("unknown long option");
166 while ((c= *++arg)) {
184 if (*++arg) settimeout(arg);
185 else if ((arg= *++argv)) settimeout(arg);
186 else usageerr("-t needs a value");
191 usageerr("unknown short option");
198 static void queueoutchar(int c) {
199 struct outqueuenode *entry;
201 entry= outqueue.tail;
202 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
203 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
204 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
205 entry= xmalloc(sizeof(*entry));
206 entry->buffer= xmalloc(peroutqueuenode);
207 entry->textp= entry->buffer;
210 LIST_LINK_TAIL(outqueue,entry);
213 entry->textp[entry->textlen++]= c;
216 static void queueoutstr(const char *str, int len) {
217 while (len-- > 0) queueoutchar(*str++);
220 static void writestdout(struct outqueuenode *entry) {
223 while (entry->textlen) {
224 r= write(1, entry->textp, entry->textlen);
226 if (errno == EINTR) continue;
227 if (errno == EAGAIN) { outblocked= 1; break; }
228 sysfail("write stdout");
230 assert(r <= entry->textlen);
234 if (!entry->textlen) {
235 LIST_UNLINK(outqueue,entry);
242 static void replacetextwithname(struct outqueuenode *entry) {
246 name= entry->addr->ans->rrs.str[0];
247 namelen= strlen(name);
252 entry->textlen= namelen;
254 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
255 newbuf= xmalloc(newlen + 1);
256 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
258 entry->buffer= entry->textp= newbuf;
259 entry->textlen= newlen;
263 static void checkadnsqueries(void) {
267 struct treething *foundthing;
271 qu= 0; context= 0; ans= 0;
272 r= adns_check(ads,&qu,&ans,&context);
273 if (r == ESRCH || r == EAGAIN) break;
276 foundthing->ans= ans;
281 static void restartbuf(void) {
282 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
286 static int comparer(const void *a, const void *b) {
287 return memcmp(a,b,4);
290 static void procaddr(void) {
291 struct treething *foundthing;
293 struct outqueuenode *entry;
297 newthing= xmalloc(sizeof(struct treething));
302 memcpy(newthing->bytes,bytes,4);
303 searchfound= tsearch(newthing,&treeroot,comparer);
304 if (!searchfound) sysfail("tsearch");
305 foundthing= *searchfound;
307 if (foundthing == newthing) {
309 memcpy(&sa.sin_addr,bytes,4);
310 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
311 rrt,0,foundthing,&foundthing->qu);
312 if (r) adnsfail("submit",r);
314 entry= xmalloc(sizeof(*entry));
315 entry->buffer= xmalloc(inbuf);
316 entry->textp= entry->buffer;
317 memcpy(entry->textp,addrtextbuf,inbuf);
318 entry->textlen= inbuf;
319 entry->addr= foundthing;
320 entry->printbefore= printbefore;
321 LIST_LINK_TAIL(outqueue,entry);
327 static void startaddr(void) {
332 static void readstdin(void) {
333 char readbuf[512], *p;
336 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
337 if (r == 0) { inputeof= 1; return; }
338 if (r == EAGAIN) return;
339 if (r != EINTR) sysfail("read stdin");
341 for (p=readbuf; r>0; r--,p++) {
343 if (cbyte==-1 && bracket && c=='[') {
344 addrtextbuf[inbuf++]= c;
346 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
349 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
350 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
352 addrtextbuf[inbuf++]= c;
354 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
356 addrtextbuf[inbuf++]= c;
358 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
359 addrtextbuf[inbuf++]= c;
361 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
369 if (!bracket && !isalnum(c)) startaddr();
372 if (cbyte==3 && inbyte>0 && !bracket) procaddr();
375 static void startup(void) {
378 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
379 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
380 memset(&sa,0,sizeof(sa));
381 sa.sin_family= AF_INET;
382 r= adns_init(&ads,0,0); if (r) adnsfail("init",r);
386 if (!bracket) startaddr();
389 int main(int argc, const char *const *argv) {
391 fd_set readfds, writefds, exceptfds;
392 struct outqueuenode *entry;
393 struct timeval *tv, tvbuf, now;
398 while (!inputeof || outqueue.head) {
401 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
402 if ((entry= outqueue.head) && !outblocked) {
407 if (entry->addr->ans) {
408 if (entry->addr->ans->nrrs)
409 replacetextwithname(entry);
413 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
416 } else if (!timercmp(&now,&entry->printbefore,<)) {
420 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
421 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
422 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
423 tvbuf.tv_usec %= 1000000;
426 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
429 if (outblocked) FD_SET(1,&writefds);
430 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
432 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
433 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
435 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
436 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
439 if (FD_ISSET(0,&readfds)) {
442 timevaladd(&printbefore,timeout);
445 } else if (FD_ISSET(1,&writefds)) {
449 if (nonblock(0,0)) sysfail("un-nonblock stdin");
450 if (nonblock(1,0)) sysfail("un-nonblock stdout");
451 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
452 if (fclose(stdout)) sysfail("close stdout");