adnslogres new -C option
[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>
592aa664 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
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"
44
45struct outqueuenode {
46 struct outqueuenode *next, *back;
47 void *buffer;
48 char *textp;
49 int textlen;
50 struct timeval printbefore;
51 struct treething *addr;
52};
53
fd60df66 54static int bracket, forever, address;
51cc4d90 55static unsigned long timeout= 1000;
2953d358 56static adns_rrtype rrt= adns_r_ptr;
57
58static int outblocked, inputeof;
59static struct { struct outqueuenode *head, *tail; } outqueue;
60static int peroutqueuenode, outqueuelen;
61
62static struct sockaddr_in sa;
63static adns_state ads;
64
65static char addrtextbuf[14];
66static int cbyte, inbyte, inbuf;
67static unsigned char bytes[4];
68static struct timeval printbefore;
69
70struct treething {
71 unsigned char bytes[4];
72 adns_query qu;
73 adns_answer *ans;
74};
75
76static struct treething *newthing;
77static void *treeroot;
78
79static int nonblock(int fd, int isnonblock) {
80 int r;
81
82 r= fcntl(fd,F_GETFL);
83 if (r==-1) return -1;
84 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
85 if (r==-1) return -1;
86 return 0;
87}
88
89static void quit(int exitstatus) NONRETURNING;
90static void quit(int exitstatus) {
91 nonblock(0,0);
92 nonblock(1,0);
93 exit(exitstatus);
94}
592aa664 95
96static void sysfail(const char *what) NONRETURNING;
97static void sysfail(const char *what) {
98 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
2953d358 99 quit(2);
100}
101
102static void *xmalloc(size_t sz) {
103 void *r;
104 r= malloc(sz); if (r) return r;
105 sysfail("malloc");
592aa664 106}
107
108static void outputerr(void) NONRETURNING;
109static void outputerr(void) { sysfail("write to stdout"); }
110
111static void usage(void) {
112 if (printf("usage: adnsresfilter [<options ...>]\n"
113 " adnsresfilter -h|--help\n"
70da58ac 114 "options: -t<milliseconds>|--timeout <milliseconds>\n"
115 " -w|--wait (always wait for queries to time out or fail)\n"
116 " -b|--brackets (require [...] around IP addresses)\n"
117 " -a|--address (always include [address] in output)\n"
118 " -u|--unchecked (do not forward map for checking)\n"
119 "Timeout is the maximum amount to delay any particular bit of output for.\n"
120 "Lookups will go on in the background. Default timeout = 100 (ms).\n")
592aa664 121 == EOF) outputerr();
122}
123
124static void usageerr(const char *why) NONRETURNING;
125static void usageerr(const char *why) {
126 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
127 usage();
2953d358 128 quit(1);
592aa664 129}
130
131static void adnsfail(const char *what, int e) NONRETURNING;
132static void adnsfail(const char *what, int e) {
133 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
2953d358 134 quit(2);
592aa664 135}
136
41559f53 137static void settimeout(const char *arg) {
138 char *ep;
139 timeout= strtoul(arg,&ep,0);
140 if (*ep) usageerr("invalid timeout");
592aa664 141}
142
2953d358 143static void parseargs(const char *const *argv) {
592aa664 144 const char *arg;
2953d358 145 int c;
592aa664 146
147 while ((arg= *++argv)) {
148 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
149 if (arg[1] == '-') {
150 if (!strcmp(arg,"--brackets")) {
151 bracket= 1;
152 } else if (!strcmp(arg,"--unchecked")) {
153 rrt= adns_r_ptr_raw;
154 } else if (!strcmp(arg,"--wait")) {
155 forever= 1;
fd60df66 156 } else if (!strcmp(arg,"--address")) {
157 address= 1;
592aa664 158 } else if (!strcmp(arg,"--help")) {
2953d358 159 usage(); quit(0);
41559f53 160 } else if (!strcmp(arg,"--timeout")) {
161 if (!(arg= *++argv)) usageerr("--timeout needs a value");
162 settimeout(arg);
163 forever= 0;
592aa664 164 } else {
165 usageerr("unknown long option");
166 }
167 } else {
168 while ((c= *++arg)) {
169 switch (c) {
170 case 'b':
171 bracket= 1;
172 break;
173 case 'u':
174 rrt= adns_r_ptr_raw;
175 break;
176 case 'w':
177 forever= 1;
178 break;
fd60df66 179 case 'a':
180 address= 1;
181 break;
592aa664 182 case 'h':
41559f53 183 usage();
184 quit(0);
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";
191 break;
592aa664 192 default:
193 usageerr("unknown short option");
194 }
195 }
196 }
197 }
2953d358 198}
592aa664 199
2953d358 200static void queueoutchar(int c) {
201 struct outqueuenode *entry;
202
203 entry= outqueue.tail;
41559f53 204 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
205 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
206 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
2953d358 207 entry= xmalloc(sizeof(*entry));
2953d358 208 entry->buffer= xmalloc(peroutqueuenode);
209 entry->textp= entry->buffer;
210 entry->textlen= 0;
211 entry->addr= 0;
212 LIST_LINK_TAIL(outqueue,entry);
213 outqueuelen++;
214 }
41559f53 215 entry->textp[entry->textlen++]= c;
2953d358 216}
592aa664 217
2953d358 218static void queueoutstr(const char *str, int len) {
41559f53 219 while (len-- > 0) queueoutchar(*str++);
2953d358 220}
592aa664 221
2953d358 222static void writestdout(struct outqueuenode *entry) {
223 int r;
224
225 while (entry->textlen) {
226 r= write(1, entry->textp, entry->textlen);
227 if (r < 0) {
228 if (errno == EINTR) continue;
229 if (errno == EAGAIN) { outblocked= 1; break; }
230 sysfail("write stdout");
231 }
41559f53 232 assert(r <= entry->textlen);
2953d358 233 entry->textp += r;
234 entry->textlen -= r;
235 }
236 if (!entry->textlen) {
237 LIST_UNLINK(outqueue,entry);
238 free(entry->buffer);
239 free(entry);
240 outqueuelen--;
241 }
242}
243
244static void replacetextwithname(struct outqueuenode *entry) {
fd60df66 245 char *name, *newbuf;
246 int namelen, newlen;
247
248 name= entry->addr->ans->rrs.str[0];
249 namelen= strlen(name);
250 if (!address) {
251 free(entry->buffer);
252 entry->buffer= 0;
253 entry->textp= name;
254 entry->textlen= namelen;
255 } else {
256 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
257 newbuf= xmalloc(newlen + 1);
258 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
259 free(entry->buffer);
260 entry->buffer= entry->textp= newbuf;
261 entry->textlen= newlen;
262 }
2953d358 263}
264
265static void checkadnsqueries(void) {
266 adns_query qu;
267 adns_answer *ans;
268 void *context;
269 struct treething *foundthing;
270 int r;
271
272 for (;;) {
273 qu= 0; context= 0; ans= 0;
274 r= adns_check(ads,&qu,&ans,&context);
275 if (r == ESRCH || r == EAGAIN) break;
276 assert(!r);
277 foundthing= context;
278 foundthing->ans= ans;
279 foundthing->qu= 0;
280 }
281}
282
283static void restartbuf(void) {
284 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
592aa664 285 inbuf= 0;
2953d358 286}
287
41559f53 288static int comparer(const void *a, const void *b) {
289 return memcmp(a,b,4);
290}
291
2953d358 292static void procaddr(void) {
293 struct treething *foundthing;
294 void **searchfound;
295 struct outqueuenode *entry;
296 int r;
297
298 if (!newthing) {
299 newthing= xmalloc(sizeof(struct treething));
300 newthing->qu= 0;
301 newthing->ans= 0;
302 }
303
304 memcpy(newthing->bytes,bytes,4);
305 searchfound= tsearch(newthing,&treeroot,comparer);
306 if (!searchfound) sysfail("tsearch");
307 foundthing= *searchfound;
308
309 if (foundthing == newthing) {
41559f53 310 newthing= 0;
2953d358 311 memcpy(&sa.sin_addr,bytes,4);
312 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
313 rrt,0,foundthing,&foundthing->qu);
314 if (r) adnsfail("submit",r);
315 }
316 entry= xmalloc(sizeof(*entry));
317 entry->buffer= xmalloc(inbuf);
318 entry->textp= entry->buffer;
319 memcpy(entry->textp,addrtextbuf,inbuf);
320 entry->textlen= inbuf;
321 entry->addr= foundthing;
41559f53 322 entry->printbefore= printbefore;
2953d358 323 LIST_LINK_TAIL(outqueue,entry);
324 outqueuelen++;
325 inbuf= 0;
326 cbyte= -1;
327}
328
329static void startaddr(void) {
330 bytes[cbyte=0]= 0;
331 inbyte= 0;
332}
333
334static void readstdin(void) {
335 char readbuf[512], *p;
336 int r, c, nbyte;
337
338 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
339 if (r == 0) { inputeof= 1; return; }
340 if (r == EAGAIN) return;
341 if (r != EINTR) sysfail("read stdin");
342 }
343 for (p=readbuf; r>0; r--,p++) {
41559f53 344 c= *p;
592aa664 345 if (cbyte==-1 && bracket && c=='[') {
2953d358 346 addrtextbuf[inbuf++]= c;
592aa664 347 startaddr();
348 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
2953d358 349 queueoutchar(c);
592aa664 350 startaddr();
351 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
352 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
353 bytes[cbyte]= nbyte;
2953d358 354 addrtextbuf[inbuf++]= c;
592aa664 355 inbyte++;
356 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
357 bytes[++cbyte]= 0;
2953d358 358 addrtextbuf[inbuf++]= c;
592aa664 359 inbyte= 0;
360 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
2953d358 361 addrtextbuf[inbuf++]= c;
592aa664 362 procaddr();
363 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
364 procaddr();
2953d358 365 queueoutchar(c);
592aa664 366 startaddr();
367 } else {
368 restartbuf();
2953d358 369 queueoutchar(c);
592aa664 370 cbyte= -1;
371 if (!bracket && !isalnum(c)) startaddr();
372 }
373 }
2953d358 374}
375
376static void startup(void) {
377 int r;
378
2953d358 379 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
380 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
381 memset(&sa,0,sizeof(sa));
382 sa.sin_family= AF_INET;
383 r= adns_init(&ads,0,0); if (r) adnsfail("init",r);
384 cbyte= -1;
385 inbyte= -1;
386 inbuf= 0;
387 if (!bracket) startaddr();
388}
389
390int main(int argc, const char *const *argv) {
391 int r, maxfd;
392 fd_set readfds, writefds, exceptfds;
393 struct outqueuenode *entry;
394 struct timeval *tv, tvbuf, now;
395
396 parseargs(argv);
397 startup();
398
399 while (!inputeof || outqueue.head) {
400 maxfd= 2;
401 tv= 0;
402 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
403 if ((entry= outqueue.head) && !outblocked) {
404 if (!entry->addr) {
405 writestdout(entry);
406 continue;
41559f53 407 }
408 if (entry->addr->ans) {
2953d358 409 if (entry->addr->ans->nrrs)
410 replacetextwithname(entry);
411 entry->addr= 0;
412 continue;
413 }
414 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
415 if (forever) {
416 tv= 0;
417 } else if (!timercmp(&now,&entry->printbefore,<)) {
418 entry->addr= 0;
419 continue;
420 } else {
41559f53 421 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
422 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
2953d358 423 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
424 tvbuf.tv_usec %= 1000000;
425 tv= &tvbuf;
426 }
427 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
428 &tv,&tvbuf,&now);
429 }
430 if (outblocked) FD_SET(1,&writefds);
41559f53 431 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
2953d358 432
433 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
434 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
435
436 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
437 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
438 checkadnsqueries();
439
440 if (FD_ISSET(0,&readfds)) {
441 if (!forever) {
442 printbefore= now;
443 timevaladd(&printbefore,timeout);
444 }
445 readstdin();
446 } else if (FD_ISSET(1,&writefds)) {
447 outblocked= 0;
448 }
449 }
2953d358 450 if (nonblock(0,0)) sysfail("un-nonblock stdin");
451 if (nonblock(1,0)) sysfail("un-nonblock stdout");
41559f53 452 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
453 if (fclose(stdout)) sysfail("close stdout");
592aa664 454 exit(0);
455}