update copyright dates and ADNS_VERSION_STRING
[adns] / client / adnsresfilter.c
CommitLineData
592aa664 1/*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5/*
6 * This file is
5a0be244 7 * Copyright (C) 1999-2000 Ian Jackson <ian@davenant.greenend.org.uk>
592aa664 8 *
9 * It is part of adns, which is
3d5cde09 10 * Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
bef232ae 11 * Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
592aa664 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
bbede9a3 36#include <sys/types.h>
37#include <unistd.h>
0a4fd9ed 38#include <fcntl.h>
2953d358 39
592aa664 40#include "config.h"
5a0be244 41#include "adns.h"
2953d358 42#include "dlist.h"
43#include "tvarith.h"
aa3ffb57 44#include "client.h"
2953d358 45
98e3fa81 46#ifdef ADNS_REGRESS_TEST
47# include "hredirect.h"
48#endif
49
2953d358 50struct outqueuenode {
51 struct outqueuenode *next, *back;
52 void *buffer;
53 char *textp;
54 int textlen;
55 struct timeval printbefore;
56 struct treething *addr;
57};
58
fd60df66 59static int bracket, forever, address;
51cc4d90 60static unsigned long timeout= 1000;
2953d358 61static adns_rrtype rrt= adns_r_ptr;
98e3fa81 62static adns_initflags initflags= 0;
63static const char *config_text;
2953d358 64
65static int outblocked, inputeof;
66static struct { struct outqueuenode *head, *tail; } outqueue;
67static int peroutqueuenode, outqueuelen;
68
69static struct sockaddr_in sa;
70static adns_state ads;
71
72static char addrtextbuf[14];
73static int cbyte, inbyte, inbuf;
74static unsigned char bytes[4];
75static struct timeval printbefore;
76
77struct treething {
78 unsigned char bytes[4];
79 adns_query qu;
80 adns_answer *ans;
81};
82
83static struct treething *newthing;
84static void *treeroot;
85
86static int nonblock(int fd, int isnonblock) {
87 int r;
88
89 r= fcntl(fd,F_GETFL);
90 if (r==-1) return -1;
91 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
92 if (r==-1) return -1;
93 return 0;
94}
95
aa3ffb57 96void quitnow(int exitstatus) {
2953d358 97 nonblock(0,0);
98 nonblock(1,0);
99 exit(exitstatus);
100}
592aa664 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));
aa3ffb57 105 quitnow(2);
2953d358 106}
107
108static void *xmalloc(size_t sz) {
109 void *r;
110 r= malloc(sz); if (r) return r;
111 sysfail("malloc");
592aa664 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"
aa3ffb57 119 " adnsresfilter -h|--help | --version\n"
70da58ac 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"
98e3fa81 125 " --config <text> (use this instead of resolv.conf)\n"
126 " --debug (turn on adns resolver debugging)\n"
70da58ac 127 "Timeout is the maximum amount to delay any particular bit of output for.\n"
98e3fa81 128 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
592aa664 129 == EOF) outputerr();
67a57aae 130 if (fflush(stdout)) sysfail("flush stdout");
592aa664 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();
aa3ffb57 137 quitnow(1);
592aa664 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));
aa3ffb57 143 quitnow(2);
592aa664 144}
145
41559f53 146static void settimeout(const char *arg) {
147 char *ep;
148 timeout= strtoul(arg,&ep,0);
149 if (*ep) usageerr("invalid timeout");
592aa664 150}
151
2953d358 152static void parseargs(const char *const *argv) {
592aa664 153 const char *arg;
2953d358 154 int c;
592aa664 155
156 while ((arg= *++argv)) {
157 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
158 if (arg[1] == '-') {
98e3fa81 159 if (!strcmp(arg,"--timeout")) {
160 if (!(arg= *++argv)) usageerr("--timeout needs a value");
161 settimeout(arg);
162 forever= 0;
592aa664 163 } else if (!strcmp(arg,"--wait")) {
164 forever= 1;
98e3fa81 165 } else if (!strcmp(arg,"--brackets")) {
166 bracket= 1;
fd60df66 167 } else if (!strcmp(arg,"--address")) {
168 address= 1;
98e3fa81 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;
592aa664 176 } else if (!strcmp(arg,"--help")) {
aa3ffb57 177 usage(); quitnow(0);
178 } else if (!strcmp(arg,"--version")) {
179 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0);
592aa664 180 } else {
181 usageerr("unknown long option");
182 }
183 } else {
184 while ((c= *++arg)) {
185 switch (c) {
98e3fa81 186 case 't':
187 if (*++arg) settimeout(arg);
188 else if ((arg= *++argv)) settimeout(arg);
189 else usageerr("-t needs a value");
190 forever= 0;
191 arg= "\0";
592aa664 192 break;
193 case 'w':
194 forever= 1;
195 break;
98e3fa81 196 case 'b':
197 bracket= 1;
198 break;
fd60df66 199 case 'a':
200 address= 1;
201 break;
98e3fa81 202 case 'u':
203 rrt= adns_r_ptr_raw;
204 break;
592aa664 205 case 'h':
41559f53 206 usage();
aa3ffb57 207 quitnow(0);
592aa664 208 default:
209 usageerr("unknown short option");
210 }
211 }
212 }
213 }
2953d358 214}
592aa664 215
2953d358 216static void queueoutchar(int c) {
217 struct outqueuenode *entry;
218
219 entry= outqueue.tail;
41559f53 220 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
221 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
222 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
2953d358 223 entry= xmalloc(sizeof(*entry));
2953d358 224 entry->buffer= xmalloc(peroutqueuenode);
225 entry->textp= entry->buffer;
226 entry->textlen= 0;
227 entry->addr= 0;
228 LIST_LINK_TAIL(outqueue,entry);
229 outqueuelen++;
230 }
41559f53 231 entry->textp[entry->textlen++]= c;
2953d358 232}
592aa664 233
2953d358 234static void queueoutstr(const char *str, int len) {
41559f53 235 while (len-- > 0) queueoutchar(*str++);
2953d358 236}
592aa664 237
2953d358 238static void writestdout(struct outqueuenode *entry) {
239 int r;
240
241 while (entry->textlen) {
242 r= write(1, entry->textp, entry->textlen);
243 if (r < 0) {
244 if (errno == EINTR) continue;
245 if (errno == EAGAIN) { outblocked= 1; break; }
246 sysfail("write stdout");
247 }
41559f53 248 assert(r <= entry->textlen);
2953d358 249 entry->textp += r;
250 entry->textlen -= r;
251 }
252 if (!entry->textlen) {
253 LIST_UNLINK(outqueue,entry);
254 free(entry->buffer);
255 free(entry);
256 outqueuelen--;
257 }
258}
259
260static void replacetextwithname(struct outqueuenode *entry) {
fd60df66 261 char *name, *newbuf;
262 int namelen, newlen;
263
264 name= entry->addr->ans->rrs.str[0];
265 namelen= strlen(name);
266 if (!address) {
267 free(entry->buffer);
268 entry->buffer= 0;
269 entry->textp= name;
270 entry->textlen= namelen;
271 } else {
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);
275 free(entry->buffer);
276 entry->buffer= entry->textp= newbuf;
277 entry->textlen= newlen;
278 }
2953d358 279}
280
281static void checkadnsqueries(void) {
282 adns_query qu;
283 adns_answer *ans;
284 void *context;
285 struct treething *foundthing;
286 int r;
287
288 for (;;) {
289 qu= 0; context= 0; ans= 0;
290 r= adns_check(ads,&qu,&ans,&context);
291 if (r == ESRCH || r == EAGAIN) break;
292 assert(!r);
293 foundthing= context;
294 foundthing->ans= ans;
295 foundthing->qu= 0;
296 }
297}
298
299static void restartbuf(void) {
300 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
592aa664 301 inbuf= 0;
2953d358 302}
303
41559f53 304static int comparer(const void *a, const void *b) {
305 return memcmp(a,b,4);
306}
307
2953d358 308static void procaddr(void) {
309 struct treething *foundthing;
310 void **searchfound;
311 struct outqueuenode *entry;
312 int r;
313
314 if (!newthing) {
315 newthing= xmalloc(sizeof(struct treething));
316 newthing->qu= 0;
317 newthing->ans= 0;
318 }
319
320 memcpy(newthing->bytes,bytes,4);
321 searchfound= tsearch(newthing,&treeroot,comparer);
322 if (!searchfound) sysfail("tsearch");
323 foundthing= *searchfound;
324
325 if (foundthing == newthing) {
41559f53 326 newthing= 0;
2953d358 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);
331 }
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;
41559f53 338 entry->printbefore= printbefore;
2953d358 339 LIST_LINK_TAIL(outqueue,entry);
340 outqueuelen++;
341 inbuf= 0;
342 cbyte= -1;
343}
344
345static void startaddr(void) {
346 bytes[cbyte=0]= 0;
347 inbyte= 0;
348}
349
350static void readstdin(void) {
351 char readbuf[512], *p;
352 int r, c, nbyte;
353
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");
358 }
359 for (p=readbuf; r>0; r--,p++) {
41559f53 360 c= *p;
592aa664 361 if (cbyte==-1 && bracket && c=='[') {
2953d358 362 addrtextbuf[inbuf++]= c;
592aa664 363 startaddr();
364 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
2953d358 365 queueoutchar(c);
592aa664 366 startaddr();
367 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
368 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
369 bytes[cbyte]= nbyte;
2953d358 370 addrtextbuf[inbuf++]= c;
592aa664 371 inbyte++;
372 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
373 bytes[++cbyte]= 0;
2953d358 374 addrtextbuf[inbuf++]= c;
592aa664 375 inbyte= 0;
376 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
2953d358 377 addrtextbuf[inbuf++]= c;
592aa664 378 procaddr();
379 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
380 procaddr();
2953d358 381 queueoutchar(c);
592aa664 382 startaddr();
383 } else {
384 restartbuf();
2953d358 385 queueoutchar(c);
592aa664 386 cbyte= -1;
387 if (!bracket && !isalnum(c)) startaddr();
388 }
389 }
2953d358 390}
391
392static void startup(void) {
393 int r;
394
2953d358 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;
98e3fa81 399 if (config_text) {
400 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
401 } else {
402 r= adns_init(&ads,initflags,0);
403 }
404 if (r) adnsfail("init",r);
2953d358 405 cbyte= -1;
406 inbyte= -1;
407 inbuf= 0;
408 if (!bracket) startaddr();
409}
410
411int main(int argc, const char *const *argv) {
412 int r, maxfd;
413 fd_set readfds, writefds, exceptfds;
414 struct outqueuenode *entry;
415 struct timeval *tv, tvbuf, now;
416
417 parseargs(argv);
418 startup();
419
420 while (!inputeof || outqueue.head) {
421 maxfd= 2;
422 tv= 0;
423 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
424 if ((entry= outqueue.head) && !outblocked) {
425 if (!entry->addr) {
426 writestdout(entry);
427 continue;
41559f53 428 }
429 if (entry->addr->ans) {
2953d358 430 if (entry->addr->ans->nrrs)
431 replacetextwithname(entry);
432 entry->addr= 0;
433 continue;
434 }
435 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
436 if (forever) {
437 tv= 0;
438 } else if (!timercmp(&now,&entry->printbefore,<)) {
439 entry->addr= 0;
440 continue;
441 } else {
41559f53 442 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
443 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
2953d358 444 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
445 tvbuf.tv_usec %= 1000000;
446 tv= &tvbuf;
447 }
448 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
449 &tv,&tvbuf,&now);
450 }
451 if (outblocked) FD_SET(1,&writefds);
41559f53 452 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
2953d358 453
454 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
455 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
456
457 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
458 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
459 checkadnsqueries();
460
461 if (FD_ISSET(0,&readfds)) {
462 if (!forever) {
463 printbefore= now;
464 timevaladd(&printbefore,timeout);
465 }
466 readstdin();
467 } else if (FD_ISSET(1,&writefds)) {
468 outblocked= 0;
469 }
470 }
2953d358 471 if (nonblock(0,0)) sysfail("un-nonblock stdin");
472 if (nonblock(1,0)) sysfail("un-nonblock stdout");
67a57aae 473 adns_finish(ads);
592aa664 474 exit(0);
475}