chiark / gitweb /
@@ -3,8 +3,9 @@
[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"
8a5e5147 44#include "client.h"
83e1be2f 45
ae0da6f3 46#ifdef ADNS_REGRESS_TEST
47# include "hredirect.h"
48#endif
49
83e1be2f 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
a93e4dbf 59static int bracket, forever, address;
9463e0a6 60static unsigned long timeout= 1000;
83e1be2f 61static adns_rrtype rrt= adns_r_ptr;
ae0da6f3 62static adns_initflags initflags= 0;
63static const char *config_text;
83e1be2f 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
8a5e5147 96void quitnow(int exitstatus) {
83e1be2f 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));
8a5e5147 105 quitnow(2);
83e1be2f 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"
8a5e5147 119 " adnsresfilter -h|--help | --version\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();
8a5e5147 137 quitnow(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));
8a5e5147 143 quitnow(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")) {
8a5e5147 177 usage(); quitnow(0);
178 } else if (!strcmp(arg,"--version")) {
179 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0);
790cfd2b 180 } else {
181 usageerr("unknown long option");
182 }
183 } else {
184 while ((c= *++arg)) {
185 switch (c) {
ae0da6f3 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";
790cfd2b 192 break;
193 case 'w':
194 forever= 1;
195 break;
ae0da6f3 196 case 'b':
197 bracket= 1;
198 break;
a93e4dbf 199 case 'a':
200 address= 1;
201 break;
ae0da6f3 202 case 'u':
203 rrt= adns_r_ptr_raw;
204 break;
790cfd2b 205 case 'h':
7ba81901 206 usage();
8a5e5147 207 quitnow(0);
790cfd2b 208 default:
209 usageerr("unknown short option");
210 }
211 }
212 }
213 }
83e1be2f 214}
790cfd2b 215
83e1be2f 216static void queueoutchar(int c) {
217 struct outqueuenode *entry;
218
219 entry= outqueue.tail;
7ba81901 220 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
221 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
222 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 223 entry= xmalloc(sizeof(*entry));
83e1be2f 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 }
7ba81901 231 entry->textp[entry->textlen++]= c;
83e1be2f 232}
790cfd2b 233
83e1be2f 234static void queueoutstr(const char *str, int len) {
7ba81901 235 while (len-- > 0) queueoutchar(*str++);
83e1be2f 236}
790cfd2b 237
83e1be2f 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 }
7ba81901 248 assert(r <= entry->textlen);
83e1be2f 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) {
a93e4dbf 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 }
83e1be2f 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);
790cfd2b 301 inbuf= 0;
83e1be2f 302}
303
7ba81901 304static int comparer(const void *a, const void *b) {
305 return memcmp(a,b,4);
306}
307
83e1be2f 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) {
7ba81901 326 newthing= 0;
83e1be2f 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;
7ba81901 338 entry->printbefore= printbefore;
83e1be2f 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++) {
7ba81901 360 c= *p;
790cfd2b 361 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 362 addrtextbuf[inbuf++]= c;
790cfd2b 363 startaddr();
364 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 365 queueoutchar(c);
790cfd2b 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;
83e1be2f 370 addrtextbuf[inbuf++]= c;
790cfd2b 371 inbyte++;
372 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
373 bytes[++cbyte]= 0;
83e1be2f 374 addrtextbuf[inbuf++]= c;
790cfd2b 375 inbyte= 0;
376 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 377 addrtextbuf[inbuf++]= c;
790cfd2b 378 procaddr();
379 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
380 procaddr();
83e1be2f 381 queueoutchar(c);
790cfd2b 382 startaddr();
383 } else {
384 restartbuf();
83e1be2f 385 queueoutchar(c);
790cfd2b 386 cbyte= -1;
387 if (!bracket && !isalnum(c)) startaddr();
388 }
389 }
83e1be2f 390}
391
392static void startup(void) {
393 int r;
394
83e1be2f 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;
ae0da6f3 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);
83e1be2f 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;
7ba81901 428 }
429 if (entry->addr->ans) {
83e1be2f 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 {
7ba81901 442 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
443 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 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);
7ba81901 452 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 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 }
83e1be2f 471 if (nonblock(0,0)) sysfail("un-nonblock stdin");
472 if (nonblock(1,0)) sysfail("un-nonblock stdout");
d024926c 473 adns_finish(ads);
790cfd2b 474 exit(0);
475}