chiark / gitweb /
Support IPv6 in addr queries.
[adns.git] / src / setup.c
1 /*
2  * setup.c
3  * - configuration file parsing
4  * - management of global state
5  */
6 /*
7  *  This file is part of adns, which is
8  *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
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 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 <stdlib.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33
34 #include <sys/types.h>
35 #include <netdb.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #include "internal.h"
41
42 static void readconfig(adns_state ads, const char *filename, int warnmissing);
43
44 static void addserver(adns_state ads, const struct sockaddr *sa, int n) {
45   int i;
46   adns_rr_addr *ss;
47   char buf[ADNS_ADDR2TEXT_BUFLEN];
48   
49   for (i=0; i<ads->nservers; i++) {
50     if (adns__sockaddr_equal_p(sa, &ads->servers[i].addr.sa)) {
51       adns__debug(ads,-1,0,"duplicate nameserver %s ignored",
52                   adns__sockaddr_ntoa(sa, buf));
53       return;
54     }
55   }
56   
57   if (ads->nservers>=MAXSERVERS) {
58     adns__diag(ads,-1,0,"too many nameservers, ignoring %s",
59                adns__sockaddr_ntoa(sa, buf));
60     return;
61   }
62
63   ss= ads->servers+ads->nservers;
64   assert(n <= sizeof(ss->addr));
65   ss->len = n;
66   memcpy(&ss->addr, sa, n);
67   ads->nservers++;
68 }
69
70 static void freesearchlist(adns_state ads) {
71   if (ads->nsearchlist) free(*ads->searchlist);
72   free(ads->searchlist);
73 }
74
75 static void saveerr(adns_state ads, int en) {
76   if (!ads->configerrno) ads->configerrno= en;
77 }
78
79 static void configparseerr(adns_state ads, const char *fn, int lno,
80                            const char *fmt, ...) {
81   va_list al;
82
83   saveerr(ads,EINVAL);
84   if (!ads->logfn || (ads->iflags & adns_if_noerrprint)) return;
85
86   if (lno==-1) adns__lprintf(ads,"adns: %s: ",fn);
87   else adns__lprintf(ads,"adns: %s:%d: ",fn,lno);
88   va_start(al,fmt);
89   adns__vlprintf(ads,fmt,al);
90   va_end(al);
91   adns__lprintf(ads,"\n");
92 }
93
94 static int nextword(const char **bufp_io, const char **word_r, int *l_r) {
95   const char *p, *q;
96
97   p= *bufp_io;
98   while (ctype_whitespace(*p)) p++;
99   if (!*p) return 0;
100
101   q= p;
102   while (*q && !ctype_whitespace(*q)) q++;
103
104   *l_r= q-p;
105   *word_r= p;
106   *bufp_io= q;
107
108   return 1;
109 }
110
111 static void ccf_nameserver(adns_state ads, const char *fn,
112                            int lno, const char *buf) {
113   adns_rr_addr a;
114   char addrbuf[ADNS_ADDR2TEXT_BUFLEN];
115   int err;
116
117   a.len= sizeof(a.addr);
118   err= adns_text2addr(buf,DNS_PORT, 0, &a.addr.sa,&a.len);
119   switch (err) {
120   case 0:
121     break;
122   case EINVAL:
123     configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
124     return;
125   default:
126     configparseerr(ads,fn,lno,"failed to parse nameserver address `%s': %s",
127                    buf,strerror(err));
128     return;
129   }
130   adns__debug(ads,-1,0,"using nameserver %s",
131               adns__sockaddr_ntoa(&a.addr.sa, addrbuf));
132   addserver(ads,&a.addr.sa,a.len);
133 }
134
135 static void ccf_search(adns_state ads, const char *fn,
136                        int lno, const char *buf) {
137   const char *bufp, *word;
138   char *newchars, **newptrs, **pp;
139   int count, tl, l;
140
141   if (!buf) return;
142
143   bufp= buf;
144   count= 0;
145   tl= 0;
146   while (nextword(&bufp,&word,&l)) { count++; tl += l+1; }
147
148   newptrs= malloc(sizeof(char*)*count);
149   if (!newptrs) { saveerr(ads,errno); return; }
150
151   newchars= malloc(tl);
152   if (!newchars) { saveerr(ads,errno); free(newptrs); return; }
153
154   bufp= buf;
155   pp= newptrs;
156   while (nextword(&bufp,&word,&l)) {
157     *pp++= newchars;
158     memcpy(newchars,word,l);
159     newchars += l;
160     *newchars++ = 0;
161   }
162
163   freesearchlist(ads);
164   ads->nsearchlist= count;
165   ads->searchlist= newptrs;
166 }
167
168 static int gen_pton(const char *text, int *af_io, union gen_addr *a) {
169   adns_rr_addr addr;
170   int err;
171
172   addr.len= sizeof(addr.addr);
173   err= adns_text2addr(text,0, adns_qf_addrlit_scope_forbid,
174                       &addr.addr.sa, &addr.len);
175   if (err) { assert(err == EINVAL); return 0; }
176   if (*af_io == AF_UNSPEC) *af_io= addr.addr.sa.sa_family;
177   else if (*af_io != addr.addr.sa.sa_family) return 0;
178   adns__sockaddr_extract(&addr.addr.sa, a, 0);
179   return 1;
180 }
181
182 static void ccf_sortlist(adns_state ads, const char *fn,
183                          int lno, const char *buf) {
184   const char *word;
185   char tbuf[200], *slash, *ep;
186   const char *maskwhat;
187   struct sortlist *sl;
188   int l;
189   int af;
190   int initial= -1;
191
192   if (!buf) return;
193   
194   ads->nsortlist= 0;
195   while (nextword(&buf,&word,&l)) {
196     if (ads->nsortlist >= MAXSORTLIST) {
197       adns__diag(ads,-1,0,"too many sortlist entries,"
198                  " ignoring %.*s onwards",l,word);
199       return;
200     }
201
202     if (l >= sizeof(tbuf)) {
203       configparseerr(ads,fn,lno,"sortlist entry `%.*s' too long",l,word);
204       continue;
205     }
206     
207     memcpy(tbuf,word,l); tbuf[l]= 0;
208     slash= strchr(tbuf,'/');
209     if (slash) *slash++= 0;
210
211     sl= &ads->sortlist[ads->nsortlist];
212     af= AF_UNSPEC;
213     if (!gen_pton(tbuf, &af, &sl->base)) {
214       configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
215       continue;
216     }
217
218     if (slash) {
219       if (slash[strspn(slash, "0123456789")]) {
220         maskwhat = "mask";
221         if (!gen_pton(slash,&af,&sl->mask)) {
222           configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash);
223           continue;
224         }
225       } else {
226         maskwhat = "prefix length";
227         initial= strtoul(slash,&ep,10);
228         if (*ep || initial>adns__addr_width(af)) {
229           configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
230           continue;
231         }
232         adns__prefix_mask(af, initial, &sl->mask);
233       }
234     } else {
235       maskwhat = "implied prefix length";
236       initial= adns__guess_prefix_length(af, &sl->base);
237       if (initial < 0) {
238         configparseerr(ads,fn,lno, "network address `%s'"
239                        " in sortlist is not in classed ranges,"
240                        " must specify mask explicitly", tbuf);
241         continue;
242       }
243       adns__prefix_mask(af, initial, &sl->mask);
244     }
245
246     if (!adns__addr_match_p(af,&sl->base, af,&sl->base,&sl->mask)) {
247       if (initial >= 0) {
248         configparseerr(ads,fn,lno, "%s %d in sortlist"
249                        " overlaps address `%s'",maskwhat,initial,tbuf);
250       } else {
251         configparseerr(ads,fn,lno, "%s `%s' in sortlist"
252                        " overlaps address `%s'",maskwhat,slash,tbuf);
253       }
254       continue;
255     }
256
257     sl->af= af;
258     ads->nsortlist++;
259   }
260 }
261
262 static void ccf_options(adns_state ads, const char *fn,
263                         int lno, const char *buf) {
264   const char *word;
265   char *ep;
266   unsigned long v;
267   int i,l;
268
269   if (!buf) return;
270
271   while (nextword(&buf,&word,&l)) {
272     if (l==5 && !memcmp(word,"debug",5)) {
273       ads->iflags |= adns_if_debug;
274       continue;
275     }
276     if (l>=6 && !memcmp(word,"ndots:",6)) {
277       v= strtoul(word+6,&ep,10);
278       if (l==6 || ep != word+l || v > INT_MAX) {
279         configparseerr(ads,fn,lno,"option `%.*s' malformed"
280                        " or has bad value",l,word);
281         continue;
282       }
283       ads->searchndots= v;
284       continue;
285     }
286     if (l>=12 && !memcmp(word,"adns_checkc:",12)) {
287       if (!strcmp(word+12,"none")) {
288         ads->iflags &= ~adns_if_checkc_freq;
289         ads->iflags |= adns_if_checkc_entex;
290       } else if (!strcmp(word+12,"entex")) {
291         ads->iflags &= ~adns_if_checkc_freq;
292         ads->iflags |= adns_if_checkc_entex;
293       } else if (!strcmp(word+12,"freq")) {
294         ads->iflags |= adns_if_checkc_freq;
295       } else {
296         configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' "
297                        "(must be none, entex or freq", word+12);
298       }
299       continue;
300     }
301     if (l>=8 && !memcmp(word,"adns_af:",8)) {
302       word += 8;
303       ads->iflags &= ~adns_if_afmask;
304       if (strcmp(word,"any")) for (;;) {
305         i= strcspn(word,",");
306         if (i>=4 && !memcmp(word,"ipv4",4))
307           ads->iflags |= adns_if_permit_ipv4;
308         else if (i>=4 && !memcmp(word,"ipv6",4))
309           ads->iflags |= adns_if_permit_ipv6;
310         else {
311           configparseerr(ads,fn,lno, "option adns_af has bad value `%.*s' "
312                          "(must be `any' or list {`ipv4',`ipv6'},...)",
313                          i, word);
314           break;
315         }
316         if (!word[i]) break;
317         word= word + i + 1;
318       }
319       continue;
320     }
321     adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
322   }
323 }
324
325 static void ccf_clearnss(adns_state ads, const char *fn,
326                          int lno, const char *buf) {
327   ads->nservers= 0;
328 }
329
330 static void ccf_include(adns_state ads, const char *fn,
331                         int lno, const char *buf) {
332   if (!*buf) {
333     configparseerr(ads,fn,lno,"`include' directive with no filename");
334     return;
335   }
336   readconfig(ads,buf,1);
337 }
338
339 static void ccf_lookup(adns_state ads, const char *fn, int lno,
340                        const char *buf) {
341   int found_bind=0;
342   const char *word;
343   int l;
344
345   if (!*buf) {
346     configparseerr(ads,fn,lno,"`lookup' directive with no databases");
347     return;
348   }
349
350   while (nextword(&buf,&word,&l)) {
351     if (l==4 && !memcmp(word,"bind",4)) {
352       found_bind=1;
353     } else if (l==4 && !memcmp(word,"file",4)) {
354       /* ignore this and hope /etc/hosts is not essential */
355     } else if (l==2 && !memcmp(word,"yp",2)) {
356       adns__diag(ads,-1,0,"%s:%d: yp lookups not supported by adns", fn,lno);
357       found_bind=-1;
358     } else {
359       adns__diag(ads,-1,0,"%s:%d: unknown `lookup' database `%.*s'",
360                  fn,lno, l,word);
361       found_bind=-1;
362     }
363   }
364   if (!found_bind)
365     adns__diag(ads,-1,0,"%s:%d: `lookup' specified, but not `bind'", fn,lno);
366 }
367
368 static const struct configcommandinfo {
369   const char *name;
370   void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
371 } configcommandinfos[]= {
372   { "nameserver",        ccf_nameserver  },
373   { "domain",            ccf_search      },
374   { "search",            ccf_search      },
375   { "sortlist",          ccf_sortlist    },
376   { "options",           ccf_options     },
377   { "clearnameservers",  ccf_clearnss    },
378   { "include",           ccf_include     },
379   { "lookup",            ccf_lookup      }, /* OpenBSD */
380   {  0                                   }
381 };
382
383 typedef union {
384   FILE *file;
385   const char *text;
386 } getline_ctx;
387
388 static int gl_file(adns_state ads, getline_ctx *src_io, const char *filename,
389                    int lno, char *buf, int buflen) {
390   FILE *file= src_io->file;
391   int c, i;
392   char *p;
393
394   p= buf;
395   buflen--;
396   i= 0;
397     
398   for (;;) { /* loop over chars */
399     if (i == buflen) {
400       adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
401       goto x_badline;
402     }
403     c= getc(file);
404     if (!c) {
405       adns__diag(ads,-1,0,"%s:%d: line contains nul, ignored",filename,lno);
406       goto x_badline;
407     } else if (c == '\n') {
408       break;
409     } else if (c == EOF) {
410       if (ferror(file)) {
411         saveerr(ads,errno);
412         adns__diag(ads,-1,0,"%s:%d: read error: %s",
413                    filename,lno,strerror(errno));
414         return -1;
415       }
416       if (!i) return -1;
417       break;
418     } else {
419       *p++= c;
420       i++;
421     }
422   }
423
424   *p++= 0;
425   return i;
426
427  x_badline:
428   saveerr(ads,EINVAL);
429   while ((c= getc(file)) != EOF && c != '\n');
430   return -2;
431 }
432
433 static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename,
434                    int lno, char *buf, int buflen) {
435   const char *cp= src_io->text;
436   int l;
437
438   if (!cp || !*cp) return -1;
439
440   if (*cp == ';' || *cp == '\n') cp++;
441   l= strcspn(cp,";\n");
442   src_io->text = cp+l;
443
444   if (l >= buflen) {
445     adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
446     saveerr(ads,EINVAL);
447     return -2;
448   }
449     
450   memcpy(buf,cp,l);
451   buf[l]= 0;
452   return l;
453 }
454
455 static void readconfiggeneric(adns_state ads, const char *filename,
456                               int (*getline)(adns_state ads, getline_ctx*,
457                                              const char *filename, int lno,
458                                              char *buf, int buflen),
459                               /* Returns >=0 for success, -1 for EOF or error
460                                * (error will have been reported), or -2 for
461                                * bad line was encountered, try again.
462                                */
463                               getline_ctx gl_ctx) {
464   char linebuf[2000], *p, *q;
465   int lno, l, dirl;
466   const struct configcommandinfo *ccip;
467
468   for (lno=1;
469        (l= getline(ads,&gl_ctx, filename,lno, linebuf,sizeof(linebuf))) != -1;
470        lno++) {
471     if (l == -2) continue;
472     while (l>0 && ctype_whitespace(linebuf[l-1])) l--;
473     linebuf[l]= 0;
474     p= linebuf;
475     while (ctype_whitespace(*p)) p++;
476     if (*p == '#' || *p == ';' || !*p) continue;
477     q= p;
478     while (*q && !ctype_whitespace(*q)) q++;
479     dirl= q-p;
480     for (ccip=configcommandinfos;
481          ccip->name &&
482            !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
483          ccip++);
484     if (!ccip->name) {
485       adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
486                  filename,lno,(int)(q-p),p);
487       continue;
488     }
489     while (ctype_whitespace(*q)) q++;
490     ccip->fn(ads,filename,lno,q);
491   }
492 }
493
494 static const char *instrum_getenv(adns_state ads, const char *envvar) {
495   const char *value;
496
497   value= getenv(envvar);
498   if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
499   else adns__debug(ads,-1,0,"environment variable %s"
500                    " set to `%s'",envvar,value);
501   return value;
502 }
503
504 static void readconfig(adns_state ads, const char *filename, int warnmissing) {
505   getline_ctx gl_ctx;
506   
507   gl_ctx.file= fopen(filename,"r");
508   if (!gl_ctx.file) {
509     if (errno == ENOENT) {
510       if (warnmissing)
511         adns__debug(ads,-1,0, "configuration file"
512                     " `%s' does not exist",filename);
513       return;
514     }
515     saveerr(ads,errno);
516     adns__diag(ads,-1,0,"cannot open configuration file `%s': %s",
517                filename,strerror(errno));
518     return;
519   }
520
521   readconfiggeneric(ads,filename,gl_file,gl_ctx);
522   
523   fclose(gl_ctx.file);
524 }
525
526 static void readconfigtext(adns_state ads, const char *text,
527                            const char *showname) {
528   getline_ctx gl_ctx;
529   
530   gl_ctx.text= text;
531   readconfiggeneric(ads,showname,gl_text,gl_ctx);
532 }
533   
534 static void readconfigenv(adns_state ads, const char *envvar) {
535   const char *filename;
536
537   if (ads->iflags & adns_if_noenv) {
538     adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
539     return;
540   }
541   filename= instrum_getenv(ads,envvar);
542   if (filename) readconfig(ads,filename,1);
543 }
544
545 static void readconfigenvtext(adns_state ads, const char *envvar) {
546   const char *textdata;
547
548   if (ads->iflags & adns_if_noenv) {
549     adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
550     return;
551   }
552   textdata= instrum_getenv(ads,envvar);
553   if (textdata) readconfigtext(ads,textdata,envvar);
554 }
555
556
557 int adns__setnonblock(adns_state ads, int fd) {
558   int r;
559   
560   r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
561   r |= O_NONBLOCK;
562   r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
563   return 0;
564 }
565
566 static int init_begin(adns_state *ads_r, adns_initflags flags,
567                       adns_logcallbackfn *logfn, void *logfndata) {
568   adns_state ads;
569   pid_t pid;
570   
571   ads= malloc(sizeof(*ads)); if (!ads) return errno;
572
573   ads->iflags= flags;
574   ads->logfn= logfn;
575   ads->logfndata= logfndata;
576   ads->configerrno= 0;
577   LIST_INIT(ads->udpw);
578   LIST_INIT(ads->tcpw);
579   LIST_INIT(ads->childw);
580   LIST_INIT(ads->output);
581   LIST_INIT(ads->intdone);
582   ads->forallnext= 0;
583   ads->nextid= 0x311f;
584   ads->nudp= 0;
585   ads->tcpsocket= -1;
586   adns__vbuf_init(&ads->tcpsend);
587   adns__vbuf_init(&ads->tcprecv);
588   ads->tcprecv_skip= 0;
589   ads->nservers= ads->nsortlist= ads->nsearchlist= ads->tcpserver= 0;
590   ads->searchndots= 1;
591   ads->tcpstate= server_disconnected;
592   timerclear(&ads->tcptimeout);
593   ads->searchlist= 0;
594
595   pid= getpid();
596   ads->rand48xsubi[0]= pid;
597   ads->rand48xsubi[1]= (unsigned long)pid >> 16;
598   ads->rand48xsubi[2]= pid ^ ((unsigned long)pid >> 16);
599
600   *ads_r= ads;
601   return 0;
602 }
603
604 static int init_finish(adns_state ads) {
605   struct sockaddr_in sin;
606   struct protoent *proto;
607   struct udpsocket *udp;
608   int i, j;
609   int r;
610   
611   if (!ads->nservers) {
612     if (ads->logfn && ads->iflags & adns_if_debug)
613       adns__lprintf(ads,"adns: no nameservers, using IPv4 localhost\n");
614     memset(&sin, 0, sizeof(sin));
615     sin.sin_family = AF_INET;
616     sin.sin_port = htons(DNS_PORT);
617     sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
618     addserver(ads,(struct sockaddr *)&sin, sizeof(sin));
619   }
620
621   proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
622   ads->nudp= 0;
623   for (i=0; i<ads->nservers; i++) {
624     if (adns__udpsocket_by_af(ads, ads->servers[i].addr.sa.sa_family))
625       continue;
626     assert(ads->nudp < MAXUDP);
627     udp= &ads->udpsocket[ads->nudp];
628     udp->af= ads->servers[i].addr.sa.sa_family;
629     udp->fd= socket(udp->af,SOCK_DGRAM,proto->p_proto);
630     if (udp->fd < 0) { r= errno; goto x_free; }
631     ads->nudp++;
632     r= adns__setnonblock(ads,udp->fd);
633     if (r) { r= errno; goto x_closeudp; }
634   }
635   
636   return 0;
637
638  x_closeudp:
639   for (j=0; j<ads->nudp; j++) close(ads->udpsocket[j].fd);
640  x_free:
641   free(ads);
642   return r;
643 }
644
645 static void init_abort(adns_state ads) {
646   if (ads->nsearchlist) {
647     free(ads->searchlist[0]);
648     free(ads->searchlist);
649   }
650   free(ads);
651 }
652
653 static void logfn_file(adns_state ads, void *logfndata,
654                        const char *fmt, va_list al) {
655   vfprintf(logfndata,fmt,al);
656 }
657
658 static int init_files(adns_state *ads_r, adns_initflags flags,
659                       adns_logcallbackfn *logfn, void *logfndata) {
660   adns_state ads;
661   const char *res_options, *adns_res_options;
662   int r;
663   
664   r= init_begin(&ads, flags, logfn, logfndata);
665   if (r) return r;
666   
667   res_options= instrum_getenv(ads,"RES_OPTIONS");
668   adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
669   ccf_options(ads,"RES_OPTIONS",-1,res_options);
670   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
671
672   readconfig(ads,"/etc/resolv.conf",1);
673   readconfig(ads,"/etc/resolv-adns.conf",0);
674   readconfigenv(ads,"RES_CONF");
675   readconfigenv(ads,"ADNS_RES_CONF");
676
677   readconfigenvtext(ads,"RES_CONF_TEXT");
678   readconfigenvtext(ads,"ADNS_RES_CONF_TEXT");
679
680   ccf_options(ads,"RES_OPTIONS",-1,res_options);
681   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
682
683   ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN"));
684   ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN"));
685
686   if (ads->configerrno && ads->configerrno != EINVAL) {
687     r= ads->configerrno;
688     init_abort(ads);
689     return r;
690   }
691
692   r= init_finish(ads);
693   if (r) return r;
694
695   adns__consistency(ads,0,cc_entex);
696   *ads_r= ads;
697   return 0;
698 }
699
700 int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
701   return init_files(ads_r, flags, logfn_file, diagfile ? diagfile : stderr);
702 }
703
704 static int init_strcfg(adns_state *ads_r, adns_initflags flags,
705                        adns_logcallbackfn *logfn, void *logfndata,
706                        const char *configtext) {
707   adns_state ads;
708   int r;
709
710   r= init_begin(&ads, flags, logfn, logfndata);
711   if (r) return r;
712
713   readconfigtext(ads,configtext,"<supplied configuration text>");
714   if (ads->configerrno) {
715     r= ads->configerrno;
716     init_abort(ads);
717     return r;
718   }
719
720   r= init_finish(ads);  if (r) return r;
721   adns__consistency(ads,0,cc_entex);
722   *ads_r= ads;
723   return 0;
724 }
725
726 int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
727                      FILE *diagfile, const char *configtext) {
728   return init_strcfg(ads_r, flags,
729                      diagfile ? logfn_file : 0, diagfile,
730                      configtext);
731 }
732
733 int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
734                     const char *configtext /*0=>use default config files*/,
735                     adns_logcallbackfn *logfn /*0=>logfndata is a FILE* */,
736                     void *logfndata /*0 with logfn==0 => discard*/) {
737   if (!logfn && logfndata)
738     logfn= logfn_file;
739   if (configtext)
740     return init_strcfg(newstate_r, flags, logfn, logfndata, configtext);
741   else
742     return init_files(newstate_r, flags, logfn, logfndata);
743 }
744
745 void adns_finish(adns_state ads) {
746   int i;
747   adns__consistency(ads,0,cc_entex);
748   for (;;) {
749     if (ads->udpw.head) adns__cancel(ads->udpw.head);
750     else if (ads->tcpw.head) adns__cancel(ads->tcpw.head);
751     else if (ads->childw.head) adns__cancel(ads->childw.head);
752     else if (ads->output.head) adns__cancel(ads->output.head);
753     else if (ads->intdone.head) adns__cancel(ads->output.head);
754     else break;
755   }
756   for (i=0; i<ads->nudp; i++) close(ads->udpsocket[i].fd);
757   if (ads->tcpsocket >= 0) close(ads->tcpsocket);
758   adns__vbuf_free(&ads->tcpsend);
759   adns__vbuf_free(&ads->tcprecv);
760   freesearchlist(ads);
761   free(ads);
762 }
763
764 void adns_forallqueries_begin(adns_state ads) {
765   adns__consistency(ads,0,cc_entex);
766   ads->forallnext=
767     ads->udpw.head ? ads->udpw.head :
768     ads->tcpw.head ? ads->tcpw.head :
769     ads->childw.head ? ads->childw.head :
770     ads->output.head;
771 }
772   
773 adns_query adns_forallqueries_next(adns_state ads, void **context_r) {
774   adns_query qu, nqu;
775
776   adns__consistency(ads,0,cc_entex);
777   nqu= ads->forallnext;
778   for (;;) {
779     qu= nqu;
780     if (!qu) return 0;
781     if (qu->next) {
782       nqu= qu->next;
783     } else if (qu == ads->udpw.tail) {
784       nqu=
785         ads->tcpw.head ? ads->tcpw.head :
786         ads->childw.head ? ads->childw.head :
787         ads->output.head;
788     } else if (qu == ads->tcpw.tail) {
789       nqu=
790         ads->childw.head ? ads->childw.head :
791         ads->output.head;
792     } else if (qu == ads->childw.tail) {
793       nqu= ads->output.head;
794     } else {
795       nqu= 0;
796     }
797     if (!qu->parent) break;
798   }
799   ads->forallnext= nqu;
800   if (context_r) *context_r= qu->ctx.ext;
801   return qu;
802 }