3 * - filter which does resolving, not part of the library
7 * Copyright (C) 1999-2000 Ian Jackson <ian@davenant.greenend.org.uk>
9 * It is part of adns, which is
10 * Copyright (C) 1997-2000 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/types.h>
46 struct outqueuenode *next, *back;
50 struct timeval printbefore;
51 struct treething *addr;
54 static int bracket, forever, address;
55 static unsigned long timeout= 1000;
56 static adns_rrtype rrt= adns_r_ptr;
58 static int outblocked, inputeof;
59 static struct { struct outqueuenode *head, *tail; } outqueue;
60 static int peroutqueuenode, outqueuelen;
62 static struct sockaddr_in sa;
63 static adns_state ads;
65 static char addrtextbuf[14];
66 static int cbyte, inbyte, inbuf;
67 static unsigned char bytes[4];
68 static struct timeval printbefore;
71 unsigned char bytes[4];
76 static struct treething *newthing;
77 static void *treeroot;
79 static int nonblock(int fd, int isnonblock) {
84 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
89 static void quit(int exitstatus) NONRETURNING;
90 static void quit(int exitstatus) {
96 static void sysfail(const char *what) NONRETURNING;
97 static void sysfail(const char *what) {
98 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
102 static void *xmalloc(size_t sz) {
104 r= malloc(sz); if (r) return r;
108 static void outputerr(void) NONRETURNING;
109 static void outputerr(void) { sysfail("write to stdout"); }
111 static void usage(void) {
112 if (printf("usage: adnsresfilter [<options ...>]\n"
113 " adnsresfilter -h|--help\n"
114 "options: -t<milliseconds>|--timeout <milliseconds>\n"
115 " -w|--wait (always wait for queries to time out or fail)\n"
116 " -b|--brackets (require [...] around IP addresses)\n"
117 " -a|--address (always include [address] in output)\n"
118 " -u|--unchecked (do not forward map for checking)\n"
119 "Timeout is the maximum amount to delay any particular bit of output for.\n"
120 "Lookups will go on in the background. Default timeout = 100 (ms).\n")
124 static void usageerr(const char *why) NONRETURNING;
125 static void usageerr(const char *why) {
126 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
131 static void adnsfail(const char *what, int e) NONRETURNING;
132 static void adnsfail(const char *what, int e) {
133 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
137 static void settimeout(const char *arg) {
139 timeout= strtoul(arg,&ep,0);
140 if (*ep) usageerr("invalid timeout");
143 static void parseargs(const char *const *argv) {
147 while ((arg= *++argv)) {
148 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
150 if (!strcmp(arg,"--brackets")) {
152 } else if (!strcmp(arg,"--unchecked")) {
154 } else if (!strcmp(arg,"--wait")) {
156 } else if (!strcmp(arg,"--address")) {
158 } else if (!strcmp(arg,"--help")) {
160 } else if (!strcmp(arg,"--timeout")) {
161 if (!(arg= *++argv)) usageerr("--timeout needs a value");
165 usageerr("unknown long option");
168 while ((c= *++arg)) {
186 if (*++arg) settimeout(arg);
187 else if ((arg= *++argv)) settimeout(arg);
188 else usageerr("-t needs a value");
193 usageerr("unknown short option");
200 static void queueoutchar(int c) {
201 struct outqueuenode *entry;
203 entry= outqueue.tail;
204 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
205 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
206 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
207 entry= xmalloc(sizeof(*entry));
208 entry->buffer= xmalloc(peroutqueuenode);
209 entry->textp= entry->buffer;
212 LIST_LINK_TAIL(outqueue,entry);
215 entry->textp[entry->textlen++]= c;
218 static void queueoutstr(const char *str, int len) {
219 while (len-- > 0) queueoutchar(*str++);
222 static void writestdout(struct outqueuenode *entry) {
225 while (entry->textlen) {
226 r= write(1, entry->textp, entry->textlen);
228 if (errno == EINTR) continue;
229 if (errno == EAGAIN) { outblocked= 1; break; }
230 sysfail("write stdout");
232 assert(r <= entry->textlen);
236 if (!entry->textlen) {
237 LIST_UNLINK(outqueue,entry);
244 static void replacetextwithname(struct outqueuenode *entry) {
248 name= entry->addr->ans->rrs.str[0];
249 namelen= strlen(name);
254 entry->textlen= namelen;
256 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
257 newbuf= xmalloc(newlen + 1);
258 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
260 entry->buffer= entry->textp= newbuf;
261 entry->textlen= newlen;
265 static void checkadnsqueries(void) {
269 struct treething *foundthing;
273 qu= 0; context= 0; ans= 0;
274 r= adns_check(ads,&qu,&ans,&context);
275 if (r == ESRCH || r == EAGAIN) break;
278 foundthing->ans= ans;
283 static void restartbuf(void) {
284 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
288 static int comparer(const void *a, const void *b) {
289 return memcmp(a,b,4);
292 static void procaddr(void) {
293 struct treething *foundthing;
295 struct outqueuenode *entry;
299 newthing= xmalloc(sizeof(struct treething));
304 memcpy(newthing->bytes,bytes,4);
305 searchfound= tsearch(newthing,&treeroot,comparer);
306 if (!searchfound) sysfail("tsearch");
307 foundthing= *searchfound;
309 if (foundthing == newthing) {
311 memcpy(&sa.sin_addr,bytes,4);
312 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
313 rrt,0,foundthing,&foundthing->qu);
314 if (r) adnsfail("submit",r);
316 entry= xmalloc(sizeof(*entry));
317 entry->buffer= xmalloc(inbuf);
318 entry->textp= entry->buffer;
319 memcpy(entry->textp,addrtextbuf,inbuf);
320 entry->textlen= inbuf;
321 entry->addr= foundthing;
322 entry->printbefore= printbefore;
323 LIST_LINK_TAIL(outqueue,entry);
329 static void startaddr(void) {
334 static void readstdin(void) {
335 char readbuf[512], *p;
338 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
339 if (r == 0) { inputeof= 1; return; }
340 if (r == EAGAIN) return;
341 if (r != EINTR) sysfail("read stdin");
343 for (p=readbuf; r>0; r--,p++) {
345 if (cbyte==-1 && bracket && c=='[') {
346 addrtextbuf[inbuf++]= c;
348 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
351 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
352 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
354 addrtextbuf[inbuf++]= c;
356 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
358 addrtextbuf[inbuf++]= c;
360 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
361 addrtextbuf[inbuf++]= c;
363 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
371 if (!bracket && !isalnum(c)) startaddr();
376 static void startup(void) {
379 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
380 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
381 memset(&sa,0,sizeof(sa));
382 sa.sin_family= AF_INET;
383 r= adns_init(&ads,0,0); if (r) adnsfail("init",r);
387 if (!bracket) startaddr();
390 int main(int argc, const char *const *argv) {
392 fd_set readfds, writefds, exceptfds;
393 struct outqueuenode *entry;
394 struct timeval *tv, tvbuf, now;
399 while (!inputeof || outqueue.head) {
402 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
403 if ((entry= outqueue.head) && !outblocked) {
408 if (entry->addr->ans) {
409 if (entry->addr->ans->nrrs)
410 replacetextwithname(entry);
414 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
417 } else if (!timercmp(&now,&entry->printbefore,<)) {
421 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
422 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
423 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
424 tvbuf.tv_usec %= 1000000;
427 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
430 if (outblocked) FD_SET(1,&writefds);
431 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
433 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
434 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
436 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
437 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
440 if (FD_ISSET(0,&readfds)) {
443 timevaladd(&printbefore,timeout);
446 } else if (FD_ISSET(1,&writefds)) {
450 if (nonblock(0,0)) sysfail("un-nonblock stdin");
451 if (nonblock(1,0)) sysfail("un-nonblock stdout");
452 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
453 if (fclose(stdout)) sysfail("close stdout");