update copyright dates
[adns] / client / adnsresfilter.c
CommitLineData
790cfd2b 1/*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5/*
39f45e7e 6 * This file is part of adns, which is
7 * Copyright (C) 1997-2000,2003,2006 Ian Jackson
8 * Copyright (C) 1999-2000,2003,2006 Tony Finch
9 * Copyright (C) 1991 Massachusetts Institute of Technology
10 * (See the file INSTALL for full details.)
790cfd2b 11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <search.h>
32#include <assert.h>
33#include <ctype.h>
34
93b54ac1 35#include <sys/types.h>
36#include <unistd.h>
8a4e090e 37#include <fcntl.h>
83e1be2f 38
790cfd2b 39#include "config.h"
cc0f95d0 40#include "adns.h"
83e1be2f 41#include "dlist.h"
42#include "tvarith.h"
8a5e5147 43#include "client.h"
83e1be2f 44
ae0da6f3 45#ifdef ADNS_REGRESS_TEST
46# include "hredirect.h"
47#endif
48
83e1be2f 49struct outqueuenode {
50 struct outqueuenode *next, *back;
51 void *buffer;
52 char *textp;
53 int textlen;
54 struct timeval printbefore;
55 struct treething *addr;
56};
57
a93e4dbf 58static int bracket, forever, address;
9463e0a6 59static unsigned long timeout= 1000;
83e1be2f 60static adns_rrtype rrt= adns_r_ptr;
ae0da6f3 61static adns_initflags initflags= 0;
62static const char *config_text;
83e1be2f 63
64static int outblocked, inputeof;
65static struct { struct outqueuenode *head, *tail; } outqueue;
66static int peroutqueuenode, outqueuelen;
67
68static struct sockaddr_in sa;
69static adns_state ads;
70
71static char addrtextbuf[14];
72static int cbyte, inbyte, inbuf;
73static unsigned char bytes[4];
74static struct timeval printbefore;
75
76struct treething {
77 unsigned char bytes[4];
78 adns_query qu;
79 adns_answer *ans;
80};
81
82static struct treething *newthing;
83static void *treeroot;
84
85static int nonblock(int fd, int isnonblock) {
86 int r;
87
88 r= fcntl(fd,F_GETFL);
89 if (r==-1) return -1;
90 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
91 if (r==-1) return -1;
92 return 0;
93}
94
8a5e5147 95void quitnow(int exitstatus) {
83e1be2f 96 nonblock(0,0);
97 nonblock(1,0);
98 exit(exitstatus);
99}
790cfd2b 100
101static void sysfail(const char *what) NONRETURNING;
102static void sysfail(const char *what) {
103 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
8a5e5147 104 quitnow(2);
83e1be2f 105}
106
107static void *xmalloc(size_t sz) {
108 void *r;
109 r= malloc(sz); if (r) return r;
110 sysfail("malloc");
790cfd2b 111}
112
113static void outputerr(void) NONRETURNING;
114static void outputerr(void) { sysfail("write to stdout"); }
115
116static void usage(void) {
117 if (printf("usage: adnsresfilter [<options ...>]\n"
8a5e5147 118 " adnsresfilter -h|--help | --version\n"
3d89679a 119 "options: -t<milliseconds>|--timeout <milliseconds>\n"
120 " -w|--wait (always wait for queries to time out or fail)\n"
121 " -b|--brackets (require [...] around IP addresses)\n"
122 " -a|--address (always include [address] in output)\n"
123 " -u|--unchecked (do not forward map for checking)\n"
ae0da6f3 124 " --config <text> (use this instead of resolv.conf)\n"
125 " --debug (turn on adns resolver debugging)\n"
3d89679a 126 "Timeout is the maximum amount to delay any particular bit of output for.\n"
ae0da6f3 127 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
790cfd2b 128 == EOF) outputerr();
d024926c 129 if (fflush(stdout)) sysfail("flush stdout");
790cfd2b 130}
131
132static void usageerr(const char *why) NONRETURNING;
133static void usageerr(const char *why) {
134 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
135 usage();
8a5e5147 136 quitnow(1);
790cfd2b 137}
138
139static void adnsfail(const char *what, int e) NONRETURNING;
140static void adnsfail(const char *what, int e) {
141 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
8a5e5147 142 quitnow(2);
790cfd2b 143}
144
7ba81901 145static void settimeout(const char *arg) {
146 char *ep;
147 timeout= strtoul(arg,&ep,0);
148 if (*ep) usageerr("invalid timeout");
790cfd2b 149}
150
83e1be2f 151static void parseargs(const char *const *argv) {
790cfd2b 152 const char *arg;
83e1be2f 153 int c;
790cfd2b 154
155 while ((arg= *++argv)) {
156 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
157 if (arg[1] == '-') {
ae0da6f3 158 if (!strcmp(arg,"--timeout")) {
159 if (!(arg= *++argv)) usageerr("--timeout needs a value");
160 settimeout(arg);
161 forever= 0;
790cfd2b 162 } else if (!strcmp(arg,"--wait")) {
163 forever= 1;
ae0da6f3 164 } else if (!strcmp(arg,"--brackets")) {
165 bracket= 1;
a93e4dbf 166 } else if (!strcmp(arg,"--address")) {
167 address= 1;
ae0da6f3 168 } else if (!strcmp(arg,"--unchecked")) {
169 rrt= adns_r_ptr_raw;
170 } else if (!strcmp(arg,"--config")) {
171 if (!(arg= *++argv)) usageerr("--config needs a value");
172 config_text= arg;
173 } else if (!strcmp(arg,"--debug")) {
174 initflags |= adns_if_debug;
790cfd2b 175 } else if (!strcmp(arg,"--help")) {
8a5e5147 176 usage(); quitnow(0);
177 } else if (!strcmp(arg,"--version")) {
178 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0);
790cfd2b 179 } else {
180 usageerr("unknown long option");
181 }
182 } else {
183 while ((c= *++arg)) {
184 switch (c) {
ae0da6f3 185 case 't':
186 if (*++arg) settimeout(arg);
187 else if ((arg= *++argv)) settimeout(arg);
188 else usageerr("-t needs a value");
189 forever= 0;
190 arg= "\0";
790cfd2b 191 break;
192 case 'w':
193 forever= 1;
194 break;
ae0da6f3 195 case 'b':
196 bracket= 1;
197 break;
a93e4dbf 198 case 'a':
199 address= 1;
200 break;
ae0da6f3 201 case 'u':
202 rrt= adns_r_ptr_raw;
203 break;
790cfd2b 204 case 'h':
7ba81901 205 usage();
8a5e5147 206 quitnow(0);
790cfd2b 207 default:
208 usageerr("unknown short option");
209 }
210 }
211 }
212 }
83e1be2f 213}
790cfd2b 214
83e1be2f 215static void queueoutchar(int c) {
216 struct outqueuenode *entry;
217
218 entry= outqueue.tail;
7ba81901 219 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
220 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
221 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 222 entry= xmalloc(sizeof(*entry));
83e1be2f 223 entry->buffer= xmalloc(peroutqueuenode);
224 entry->textp= entry->buffer;
225 entry->textlen= 0;
226 entry->addr= 0;
227 LIST_LINK_TAIL(outqueue,entry);
228 outqueuelen++;
229 }
7ba81901 230 entry->textp[entry->textlen++]= c;
83e1be2f 231}
790cfd2b 232
83e1be2f 233static void queueoutstr(const char *str, int len) {
7ba81901 234 while (len-- > 0) queueoutchar(*str++);
83e1be2f 235}
790cfd2b 236
83e1be2f 237static void writestdout(struct outqueuenode *entry) {
238 int r;
239
240 while (entry->textlen) {
241 r= write(1, entry->textp, entry->textlen);
242 if (r < 0) {
243 if (errno == EINTR) continue;
244 if (errno == EAGAIN) { outblocked= 1; break; }
245 sysfail("write stdout");
246 }
7ba81901 247 assert(r <= entry->textlen);
83e1be2f 248 entry->textp += r;
249 entry->textlen -= r;
250 }
251 if (!entry->textlen) {
252 LIST_UNLINK(outqueue,entry);
253 free(entry->buffer);
254 free(entry);
255 outqueuelen--;
256 }
257}
258
259static void replacetextwithname(struct outqueuenode *entry) {
a93e4dbf 260 char *name, *newbuf;
261 int namelen, newlen;
262
263 name= entry->addr->ans->rrs.str[0];
264 namelen= strlen(name);
265 if (!address) {
266 free(entry->buffer);
267 entry->buffer= 0;
268 entry->textp= name;
269 entry->textlen= namelen;
270 } else {
271 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
272 newbuf= xmalloc(newlen + 1);
273 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
274 free(entry->buffer);
275 entry->buffer= entry->textp= newbuf;
276 entry->textlen= newlen;
277 }
83e1be2f 278}
279
280static void checkadnsqueries(void) {
281 adns_query qu;
282 adns_answer *ans;
283 void *context;
284 struct treething *foundthing;
285 int r;
286
287 for (;;) {
288 qu= 0; context= 0; ans= 0;
289 r= adns_check(ads,&qu,&ans,&context);
290 if (r == ESRCH || r == EAGAIN) break;
291 assert(!r);
292 foundthing= context;
293 foundthing->ans= ans;
294 foundthing->qu= 0;
295 }
296}
297
298static void restartbuf(void) {
299 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
790cfd2b 300 inbuf= 0;
83e1be2f 301}
302
7ba81901 303static int comparer(const void *a, const void *b) {
304 return memcmp(a,b,4);
305}
306
83e1be2f 307static void procaddr(void) {
308 struct treething *foundthing;
309 void **searchfound;
310 struct outqueuenode *entry;
311 int r;
312
313 if (!newthing) {
314 newthing= xmalloc(sizeof(struct treething));
315 newthing->qu= 0;
316 newthing->ans= 0;
317 }
318
319 memcpy(newthing->bytes,bytes,4);
320 searchfound= tsearch(newthing,&treeroot,comparer);
321 if (!searchfound) sysfail("tsearch");
322 foundthing= *searchfound;
323
324 if (foundthing == newthing) {
7ba81901 325 newthing= 0;
83e1be2f 326 memcpy(&sa.sin_addr,bytes,4);
327 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
328 rrt,0,foundthing,&foundthing->qu);
329 if (r) adnsfail("submit",r);
330 }
331 entry= xmalloc(sizeof(*entry));
332 entry->buffer= xmalloc(inbuf);
333 entry->textp= entry->buffer;
334 memcpy(entry->textp,addrtextbuf,inbuf);
335 entry->textlen= inbuf;
336 entry->addr= foundthing;
7ba81901 337 entry->printbefore= printbefore;
83e1be2f 338 LIST_LINK_TAIL(outqueue,entry);
339 outqueuelen++;
340 inbuf= 0;
341 cbyte= -1;
342}
343
344static void startaddr(void) {
345 bytes[cbyte=0]= 0;
346 inbyte= 0;
347}
348
349static void readstdin(void) {
350 char readbuf[512], *p;
351 int r, c, nbyte;
352
353 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
354 if (r == 0) { inputeof= 1; return; }
355 if (r == EAGAIN) return;
356 if (r != EINTR) sysfail("read stdin");
357 }
358 for (p=readbuf; r>0; r--,p++) {
7ba81901 359 c= *p;
790cfd2b 360 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 361 addrtextbuf[inbuf++]= c;
790cfd2b 362 startaddr();
363 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 364 queueoutchar(c);
790cfd2b 365 startaddr();
366 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
367 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
368 bytes[cbyte]= nbyte;
83e1be2f 369 addrtextbuf[inbuf++]= c;
790cfd2b 370 inbyte++;
371 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
372 bytes[++cbyte]= 0;
83e1be2f 373 addrtextbuf[inbuf++]= c;
790cfd2b 374 inbyte= 0;
375 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 376 addrtextbuf[inbuf++]= c;
790cfd2b 377 procaddr();
378 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
379 procaddr();
83e1be2f 380 queueoutchar(c);
790cfd2b 381 startaddr();
382 } else {
383 restartbuf();
83e1be2f 384 queueoutchar(c);
790cfd2b 385 cbyte= -1;
386 if (!bracket && !isalnum(c)) startaddr();
387 }
388 }
83e1be2f 389}
390
391static void startup(void) {
392 int r;
393
83e1be2f 394 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
395 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
396 memset(&sa,0,sizeof(sa));
397 sa.sin_family= AF_INET;
ae0da6f3 398 if (config_text) {
399 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
400 } else {
401 r= adns_init(&ads,initflags,0);
402 }
403 if (r) adnsfail("init",r);
83e1be2f 404 cbyte= -1;
405 inbyte= -1;
406 inbuf= 0;
407 if (!bracket) startaddr();
408}
409
410int main(int argc, const char *const *argv) {
411 int r, maxfd;
412 fd_set readfds, writefds, exceptfds;
413 struct outqueuenode *entry;
414 struct timeval *tv, tvbuf, now;
415
416 parseargs(argv);
417 startup();
418
419 while (!inputeof || outqueue.head) {
420 maxfd= 2;
421 tv= 0;
422 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
423 if ((entry= outqueue.head) && !outblocked) {
424 if (!entry->addr) {
425 writestdout(entry);
426 continue;
7ba81901 427 }
428 if (entry->addr->ans) {
83e1be2f 429 if (entry->addr->ans->nrrs)
430 replacetextwithname(entry);
431 entry->addr= 0;
432 continue;
433 }
434 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
435 if (forever) {
436 tv= 0;
437 } else if (!timercmp(&now,&entry->printbefore,<)) {
438 entry->addr= 0;
439 continue;
440 } else {
7ba81901 441 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
442 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 443 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
444 tvbuf.tv_usec %= 1000000;
445 tv= &tvbuf;
446 }
447 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
448 &tv,&tvbuf,&now);
449 }
450 if (outblocked) FD_SET(1,&writefds);
7ba81901 451 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 452
453 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
454 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
455
456 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
457 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
458 checkadnsqueries();
459
460 if (FD_ISSET(0,&readfds)) {
461 if (!forever) {
462 printbefore= now;
463 timevaladd(&printbefore,timeout);
464 }
465 readstdin();
466 } else if (FD_ISSET(1,&writefds)) {
467 outblocked= 0;
468 }
469 }
83e1be2f 470 if (nonblock(0,0)) sysfail("un-nonblock stdin");
471 if (nonblock(1,0)) sysfail("un-nonblock stdout");
d024926c 472 adns_finish(ads);
790cfd2b 473 exit(0);
474}