chiark / gitweb /
@@ -3,9 +3,11 @@
[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>
790cfd2b 11 * Copyright (C) 1999 Tony Finch <dot@dotat.at>
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();
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();
83e1be2f 136 quit(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));
83e1be2f 142 quit(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")) {
83e1be2f 176 usage(); quit(0);
790cfd2b 177 } else {
178 usageerr("unknown long option");
179 }
180 } else {
181 while ((c= *++arg)) {
182 switch (c) {
ae0da6f3 183 case 't':
184 if (*++arg) settimeout(arg);
185 else if ((arg= *++argv)) settimeout(arg);
186 else usageerr("-t needs a value");
187 forever= 0;
188 arg= "\0";
790cfd2b 189 break;
190 case 'w':
191 forever= 1;
192 break;
ae0da6f3 193 case 'b':
194 bracket= 1;
195 break;
a93e4dbf 196 case 'a':
197 address= 1;
198 break;
ae0da6f3 199 case 'u':
200 rrt= adns_r_ptr_raw;
201 break;
790cfd2b 202 case 'h':
7ba81901 203 usage();
204 quit(0);
790cfd2b 205 default:
206 usageerr("unknown short option");
207 }
208 }
209 }
210 }
83e1be2f 211}
790cfd2b 212
83e1be2f 213static void queueoutchar(int c) {
214 struct outqueuenode *entry;
215
216 entry= outqueue.tail;
7ba81901 217 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
218 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
219 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 220 entry= xmalloc(sizeof(*entry));
83e1be2f 221 entry->buffer= xmalloc(peroutqueuenode);
222 entry->textp= entry->buffer;
223 entry->textlen= 0;
224 entry->addr= 0;
225 LIST_LINK_TAIL(outqueue,entry);
226 outqueuelen++;
227 }
7ba81901 228 entry->textp[entry->textlen++]= c;
83e1be2f 229}
790cfd2b 230
83e1be2f 231static void queueoutstr(const char *str, int len) {
7ba81901 232 while (len-- > 0) queueoutchar(*str++);
83e1be2f 233}
790cfd2b 234
83e1be2f 235static void writestdout(struct outqueuenode *entry) {
236 int r;
237
238 while (entry->textlen) {
239 r= write(1, entry->textp, entry->textlen);
240 if (r < 0) {
241 if (errno == EINTR) continue;
242 if (errno == EAGAIN) { outblocked= 1; break; }
243 sysfail("write stdout");
244 }
7ba81901 245 assert(r <= entry->textlen);
83e1be2f 246 entry->textp += r;
247 entry->textlen -= r;
248 }
249 if (!entry->textlen) {
250 LIST_UNLINK(outqueue,entry);
251 free(entry->buffer);
252 free(entry);
253 outqueuelen--;
254 }
255}
256
257static void replacetextwithname(struct outqueuenode *entry) {
a93e4dbf 258 char *name, *newbuf;
259 int namelen, newlen;
260
261 name= entry->addr->ans->rrs.str[0];
262 namelen= strlen(name);
263 if (!address) {
264 free(entry->buffer);
265 entry->buffer= 0;
266 entry->textp= name;
267 entry->textlen= namelen;
268 } else {
269 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
270 newbuf= xmalloc(newlen + 1);
271 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
272 free(entry->buffer);
273 entry->buffer= entry->textp= newbuf;
274 entry->textlen= newlen;
275 }
83e1be2f 276}
277
278static void checkadnsqueries(void) {
279 adns_query qu;
280 adns_answer *ans;
281 void *context;
282 struct treething *foundthing;
283 int r;
284
285 for (;;) {
286 qu= 0; context= 0; ans= 0;
287 r= adns_check(ads,&qu,&ans,&context);
288 if (r == ESRCH || r == EAGAIN) break;
289 assert(!r);
290 foundthing= context;
291 foundthing->ans= ans;
292 foundthing->qu= 0;
293 }
294}
295
296static void restartbuf(void) {
297 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
790cfd2b 298 inbuf= 0;
83e1be2f 299}
300
7ba81901 301static int comparer(const void *a, const void *b) {
302 return memcmp(a,b,4);
303}
304
83e1be2f 305static void procaddr(void) {
306 struct treething *foundthing;
307 void **searchfound;
308 struct outqueuenode *entry;
309 int r;
310
311 if (!newthing) {
312 newthing= xmalloc(sizeof(struct treething));
313 newthing->qu= 0;
314 newthing->ans= 0;
315 }
316
317 memcpy(newthing->bytes,bytes,4);
318 searchfound= tsearch(newthing,&treeroot,comparer);
319 if (!searchfound) sysfail("tsearch");
320 foundthing= *searchfound;
321
322 if (foundthing == newthing) {
7ba81901 323 newthing= 0;
83e1be2f 324 memcpy(&sa.sin_addr,bytes,4);
325 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
326 rrt,0,foundthing,&foundthing->qu);
327 if (r) adnsfail("submit",r);
328 }
329 entry= xmalloc(sizeof(*entry));
330 entry->buffer= xmalloc(inbuf);
331 entry->textp= entry->buffer;
332 memcpy(entry->textp,addrtextbuf,inbuf);
333 entry->textlen= inbuf;
334 entry->addr= foundthing;
7ba81901 335 entry->printbefore= printbefore;
83e1be2f 336 LIST_LINK_TAIL(outqueue,entry);
337 outqueuelen++;
338 inbuf= 0;
339 cbyte= -1;
340}
341
342static void startaddr(void) {
343 bytes[cbyte=0]= 0;
344 inbyte= 0;
345}
346
347static void readstdin(void) {
348 char readbuf[512], *p;
349 int r, c, nbyte;
350
351 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
352 if (r == 0) { inputeof= 1; return; }
353 if (r == EAGAIN) return;
354 if (r != EINTR) sysfail("read stdin");
355 }
356 for (p=readbuf; r>0; r--,p++) {
7ba81901 357 c= *p;
790cfd2b 358 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 359 addrtextbuf[inbuf++]= c;
790cfd2b 360 startaddr();
361 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 362 queueoutchar(c);
790cfd2b 363 startaddr();
364 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
365 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
366 bytes[cbyte]= nbyte;
83e1be2f 367 addrtextbuf[inbuf++]= c;
790cfd2b 368 inbyte++;
369 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
370 bytes[++cbyte]= 0;
83e1be2f 371 addrtextbuf[inbuf++]= c;
790cfd2b 372 inbyte= 0;
373 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 374 addrtextbuf[inbuf++]= c;
790cfd2b 375 procaddr();
376 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
377 procaddr();
83e1be2f 378 queueoutchar(c);
790cfd2b 379 startaddr();
380 } else {
381 restartbuf();
83e1be2f 382 queueoutchar(c);
790cfd2b 383 cbyte= -1;
384 if (!bracket && !isalnum(c)) startaddr();
385 }
386 }
83e1be2f 387}
388
389static void startup(void) {
390 int r;
391
83e1be2f 392 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
393 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
394 memset(&sa,0,sizeof(sa));
395 sa.sin_family= AF_INET;
ae0da6f3 396 if (config_text) {
397 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
398 } else {
399 r= adns_init(&ads,initflags,0);
400 }
401 if (r) adnsfail("init",r);
83e1be2f 402 cbyte= -1;
403 inbyte= -1;
404 inbuf= 0;
405 if (!bracket) startaddr();
406}
407
408int main(int argc, const char *const *argv) {
409 int r, maxfd;
410 fd_set readfds, writefds, exceptfds;
411 struct outqueuenode *entry;
412 struct timeval *tv, tvbuf, now;
413
414 parseargs(argv);
415 startup();
416
417 while (!inputeof || outqueue.head) {
418 maxfd= 2;
419 tv= 0;
420 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
421 if ((entry= outqueue.head) && !outblocked) {
422 if (!entry->addr) {
423 writestdout(entry);
424 continue;
7ba81901 425 }
426 if (entry->addr->ans) {
83e1be2f 427 if (entry->addr->ans->nrrs)
428 replacetextwithname(entry);
429 entry->addr= 0;
430 continue;
431 }
432 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
433 if (forever) {
434 tv= 0;
435 } else if (!timercmp(&now,&entry->printbefore,<)) {
436 entry->addr= 0;
437 continue;
438 } else {
7ba81901 439 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
440 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 441 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
442 tvbuf.tv_usec %= 1000000;
443 tv= &tvbuf;
444 }
445 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
446 &tv,&tvbuf,&now);
447 }
448 if (outblocked) FD_SET(1,&writefds);
7ba81901 449 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 450
451 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
452 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
453
454 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
455 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
456 checkadnsqueries();
457
458 if (FD_ISSET(0,&readfds)) {
459 if (!forever) {
460 printbefore= now;
461 timevaladd(&printbefore,timeout);
462 }
463 readstdin();
464 } else if (FD_ISSET(1,&writefds)) {
465 outblocked= 0;
466 }
467 }
83e1be2f 468 if (nonblock(0,0)) sysfail("un-nonblock stdin");
469 if (nonblock(1,0)) sysfail("un-nonblock stdout");
7ba81901 470 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
471 if (fclose(stdout)) sysfail("close stdout");
790cfd2b 472 exit(0);
473}