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