Initial draft of test harness arrangements, from chiark. cvs rdiff
[adns] / src / setup.c
CommitLineData
e576be50 1/*
2 * setup.c
3 * - configuration file parsing
4 * - management of global state
5 */
6/*
7 * This file is part of adns, which is Copyright (C) 1997, 1998 Ian Jackson
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
656b2da9 23
4353a5c4 24#include <stdlib.h>
25#include <errno.h>
26#include <string.h>
27#include <unistd.h>
28#include <fcntl.h>
656b2da9 29
4353a5c4 30#include <netdb.h>
31#include <arpa/inet.h>
32
33#include "internal.h"
34
36369543 35static void readconfig(adns_state ads, const char *filename);
36
656b2da9 37static void addserver(adns_state ads, struct in_addr addr) {
38 int i;
39 struct server *ss;
40
41 for (i=0; i<ads->nservers; i++) {
42 if (ads->servers[i].addr.s_addr == addr.s_addr) {
3955725c 43 adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
656b2da9 44 return;
45 }
46 }
47
48 if (ads->nservers>=MAXSERVERS) {
3955725c 49 adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr));
656b2da9 50 return;
51 }
52
53 ss= ads->servers+ads->nservers;
54 ss->addr= addr;
656b2da9 55 ads->nservers++;
56}
57
36369543 58static void saveerr(adns_state ads, int en) {
59 if (!ads->configerrno) ads->configerrno= en;
60}
61
656b2da9 62static void configparseerr(adns_state ads, const char *fn, int lno,
63 const char *fmt, ...) {
64 va_list al;
36369543 65
66 saveerr(ads,EINVAL);
67 if (!ads->diagfile || (ads->iflags & adns_if_noerrprint)) return;
68
69 if (lno==-1) fprintf(ads->diagfile,"adns: %s: ",fn);
70 else fprintf(ads->diagfile,"adns: %s:%d: ",fn,lno);
656b2da9 71 va_start(al,fmt);
36369543 72 vfprintf(ads->diagfile,fmt,al);
656b2da9 73 va_end(al);
36369543 74 fputc('\n',ads->diagfile);
656b2da9 75}
76
77static void ccf_nameserver(adns_state ads, const char *fn, int lno, const char *buf) {
78 struct in_addr ia;
79
80 if (!inet_aton(buf,&ia)) {
81 configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
82 return;
83 }
3955725c 84 adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
656b2da9 85 addserver(ads,ia);
86}
87
88static void ccf_search(adns_state ads, const char *fn, int lno, const char *buf) {
89 if (!buf) return;
3955725c 90 adns__diag(ads,-1,0,"warning - `search' ignored fixme");
656b2da9 91}
92
93static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) {
3955725c 94 adns__diag(ads,-1,0,"warning - `sortlist' ignored fixme");
656b2da9 95}
96
97static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) {
98 if (!buf) return;
3955725c 99 adns__diag(ads,-1,0,"warning - `options' ignored fixme");
656b2da9 100}
101
102static void ccf_clearnss(adns_state ads, const char *fn, int lno, const char *buf) {
103 ads->nservers= 0;
104}
105
36369543 106static void ccf_include(adns_state ads, const char *fn, int lno, const char *buf) {
107 if (!*buf) {
108 configparseerr(ads,fn,lno,"`include' directive with no filename");
109 return;
110 }
111 readconfig(ads,buf);
112}
113
656b2da9 114static const struct configcommandinfo {
115 const char *name;
116 void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
117} configcommandinfos[]= {
118 { "nameserver", ccf_nameserver },
119 { "domain", ccf_search },
120 { "search", ccf_search },
121 { "sortlist", ccf_sortlist },
122 { "options", ccf_options },
123 { "clearnameservers", ccf_clearnss },
36369543 124 { "include", ccf_include },
656b2da9 125 { 0 }
126};
127
36369543 128typedef union {
656b2da9 129 FILE *file;
36369543 130 const char *text;
131} getline_ctx;
656b2da9 132
36369543 133static int gl_file(adns_state ads, getline_ctx *src_io, const char *filename,
134 int lno, char *buf, int buflen) {
135 FILE *file= src_io->file;
136 int c, i;
137 char *p;
138
139 p= buf;
140 buflen--;
141 i= 0;
142
143 for (;;) { /* loop over chars */
144 if (i == buflen) {
145 adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
146 goto x_badline;
147 }
148 c= getc(file);
149 if (!c) {
150 adns__diag(ads,-1,0,"%s:%d: line contains nul, ignored",filename,lno);
151 goto x_badline;
152 } else if (c == '\n') {
153 break;
154 } else if (c == EOF) {
155 if (ferror(file)) {
156 saveerr(ads,errno);
157 adns__diag(ads,-1,0,"%s:%d: read error: %s",filename,lno,strerror(errno));
158 return -1;
159 }
160 if (!i) return -1;
161 break;
162 } else {
163 *p++= c;
164 i++;
656b2da9 165 }
656b2da9 166 }
167
36369543 168 *p++= 0;
169 return i;
170
171 x_badline:
172 saveerr(ads,EINVAL);
173 while ((c= getc(file)) != EOF && c != '\n');
174 return -2;
175}
176
177static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename,
178 int lno, char *buf, int buflen) {
179 const char *cp= src_io->text, *nn;
180 int l;
181
182 if (!cp) return -1;
183
184 nn= strchr(cp,'\n');
185
186 l= nn ? nn-cp : strlen(cp);
187 src_io->text= nn ? nn+1 : 0;
188
189 if (l >= buflen) {
190 adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
191 saveerr(ads,EINVAL);
192 return -2;
193 }
194
195 memcpy(buf,cp,l);
196 buf[l]= 0;
197 return l;
198}
199
200static void readconfiggeneric(adns_state ads, const char *filename,
201 int (*getline)(adns_state ads, getline_ctx*,
202 const char *filename, int lno,
203 char *buf, int buflen),
204 /* Returns >=0 for success, -1 for EOF or error
205 * (error will have been reported), or -2 for
206 * bad line was encountered, try again.
207 */
208 getline_ctx gl_ctx) {
209 char linebuf[2000], *p, *q;
210 int lno, l, dirl;
211 const struct configcommandinfo *ccip;
212
213 for (lno=1;
214 (l= getline(ads,&gl_ctx, filename,lno, linebuf,sizeof(linebuf))) != -1;
215 lno++) {
216 if (l == -2) continue;
656b2da9 217 while (l>0 && ctype_whitespace(linebuf[l-1])) l--;
218 linebuf[l]= 0;
219 p= linebuf;
220 while (ctype_whitespace(*p)) p++;
36369543 221 if (*p == '#' || !*p) continue;
656b2da9 222 q= p;
223 while (*q && !ctype_whitespace(*q)) q++;
36369543 224 dirl= q-p;
656b2da9 225 for (ccip=configcommandinfos;
36369543 226 ccip->name && !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
656b2da9 227 ccip++);
228 if (!ccip->name) {
3955725c 229 adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
4353a5c4 230 filename,lno,q-p,p);
656b2da9 231 continue;
232 }
233 while (ctype_whitespace(*q)) q++;
234 ccip->fn(ads,filename,lno,q);
235 }
656b2da9 236}
237
238static const char *instrum_getenv(adns_state ads, const char *envvar) {
239 const char *value;
240
241 value= getenv(envvar);
3955725c 242 if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
243 else adns__debug(ads,-1,0,"environment variable %s set to `%s'",envvar,value);
656b2da9 244 return value;
245}
246
36369543 247static void readconfig(adns_state ads, const char *filename) {
248 getline_ctx gl_ctx;
249
250 gl_ctx.file= fopen(filename,"r");
251 if (!gl_ctx.file) {
252 if (errno == ENOENT) {
253 adns__debug(ads,-1,0,"configuration file `%s' does not exist",filename);
254 return;
255 }
256 saveerr(ads,errno);
257 adns__diag(ads,-1,0,"cannot open configuration file `%s': %s",
258 filename,strerror(errno));
259 return;
260 }
261
262 readconfiggeneric(ads,filename,gl_file,gl_ctx);
263
264 fclose(gl_ctx.file);
265}
266
267static void readconfigtext(adns_state ads, const char *text, const char *showname) {
268 getline_ctx gl_ctx;
269
270 gl_ctx.text= text;
271 readconfiggeneric(ads,showname,gl_text,gl_ctx);
272}
273
656b2da9 274static void readconfigenv(adns_state ads, const char *envvar) {
275 const char *filename;
276
277 if (ads->iflags & adns_if_noenv) {
3955725c 278 adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
656b2da9 279 return;
280 }
281 filename= instrum_getenv(ads,envvar);
282 if (filename) readconfig(ads,filename);
283}
4353a5c4 284
285
286int adns__setnonblock(adns_state ads, int fd) {
287 int r;
656b2da9 288
4353a5c4 289 r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
290 r |= O_NONBLOCK;
291 r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
292 return 0;
293}
294
36369543 295static int init_begin(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
656b2da9 296 adns_state ads;
656b2da9 297
298 ads= malloc(sizeof(*ads)); if (!ads) return errno;
36369543 299
656b2da9 300 ads->iflags= flags;
36369543 301 ads->diagfile= diagfile;
4353a5c4 302 LIST_INIT(ads->timew);
303 LIST_INIT(ads->childw);
304 LIST_INIT(ads->output);
305 ads->nextid= 0x311f;
306 ads->udpsocket= ads->tcpsocket= -1;
4353a5c4 307 adns__vbuf_init(&ads->tcpsend);
308 adns__vbuf_init(&ads->tcprecv);
309 ads->nservers= ads->tcpserver= 0;
310 ads->tcpstate= server_disconnected;
311 timerclear(&ads->tcptimeout);
656b2da9 312
36369543 313 *ads_r= ads;
314 return 0;
315}
656b2da9 316
36369543 317static int init_finish(adns_state ads) {
318 struct in_addr ia;
319 struct protoent *proto;
320 int r;
321
656b2da9 322 if (!ads->nservers) {
36369543 323 if (ads->diagfile && ads->iflags & adns_if_debug)
324 fprintf(ads->diagfile,"adns: no nameservers, using localhost\n");
656b2da9 325 ia.s_addr= INADDR_LOOPBACK;
326 addserver(ads,ia);
327 }
328
329 proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
330 ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
8402e34c 331 if (ads->udpsocket<0) { r= errno; goto x_free; }
4bec51a4 332
94436798 333 r= adns__setnonblock(ads,ads->udpsocket);
334 if (r) { r= errno; goto x_closeudp; }
656b2da9 335
656b2da9 336 return 0;
337
94436798 338 x_closeudp:
339 close(ads->udpsocket);
656b2da9 340 x_free:
341 free(ads);
342 return r;
343}
344
36369543 345int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
346 adns_state ads;
347 const char *res_options, *adns_res_options;
348 int r;
349
350 r= init_begin(&ads, flags, diagfile ? diagfile : stderr);
351 if (r) return r;
352
353 res_options= instrum_getenv(ads,"RES_OPTIONS");
354 adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
355 ccf_options(ads,"RES_OPTIONS",-1,res_options);
356 ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
357
358 readconfig(ads,"/etc/resolv.conf");
359 readconfigenv(ads,"RES_CONF");
360 readconfigenv(ads,"ADNS_RES_CONF");
361
362 ccf_options(ads,"RES_OPTIONS",-1,res_options);
363 ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
364
365 ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN"));
366 ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN"));
367
368 r= init_finish(ads);
369 if (r) return r;
370
371 *ads_r= ads;
372 return 0;
373}
374
375int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
376 FILE *diagfile, const char *configtext) {
377 adns_state ads;
378 int r;
379
380 r= init_begin(&ads, flags, diagfile); if (r) return r;
381
382 readconfigtext(ads,configtext,"<supplied configuration text>");
383 if (ads->configerrno) {
384 r= ads->configerrno;
385 free(ads);
386 return r;
387 }
388
389 r= init_finish(ads); if (r) return r;
390 *ads_r= ads;
391 return 0;
392}
393
9ec44266 394void adns_finish(adns_state ads) {
395 for (;;) {
396 if (ads->timew.head) adns_cancel(ads->timew.head);
397 else if (ads->childw.head) adns_cancel(ads->childw.head);
398 else if (ads->output.head) adns_cancel(ads->output.head);
399 else break;
400 }
401 close(ads->udpsocket);
402 if (ads->tcpsocket >= 0) close(ads->tcpsocket);
403 adns__vbuf_free(&ads->tcpsend);
404 adns__vbuf_free(&ads->tcprecv);
405 free(ads);
656b2da9 406}