Update copyright dates and documentation.
[adns] / client / adnsresfilter.c
CommitLineData
790cfd2b 1/*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5/*
6 * This file is
cc0f95d0 7 * Copyright (C) 1999-2000 Ian Jackson <ian@davenant.greenend.org.uk>
790cfd2b 8 *
9 * It is part of adns, which is
89435c42 10 * Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
c6826df6 11 * Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
790cfd2b 12 *
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)
16 * any later version.
17 *
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.
22 *
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.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <search.h>
33#include <assert.h>
34#include <ctype.h>
35
93b54ac1 36#include <sys/types.h>
37#include <unistd.h>
8a4e090e 38#include <fcntl.h>
83e1be2f 39
790cfd2b 40#include "config.h"
cc0f95d0 41#include "adns.h"
83e1be2f 42#include "dlist.h"
43#include "tvarith.h"
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
95static void quit(int exitstatus) NONRETURNING;
96static void quit(int exitstatus) {
97 nonblock(0,0);
98 nonblock(1,0);
99 exit(exitstatus);
100}
790cfd2b 101
102static void sysfail(const char *what) NONRETURNING;
103static void sysfail(const char *what) {
104 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
83e1be2f 105 quit(2);
106}
107
108static void *xmalloc(size_t sz) {
109 void *r;
110 r= malloc(sz); if (r) return r;
111 sysfail("malloc");
790cfd2b 112}
113
114static void outputerr(void) NONRETURNING;
115static void outputerr(void) { sysfail("write to stdout"); }
116
117static void usage(void) {
118 if (printf("usage: adnsresfilter [<options ...>]\n"
119 " adnsresfilter -h|--help\n"
3d89679a 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"
ae0da6f3 125 " --config <text> (use this instead of resolv.conf)\n"
126 " --debug (turn on adns resolver debugging)\n"
3d89679a 127 "Timeout is the maximum amount to delay any particular bit of output for.\n"
ae0da6f3 128 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
790cfd2b 129 == EOF) outputerr();
d024926c 130 if (fflush(stdout)) sysfail("flush stdout");
790cfd2b 131}
132
133static void usageerr(const char *why) NONRETURNING;
134static void usageerr(const char *why) {
135 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
136 usage();
83e1be2f 137 quit(1);
790cfd2b 138}
139
140static void adnsfail(const char *what, int e) NONRETURNING;
141static void adnsfail(const char *what, int e) {
142 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
83e1be2f 143 quit(2);
790cfd2b 144}
145
7ba81901 146static void settimeout(const char *arg) {
147 char *ep;
148 timeout= strtoul(arg,&ep,0);
149 if (*ep) usageerr("invalid timeout");
790cfd2b 150}
151
83e1be2f 152static void parseargs(const char *const *argv) {
790cfd2b 153 const char *arg;
83e1be2f 154 int c;
790cfd2b 155
156 while ((arg= *++argv)) {
157 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
158 if (arg[1] == '-') {
ae0da6f3 159 if (!strcmp(arg,"--timeout")) {
160 if (!(arg= *++argv)) usageerr("--timeout needs a value");
161 settimeout(arg);
162 forever= 0;
790cfd2b 163 } else if (!strcmp(arg,"--wait")) {
164 forever= 1;
ae0da6f3 165 } else if (!strcmp(arg,"--brackets")) {
166 bracket= 1;
a93e4dbf 167 } else if (!strcmp(arg,"--address")) {
168 address= 1;
ae0da6f3 169 } else if (!strcmp(arg,"--unchecked")) {
170 rrt= adns_r_ptr_raw;
171 } else if (!strcmp(arg,"--config")) {
172 if (!(arg= *++argv)) usageerr("--config needs a value");
173 config_text= arg;
174 } else if (!strcmp(arg,"--debug")) {
175 initflags |= adns_if_debug;
790cfd2b 176 } else if (!strcmp(arg,"--help")) {
83e1be2f 177 usage(); quit(0);
790cfd2b 178 } else {
179 usageerr("unknown long option");
180 }
181 } else {
182 while ((c= *++arg)) {
183 switch (c) {
ae0da6f3 184 case 't':
185 if (*++arg) settimeout(arg);
186 else if ((arg= *++argv)) settimeout(arg);
187 else usageerr("-t needs a value");
188 forever= 0;
189 arg= "\0";
790cfd2b 190 break;
191 case 'w':
192 forever= 1;
193 break;
ae0da6f3 194 case 'b':
195 bracket= 1;
196 break;
a93e4dbf 197 case 'a':
198 address= 1;
199 break;
ae0da6f3 200 case 'u':
201 rrt= adns_r_ptr_raw;
202 break;
790cfd2b 203 case 'h':
7ba81901 204 usage();
205 quit(0);
790cfd2b 206 default:
207 usageerr("unknown short option");
208 }
209 }
210 }
211 }
83e1be2f 212}
790cfd2b 213
83e1be2f 214static void queueoutchar(int c) {
215 struct outqueuenode *entry;
216
217 entry= outqueue.tail;
7ba81901 218 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
219 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
220 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 221 entry= xmalloc(sizeof(*entry));
83e1be2f 222 entry->buffer= xmalloc(peroutqueuenode);
223 entry->textp= entry->buffer;
224 entry->textlen= 0;
225 entry->addr= 0;
226 LIST_LINK_TAIL(outqueue,entry);
227 outqueuelen++;
228 }
7ba81901 229 entry->textp[entry->textlen++]= c;
83e1be2f 230}
790cfd2b 231
83e1be2f 232static void queueoutstr(const char *str, int len) {
7ba81901 233 while (len-- > 0) queueoutchar(*str++);
83e1be2f 234}
790cfd2b 235
83e1be2f 236static void writestdout(struct outqueuenode *entry) {
237 int r;
238
239 while (entry->textlen) {
240 r= write(1, entry->textp, entry->textlen);
241 if (r < 0) {
242 if (errno == EINTR) continue;
243 if (errno == EAGAIN) { outblocked= 1; break; }
244 sysfail("write stdout");
245 }
7ba81901 246 assert(r <= entry->textlen);
83e1be2f 247 entry->textp += r;
248 entry->textlen -= r;
249 }
250 if (!entry->textlen) {
251 LIST_UNLINK(outqueue,entry);
252 free(entry->buffer);
253 free(entry);
254 outqueuelen--;
255 }
256}
257
258static void replacetextwithname(struct outqueuenode *entry) {
a93e4dbf 259 char *name, *newbuf;
260 int namelen, newlen;
261
262 name= entry->addr->ans->rrs.str[0];
263 namelen= strlen(name);
264 if (!address) {
265 free(entry->buffer);
266 entry->buffer= 0;
267 entry->textp= name;
268 entry->textlen= namelen;
269 } else {
270 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
271 newbuf= xmalloc(newlen + 1);
272 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
273 free(entry->buffer);
274 entry->buffer= entry->textp= newbuf;
275 entry->textlen= newlen;
276 }
83e1be2f 277}
278
279static void checkadnsqueries(void) {
280 adns_query qu;
281 adns_answer *ans;
282 void *context;
283 struct treething *foundthing;
284 int r;
285
286 for (;;) {
287 qu= 0; context= 0; ans= 0;
288 r= adns_check(ads,&qu,&ans,&context);
289 if (r == ESRCH || r == EAGAIN) break;
290 assert(!r);
291 foundthing= context;
292 foundthing->ans= ans;
293 foundthing->qu= 0;
294 }
295}
296
297static void restartbuf(void) {
298 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
790cfd2b 299 inbuf= 0;
83e1be2f 300}
301
7ba81901 302static int comparer(const void *a, const void *b) {
303 return memcmp(a,b,4);
304}
305
83e1be2f 306static void procaddr(void) {
307 struct treething *foundthing;
308 void **searchfound;
309 struct outqueuenode *entry;
310 int r;
311
312 if (!newthing) {
313 newthing= xmalloc(sizeof(struct treething));
314 newthing->qu= 0;
315 newthing->ans= 0;
316 }
317
318 memcpy(newthing->bytes,bytes,4);
319 searchfound= tsearch(newthing,&treeroot,comparer);
320 if (!searchfound) sysfail("tsearch");
321 foundthing= *searchfound;
322
323 if (foundthing == newthing) {
7ba81901 324 newthing= 0;
83e1be2f 325 memcpy(&sa.sin_addr,bytes,4);
326 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
327 rrt,0,foundthing,&foundthing->qu);
328 if (r) adnsfail("submit",r);
329 }
330 entry= xmalloc(sizeof(*entry));
331 entry->buffer= xmalloc(inbuf);
332 entry->textp= entry->buffer;
333 memcpy(entry->textp,addrtextbuf,inbuf);
334 entry->textlen= inbuf;
335 entry->addr= foundthing;
7ba81901 336 entry->printbefore= printbefore;
83e1be2f 337 LIST_LINK_TAIL(outqueue,entry);
338 outqueuelen++;
339 inbuf= 0;
340 cbyte= -1;
341}
342
343static void startaddr(void) {
344 bytes[cbyte=0]= 0;
345 inbyte= 0;
346}
347
348static void readstdin(void) {
349 char readbuf[512], *p;
350 int r, c, nbyte;
351
352 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
353 if (r == 0) { inputeof= 1; return; }
354 if (r == EAGAIN) return;
355 if (r != EINTR) sysfail("read stdin");
356 }
357 for (p=readbuf; r>0; r--,p++) {
7ba81901 358 c= *p;
790cfd2b 359 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 360 addrtextbuf[inbuf++]= c;
790cfd2b 361 startaddr();
362 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 363 queueoutchar(c);
790cfd2b 364 startaddr();
365 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
366 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
367 bytes[cbyte]= nbyte;
83e1be2f 368 addrtextbuf[inbuf++]= c;
790cfd2b 369 inbyte++;
370 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
371 bytes[++cbyte]= 0;
83e1be2f 372 addrtextbuf[inbuf++]= c;
790cfd2b 373 inbyte= 0;
374 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 375 addrtextbuf[inbuf++]= c;
790cfd2b 376 procaddr();
377 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
378 procaddr();
83e1be2f 379 queueoutchar(c);
790cfd2b 380 startaddr();
381 } else {
382 restartbuf();
83e1be2f 383 queueoutchar(c);
790cfd2b 384 cbyte= -1;
385 if (!bracket && !isalnum(c)) startaddr();
386 }
387 }
83e1be2f 388}
389
390static void startup(void) {
391 int r;
392
83e1be2f 393 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
394 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
395 memset(&sa,0,sizeof(sa));
396 sa.sin_family= AF_INET;
ae0da6f3 397 if (config_text) {
398 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
399 } else {
400 r= adns_init(&ads,initflags,0);
401 }
402 if (r) adnsfail("init",r);
83e1be2f 403 cbyte= -1;
404 inbyte= -1;
405 inbuf= 0;
406 if (!bracket) startaddr();
407}
408
409int main(int argc, const char *const *argv) {
410 int r, maxfd;
411 fd_set readfds, writefds, exceptfds;
412 struct outqueuenode *entry;
413 struct timeval *tv, tvbuf, now;
414
415 parseargs(argv);
416 startup();
417
418 while (!inputeof || outqueue.head) {
419 maxfd= 2;
420 tv= 0;
421 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
422 if ((entry= outqueue.head) && !outblocked) {
423 if (!entry->addr) {
424 writestdout(entry);
425 continue;
7ba81901 426 }
427 if (entry->addr->ans) {
83e1be2f 428 if (entry->addr->ans->nrrs)
429 replacetextwithname(entry);
430 entry->addr= 0;
431 continue;
432 }
433 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
434 if (forever) {
435 tv= 0;
436 } else if (!timercmp(&now,&entry->printbefore,<)) {
437 entry->addr= 0;
438 continue;
439 } else {
7ba81901 440 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
441 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 442 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
443 tvbuf.tv_usec %= 1000000;
444 tv= &tvbuf;
445 }
446 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
447 &tv,&tvbuf,&now);
448 }
449 if (outblocked) FD_SET(1,&writefds);
7ba81901 450 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 451
452 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
453 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
454
455 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
456 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
457 checkadnsqueries();
458
459 if (FD_ISSET(0,&readfds)) {
460 if (!forever) {
461 printbefore= now;
462 timevaladd(&printbefore,timeout);
463 }
464 readstdin();
465 } else if (FD_ISSET(1,&writefds)) {
466 outblocked= 0;
467 }
468 }
83e1be2f 469 if (nonblock(0,0)) sysfail("un-nonblock stdin");
470 if (nonblock(1,0)) sysfail("un-nonblock stdout");
d024926c 471 adns_finish(ads);
790cfd2b 472 exit(0);
473}