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