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