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-2000 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 #ifdef ADNS_REGRESS_TEST
47 # include "hredirect.h"
51 struct outqueuenode *next, *back;
55 struct timeval printbefore;
56 struct treething *addr;
59 static int bracket, forever, address;
60 static unsigned long timeout= 1000;
61 static adns_rrtype rrt= adns_r_ptr;
62 static adns_initflags initflags= 0;
63 static const char *config_text;
65 static int outblocked, inputeof;
66 static struct { struct outqueuenode *head, *tail; } outqueue;
67 static int peroutqueuenode, outqueuelen;
69 static struct sockaddr_in sa;
70 static adns_state ads;
72 static char addrtextbuf[14];
73 static int cbyte, inbyte, inbuf;
74 static unsigned char bytes[4];
75 static struct timeval printbefore;
78 unsigned char bytes[4];
83 static struct treething *newthing;
84 static void *treeroot;
86 static int nonblock(int fd, int isnonblock) {
91 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
96 void quitnow(int exitstatus) {
102 static void sysfail(const char *what) NONRETURNING;
103 static void sysfail(const char *what) {
104 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
108 static void *xmalloc(size_t sz) {
110 r= malloc(sz); if (r) return r;
114 static void outputerr(void) NONRETURNING;
115 static void outputerr(void) { sysfail("write to stdout"); }
117 static void usage(void) {
118 if (printf("usage: adnsresfilter [<options ...>]\n"
119 " adnsresfilter -h|--help | --version\n"
120 "options: -t<milliseconds>|--timeout <milliseconds>\n"
121 " -w|--wait (always wait for queries to time out or fail)\n"
122 " -b|--brackets (require [...] around IP addresses)\n"
123 " -a|--address (always include [address] in output)\n"
124 " -u|--unchecked (do not forward map for checking)\n"
125 " --config <text> (use this instead of resolv.conf)\n"
126 " --debug (turn on adns resolver debugging)\n"
127 "Timeout is the maximum amount to delay any particular bit of output for.\n"
128 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
130 if (fflush(stdout)) sysfail("flush stdout");
133 static void usageerr(const char *why) NONRETURNING;
134 static void usageerr(const char *why) {
135 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
140 static void adnsfail(const char *what, int e) NONRETURNING;
141 static void adnsfail(const char *what, int e) {
142 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
146 static void settimeout(const char *arg) {
148 timeout= strtoul(arg,&ep,0);
149 if (*ep) usageerr("invalid timeout");
152 static void parseargs(const char *const *argv) {
156 while ((arg= *++argv)) {
157 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
159 if (!strcmp(arg,"--timeout")) {
160 if (!(arg= *++argv)) usageerr("--timeout needs a value");
163 } else if (!strcmp(arg,"--wait")) {
165 } else if (!strcmp(arg,"--brackets")) {
167 } else if (!strcmp(arg,"--address")) {
169 } else if (!strcmp(arg,"--unchecked")) {
171 } else if (!strcmp(arg,"--config")) {
172 if (!(arg= *++argv)) usageerr("--config needs a value");
174 } else if (!strcmp(arg,"--debug")) {
175 initflags |= adns_if_debug;
176 } else if (!strcmp(arg,"--help")) {
178 } else if (!strcmp(arg,"--version")) {
179 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0);
181 usageerr("unknown long option");
184 while ((c= *++arg)) {
187 if (*++arg) settimeout(arg);
188 else if ((arg= *++argv)) settimeout(arg);
189 else usageerr("-t needs a value");
209 usageerr("unknown short option");
216 static void queueoutchar(int c) {
217 struct outqueuenode *entry;
219 entry= outqueue.tail;
220 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
221 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
222 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
223 entry= xmalloc(sizeof(*entry));
224 entry->buffer= xmalloc(peroutqueuenode);
225 entry->textp= entry->buffer;
228 LIST_LINK_TAIL(outqueue,entry);
231 entry->textp[entry->textlen++]= c;
234 static void queueoutstr(const char *str, int len) {
235 while (len-- > 0) queueoutchar(*str++);
238 static void writestdout(struct outqueuenode *entry) {
241 while (entry->textlen) {
242 r= write(1, entry->textp, entry->textlen);
244 if (errno == EINTR) continue;
245 if (errno == EAGAIN) { outblocked= 1; break; }
246 sysfail("write stdout");
248 assert(r <= entry->textlen);
252 if (!entry->textlen) {
253 LIST_UNLINK(outqueue,entry);
260 static void replacetextwithname(struct outqueuenode *entry) {
264 name= entry->addr->ans->rrs.str[0];
265 namelen= strlen(name);
270 entry->textlen= namelen;
272 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
273 newbuf= xmalloc(newlen + 1);
274 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
276 entry->buffer= entry->textp= newbuf;
277 entry->textlen= newlen;
281 static void checkadnsqueries(void) {
285 struct treething *foundthing;
289 qu= 0; context= 0; ans= 0;
290 r= adns_check(ads,&qu,&ans,&context);
291 if (r == ESRCH || r == EAGAIN) break;
294 foundthing->ans= ans;
299 static void restartbuf(void) {
300 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
304 static int comparer(const void *a, const void *b) {
305 return memcmp(a,b,4);
308 static void procaddr(void) {
309 struct treething *foundthing;
311 struct outqueuenode *entry;
315 newthing= xmalloc(sizeof(struct treething));
320 memcpy(newthing->bytes,bytes,4);
321 searchfound= tsearch(newthing,&treeroot,comparer);
322 if (!searchfound) sysfail("tsearch");
323 foundthing= *searchfound;
325 if (foundthing == newthing) {
327 memcpy(&sa.sin_addr,bytes,4);
328 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
329 rrt,0,foundthing,&foundthing->qu);
330 if (r) adnsfail("submit",r);
332 entry= xmalloc(sizeof(*entry));
333 entry->buffer= xmalloc(inbuf);
334 entry->textp= entry->buffer;
335 memcpy(entry->textp,addrtextbuf,inbuf);
336 entry->textlen= inbuf;
337 entry->addr= foundthing;
338 entry->printbefore= printbefore;
339 LIST_LINK_TAIL(outqueue,entry);
345 static void startaddr(void) {
350 static void readstdin(void) {
351 char readbuf[512], *p;
354 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
355 if (r == 0) { inputeof= 1; return; }
356 if (r == EAGAIN) return;
357 if (r != EINTR) sysfail("read stdin");
359 for (p=readbuf; r>0; r--,p++) {
361 if (cbyte==-1 && bracket && c=='[') {
362 addrtextbuf[inbuf++]= c;
364 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
367 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
368 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
370 addrtextbuf[inbuf++]= c;
372 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
374 addrtextbuf[inbuf++]= c;
376 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
377 addrtextbuf[inbuf++]= c;
379 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
387 if (!bracket && !isalnum(c)) startaddr();
392 static void startup(void) {
395 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
396 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
397 memset(&sa,0,sizeof(sa));
398 sa.sin_family= AF_INET;
400 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
402 r= adns_init(&ads,initflags,0);
404 if (r) adnsfail("init",r);
408 if (!bracket) startaddr();
411 int main(int argc, const char *const *argv) {
413 fd_set readfds, writefds, exceptfds;
414 struct outqueuenode *entry;
415 struct timeval *tv, tvbuf, now;
420 while (!inputeof || outqueue.head) {
423 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
424 if ((entry= outqueue.head) && !outblocked) {
429 if (entry->addr->ans) {
430 if (entry->addr->ans->nrrs)
431 replacetextwithname(entry);
435 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
438 } else if (!timercmp(&now,&entry->printbefore,<)) {
442 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
443 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
444 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
445 tvbuf.tv_usec %= 1000000;
448 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
451 if (outblocked) FD_SET(1,&writefds);
452 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
454 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
455 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
457 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
458 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
461 if (FD_ISSET(0,&readfds)) {
464 timevaladd(&printbefore,timeout);
467 } else if (FD_ISSET(1,&writefds)) {
471 if (nonblock(0,0)) sysfail("un-nonblock stdin");
472 if (nonblock(1,0)) sysfail("un-nonblock stdout");