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