chiark / gitweb /
@@ -1,10 +1,24 @@
[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
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
a93e4dbf 54static int bracket, forever, address;
9463e0a6 55static unsigned long timeout= 1000;
83e1be2f 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}
790cfd2b 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));
83e1be2f 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");
790cfd2b 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"
3d89679a 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")
790cfd2b 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();
83e1be2f 128 quit(1);
790cfd2b 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));
83e1be2f 134 quit(2);
790cfd2b 135}
136
7ba81901 137static void settimeout(const char *arg) {
138 char *ep;
139 timeout= strtoul(arg,&ep,0);
140 if (*ep) usageerr("invalid timeout");
790cfd2b 141}
142
83e1be2f 143static void parseargs(const char *const *argv) {
790cfd2b 144 const char *arg;
83e1be2f 145 int c;
790cfd2b 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;
a93e4dbf 156 } else if (!strcmp(arg,"--address")) {
157 address= 1;
790cfd2b 158 } else if (!strcmp(arg,"--help")) {
83e1be2f 159 usage(); quit(0);
7ba81901 160 } else if (!strcmp(arg,"--timeout")) {
161 if (!(arg= *++argv)) usageerr("--timeout needs a value");
162 settimeout(arg);
163 forever= 0;
790cfd2b 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;
a93e4dbf 179 case 'a':
180 address= 1;
181 break;
790cfd2b 182 case 'h':
7ba81901 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;
790cfd2b 192 default:
193 usageerr("unknown short option");
194 }
195 }
196 }
197 }
83e1be2f 198}
790cfd2b 199
83e1be2f 200static void queueoutchar(int c) {
201 struct outqueuenode *entry;
202
203 entry= outqueue.tail;
7ba81901 204 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
205 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
206 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 207 entry= xmalloc(sizeof(*entry));
83e1be2f 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 }
7ba81901 215 entry->textp[entry->textlen++]= c;
83e1be2f 216}
790cfd2b 217
83e1be2f 218static void queueoutstr(const char *str, int len) {
7ba81901 219 while (len-- > 0) queueoutchar(*str++);
83e1be2f 220}
790cfd2b 221
83e1be2f 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 }
7ba81901 232 assert(r <= entry->textlen);
83e1be2f 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) {
a93e4dbf 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 }
83e1be2f 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);
790cfd2b 285 inbuf= 0;
83e1be2f 286}
287
7ba81901 288static int comparer(const void *a, const void *b) {
289 return memcmp(a,b,4);
290}
291
83e1be2f 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) {
7ba81901 310 newthing= 0;
83e1be2f 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;
7ba81901 322 entry->printbefore= printbefore;
83e1be2f 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++) {
7ba81901 344 c= *p;
790cfd2b 345 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 346 addrtextbuf[inbuf++]= c;
790cfd2b 347 startaddr();
348 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 349 queueoutchar(c);
790cfd2b 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;
83e1be2f 354 addrtextbuf[inbuf++]= c;
790cfd2b 355 inbyte++;
356 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
357 bytes[++cbyte]= 0;
83e1be2f 358 addrtextbuf[inbuf++]= c;
790cfd2b 359 inbyte= 0;
360 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 361 addrtextbuf[inbuf++]= c;
790cfd2b 362 procaddr();
363 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
364 procaddr();
83e1be2f 365 queueoutchar(c);
790cfd2b 366 startaddr();
367 } else {
368 restartbuf();
83e1be2f 369 queueoutchar(c);
790cfd2b 370 cbyte= -1;
371 if (!bracket && !isalnum(c)) startaddr();
372 }
373 }
83e1be2f 374}
375
376static void startup(void) {
377 int r;
378
83e1be2f 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;
7ba81901 407 }
408 if (entry->addr->ans) {
83e1be2f 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 {
7ba81901 421 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
422 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 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);
7ba81901 431 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 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 }
83e1be2f 450 if (nonblock(0,0)) sysfail("un-nonblock stdin");
451 if (nonblock(1,0)) sysfail("un-nonblock stdout");
7ba81901 452 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
453 if (fclose(stdout)) sysfail("close stdout");
790cfd2b 454 exit(0);
455}