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