chiark / gitweb /
3419cd6a5134f2157d710b541825ffa436d2ea36
[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 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     adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
302   }
303 }
304
305 static void ccf_clearnss(adns_state ads, const char *fn,
306                          int lno, const char *buf) {
307   ads->nservers= 0;
308 }
309
310 static void ccf_include(adns_state ads, const char *fn,
311                         int lno, const char *buf) {
312   if (!*buf) {
313     configparseerr(ads,fn,lno,"`include' directive with no filename");
314     return;
315   }
316   readconfig(ads,buf,1);
317 }
318
319 static void ccf_lookup(adns_state ads, const char *fn, int lno,
320                        const char *buf) {
321   int found_bind=0;
322   const char *word;
323   int l;
324
325   if (!*buf) {
326     configparseerr(ads,fn,lno,"`lookup' directive with no databases");
327     return;
328   }
329
330   while (nextword(&buf,&word,&l)) {
331     if (l==4 && !memcmp(word,"bind",4)) {
332       found_bind=1;
333     } else if (l==4 && !memcmp(word,"file",4)) {
334       /* ignore this and hope /etc/hosts is not essential */
335     } else if (l==2 && !memcmp(word,"yp",2)) {
336       adns__diag(ads,-1,0,"%s:%d: yp lookups not supported by adns", fn,lno);
337       found_bind=-1;
338     } else {
339       adns__diag(ads,-1,0,"%s:%d: unknown `lookup' database `%.*s'",
340                  fn,lno, l,word);
341       found_bind=-1;
342     }
343   }
344   if (!found_bind)
345     adns__diag(ads,-1,0,"%s:%d: `lookup' specified, but not `bind'", fn,lno);
346 }
347
348 static const struct configcommandinfo {
349   const char *name;
350   void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
351 } configcommandinfos[]= {
352   { "nameserver",        ccf_nameserver  },
353   { "domain",            ccf_search      },
354   { "search",            ccf_search      },
355   { "sortlist",          ccf_sortlist    },
356   { "options",           ccf_options     },
357   { "clearnameservers",  ccf_clearnss    },
358   { "include",           ccf_include     },
359   { "lookup",            ccf_lookup      }, /* OpenBSD */
360   {  0                                   }
361 };
362
363 typedef union {
364   FILE *file;
365   const char *text;
366 } getline_ctx;
367
368 static int gl_file(adns_state ads, getline_ctx *src_io, const char *filename,
369                    int lno, char *buf, int buflen) {
370   FILE *file= src_io->file;
371   int c, i;
372   char *p;
373
374   p= buf;
375   buflen--;
376   i= 0;
377     
378   for (;;) { /* loop over chars */
379     if (i == buflen) {
380       adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
381       goto x_badline;
382     }
383     c= getc(file);
384     if (!c) {
385       adns__diag(ads,-1,0,"%s:%d: line contains nul, ignored",filename,lno);
386       goto x_badline;
387     } else if (c == '\n') {
388       break;
389     } else if (c == EOF) {
390       if (ferror(file)) {
391         saveerr(ads,errno);
392         adns__diag(ads,-1,0,"%s:%d: read error: %s",
393                    filename,lno,strerror(errno));
394         return -1;
395       }
396       if (!i) return -1;
397       break;
398     } else {
399       *p++= c;
400       i++;
401     }
402   }
403
404   *p++= 0;
405   return i;
406
407  x_badline:
408   saveerr(ads,EINVAL);
409   while ((c= getc(file)) != EOF && c != '\n');
410   return -2;
411 }
412
413 static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename,
414                    int lno, char *buf, int buflen) {
415   const char *cp= src_io->text;
416   int l;
417
418   if (!cp || !*cp) return -1;
419
420   if (*cp == ';' || *cp == '\n') cp++;
421   l= strcspn(cp,";\n");
422   src_io->text = cp+l;
423
424   if (l >= buflen) {
425     adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
426     saveerr(ads,EINVAL);
427     return -2;
428   }
429     
430   memcpy(buf,cp,l);
431   buf[l]= 0;
432   return l;
433 }
434
435 static void readconfiggeneric(adns_state ads, const char *filename,
436                               int (*getline)(adns_state ads, getline_ctx*,
437                                              const char *filename, int lno,
438                                              char *buf, int buflen),
439                               /* Returns >=0 for success, -1 for EOF or error
440                                * (error will have been reported), or -2 for
441                                * bad line was encountered, try again.
442                                */
443                               getline_ctx gl_ctx) {
444   char linebuf[2000], *p, *q;
445   int lno, l, dirl;
446   const struct configcommandinfo *ccip;
447
448   for (lno=1;
449        (l= getline(ads,&gl_ctx, filename,lno, linebuf,sizeof(linebuf))) != -1;
450        lno++) {
451     if (l == -2) continue;
452     while (l>0 && ctype_whitespace(linebuf[l-1])) l--;
453     linebuf[l]= 0;
454     p= linebuf;
455     while (ctype_whitespace(*p)) p++;
456     if (*p == '#' || *p == ';' || !*p) continue;
457     q= p;
458     while (*q && !ctype_whitespace(*q)) q++;
459     dirl= q-p;
460     for (ccip=configcommandinfos;
461          ccip->name &&
462            !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
463          ccip++);
464     if (!ccip->name) {
465       adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
466                  filename,lno,(int)(q-p),p);
467       continue;
468     }
469     while (ctype_whitespace(*q)) q++;
470     ccip->fn(ads,filename,lno,q);
471   }
472 }
473
474 static const char *instrum_getenv(adns_state ads, const char *envvar) {
475   const char *value;
476
477   value= getenv(envvar);
478   if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
479   else adns__debug(ads,-1,0,"environment variable %s"
480                    " set to `%s'",envvar,value);
481   return value;
482 }
483
484 static void readconfig(adns_state ads, const char *filename, int warnmissing) {
485   getline_ctx gl_ctx;
486   
487   gl_ctx.file= fopen(filename,"r");
488   if (!gl_ctx.file) {
489     if (errno == ENOENT) {
490       if (warnmissing)
491         adns__debug(ads,-1,0, "configuration file"
492                     " `%s' does not exist",filename);
493       return;
494     }
495     saveerr(ads,errno);
496     adns__diag(ads,-1,0,"cannot open configuration file `%s': %s",
497                filename,strerror(errno));
498     return;
499   }
500
501   readconfiggeneric(ads,filename,gl_file,gl_ctx);
502   
503   fclose(gl_ctx.file);
504 }
505
506 static void readconfigtext(adns_state ads, const char *text,
507                            const char *showname) {
508   getline_ctx gl_ctx;
509   
510   gl_ctx.text= text;
511   readconfiggeneric(ads,showname,gl_text,gl_ctx);
512 }
513   
514 static void readconfigenv(adns_state ads, const char *envvar) {
515   const char *filename;
516
517   if (ads->iflags & adns_if_noenv) {
518     adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
519     return;
520   }
521   filename= instrum_getenv(ads,envvar);
522   if (filename) readconfig(ads,filename,1);
523 }
524
525 static void readconfigenvtext(adns_state ads, const char *envvar) {
526   const char *textdata;
527
528   if (ads->iflags & adns_if_noenv) {
529     adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
530     return;
531   }
532   textdata= instrum_getenv(ads,envvar);
533   if (textdata) readconfigtext(ads,textdata,envvar);
534 }
535
536
537 int adns__setnonblock(adns_state ads, int fd) {
538   int r;
539   
540   r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
541   r |= O_NONBLOCK;
542   r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
543   return 0;
544 }
545
546 static int init_begin(adns_state *ads_r, adns_initflags flags,
547                       adns_logcallbackfn *logfn, void *logfndata) {
548   adns_state ads;
549   pid_t pid;
550   
551   ads= malloc(sizeof(*ads)); if (!ads) return errno;
552
553   ads->iflags= flags;
554   ads->logfn= logfn;
555   ads->logfndata= logfndata;
556   ads->configerrno= 0;
557   LIST_INIT(ads->udpw);
558   LIST_INIT(ads->tcpw);
559   LIST_INIT(ads->childw);
560   LIST_INIT(ads->output);
561   ads->forallnext= 0;
562   ads->nextid= 0x311f;
563   ads->nudp= 0;
564   ads->tcpsocket= -1;
565   adns__vbuf_init(&ads->tcpsend);
566   adns__vbuf_init(&ads->tcprecv);
567   ads->tcprecv_skip= 0;
568   ads->nservers= ads->nsortlist= ads->nsearchlist= ads->tcpserver= 0;
569   ads->searchndots= 1;
570   ads->tcpstate= server_disconnected;
571   timerclear(&ads->tcptimeout);
572   ads->searchlist= 0;
573
574   pid= getpid();
575   ads->rand48xsubi[0]= pid;
576   ads->rand48xsubi[1]= (unsigned long)pid >> 16;
577   ads->rand48xsubi[2]= pid ^ ((unsigned long)pid >> 16);
578
579   *ads_r= ads;
580   return 0;
581 }
582
583 static int init_finish(adns_state ads) {
584   struct sockaddr_in sin;
585   struct protoent *proto;
586   struct udpsocket *udp;
587   int i, j;
588   int r;
589   
590   if (!ads->nservers) {
591     if (ads->logfn && ads->iflags & adns_if_debug)
592       adns__lprintf(ads,"adns: no nameservers, using IPv4 localhost\n");
593     memset(&sin, 0, sizeof(sin));
594     sin.sin_family = AF_INET;
595     sin.sin_port = htons(DNS_PORT);
596     sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
597     addserver(ads,(struct sockaddr *)&sin, sizeof(sin));
598   }
599
600   proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
601   ads->nudp= 0;
602   for (i=0; i<ads->nservers; i++) {
603     if (adns__udpsocket_by_af(ads, ads->servers[i].addr.sa.sa_family))
604       continue;
605     assert(ads->nudp < MAXUDP);
606     udp= &ads->udpsocket[ads->nudp];
607     udp->af= ads->servers[i].addr.sa.sa_family;
608     udp->fd= socket(udp->af,SOCK_DGRAM,proto->p_proto);
609     if (udp->fd < 0) { r= errno; goto x_free; }
610     ads->nudp++;
611     r= adns__setnonblock(ads,udp->fd);
612     if (r) { r= errno; goto x_closeudp; }
613   }
614   
615   return 0;
616
617  x_closeudp:
618   for (j=0; j<ads->nudp; j++) close(ads->udpsocket[j].fd);
619  x_free:
620   free(ads);
621   return r;
622 }
623
624 static void init_abort(adns_state ads) {
625   if (ads->nsearchlist) {
626     free(ads->searchlist[0]);
627     free(ads->searchlist);
628   }
629   free(ads);
630 }
631
632 static void logfn_file(adns_state ads, void *logfndata,
633                        const char *fmt, va_list al) {
634   vfprintf(logfndata,fmt,al);
635 }
636
637 static int init_files(adns_state *ads_r, adns_initflags flags,
638                       adns_logcallbackfn *logfn, void *logfndata) {
639   adns_state ads;
640   const char *res_options, *adns_res_options;
641   int r;
642   
643   r= init_begin(&ads, flags, logfn, logfndata);
644   if (r) return r;
645   
646   res_options= instrum_getenv(ads,"RES_OPTIONS");
647   adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
648   ccf_options(ads,"RES_OPTIONS",-1,res_options);
649   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
650
651   readconfig(ads,"/etc/resolv.conf",1);
652   readconfig(ads,"/etc/resolv-adns.conf",0);
653   readconfigenv(ads,"RES_CONF");
654   readconfigenv(ads,"ADNS_RES_CONF");
655
656   readconfigenvtext(ads,"RES_CONF_TEXT");
657   readconfigenvtext(ads,"ADNS_RES_CONF_TEXT");
658
659   ccf_options(ads,"RES_OPTIONS",-1,res_options);
660   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
661
662   ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN"));
663   ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN"));
664
665   if (ads->configerrno && ads->configerrno != EINVAL) {
666     r= ads->configerrno;
667     init_abort(ads);
668     return r;
669   }
670
671   r= init_finish(ads);
672   if (r) return r;
673
674   adns__consistency(ads,0,cc_entex);
675   *ads_r= ads;
676   return 0;
677 }
678
679 int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
680   return init_files(ads_r, flags, logfn_file, diagfile ? diagfile : stderr);
681 }
682
683 static int init_strcfg(adns_state *ads_r, adns_initflags flags,
684                        adns_logcallbackfn *logfn, void *logfndata,
685                        const char *configtext) {
686   adns_state ads;
687   int r;
688
689   r= init_begin(&ads, flags, logfn, logfndata);
690   if (r) return r;
691
692   readconfigtext(ads,configtext,"<supplied configuration text>");
693   if (ads->configerrno) {
694     r= ads->configerrno;
695     init_abort(ads);
696     return r;
697   }
698
699   r= init_finish(ads);  if (r) return r;
700   adns__consistency(ads,0,cc_entex);
701   *ads_r= ads;
702   return 0;
703 }
704
705 int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
706                      FILE *diagfile, const char *configtext) {
707   return init_strcfg(ads_r, flags,
708                      diagfile ? logfn_file : 0, diagfile,
709                      configtext);
710 }
711
712 int adns_init_logfn(adns_state *newstate_r, adns_initflags flags,
713                     const char *configtext /*0=>use default config files*/,
714                     adns_logcallbackfn *logfn /*0=>logfndata is a FILE* */,
715                     void *logfndata /*0 with logfn==0 => discard*/) {
716   if (!logfn && logfndata)
717     logfn= logfn_file;
718   if (configtext)
719     return init_strcfg(newstate_r, flags, logfn, logfndata, configtext);
720   else
721     return init_files(newstate_r, flags, logfn, logfndata);
722 }
723
724 void adns_finish(adns_state ads) {
725   int i;
726   adns__consistency(ads,0,cc_entex);
727   for (;;) {
728     if (ads->udpw.head) adns__cancel(ads->udpw.head);
729     else if (ads->tcpw.head) adns__cancel(ads->tcpw.head);
730     else if (ads->childw.head) adns__cancel(ads->childw.head);
731     else if (ads->output.head) adns__cancel(ads->output.head);
732     else break;
733   }
734   for (i=0; i<ads->nudp; i++) close(ads->udpsocket[i].fd);
735   if (ads->tcpsocket >= 0) close(ads->tcpsocket);
736   adns__vbuf_free(&ads->tcpsend);
737   adns__vbuf_free(&ads->tcprecv);
738   freesearchlist(ads);
739   free(ads);
740 }
741
742 void adns_forallqueries_begin(adns_state ads) {
743   adns__consistency(ads,0,cc_entex);
744   ads->forallnext=
745     ads->udpw.head ? ads->udpw.head :
746     ads->tcpw.head ? ads->tcpw.head :
747     ads->childw.head ? ads->childw.head :
748     ads->output.head;
749 }
750   
751 adns_query adns_forallqueries_next(adns_state ads, void **context_r) {
752   adns_query qu, nqu;
753
754   adns__consistency(ads,0,cc_entex);
755   nqu= ads->forallnext;
756   for (;;) {
757     qu= nqu;
758     if (!qu) return 0;
759     if (qu->next) {
760       nqu= qu->next;
761     } else if (qu == ads->udpw.tail) {
762       nqu=
763         ads->tcpw.head ? ads->tcpw.head :
764         ads->childw.head ? ads->childw.head :
765         ads->output.head;
766     } else if (qu == ads->tcpw.tail) {
767       nqu=
768         ads->childw.head ? ads->childw.head :
769         ads->output.head;
770     } else if (qu == ads->childw.tail) {
771       nqu= ads->output.head;
772     } else {
773       nqu= 0;
774     }
775     if (!qu->parent) break;
776   }
777   ads->forallnext= nqu;
778   if (context_r) *context_r= qu->ctx.ext;
779   return qu;
780 }