chiark / gitweb /
Implement SOA, RP, HINFO (but no mailbox quoting). Implement _finish.
[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 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  */
23
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #include <netdb.h>
31 #include <arpa/inet.h>
32
33 #include "internal.h"
34
35 static void addserver(adns_state ads, struct in_addr addr) {
36   int i;
37   struct server *ss;
38   
39   for (i=0; i<ads->nservers; i++) {
40     if (ads->servers[i].addr.s_addr == addr.s_addr) {
41       adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
42       return;
43     }
44   }
45   
46   if (ads->nservers>=MAXSERVERS) {
47     adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr));
48     return;
49   }
50
51   ss= ads->servers+ads->nservers;
52   ss->addr= addr;
53   ads->nservers++;
54 }
55
56 static void configparseerr(adns_state ads, const char *fn, int lno,
57                            const char *fmt, ...) {
58   va_list al;
59   
60   if (ads->iflags & adns_if_noerrprint) return;
61   if (lno==-1) fprintf(stderr,"adns: %s: ",fn);
62   else fprintf(stderr,"adns: %s:%d: ",fn,lno);
63   va_start(al,fmt);
64   vfprintf(stderr,fmt,al);
65   va_end(al);
66   fputc('\n',stderr);
67 }
68
69 static void ccf_nameserver(adns_state ads, const char *fn, int lno, const char *buf) {
70   struct in_addr ia;
71   
72   if (!inet_aton(buf,&ia)) {
73     configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
74     return;
75   }
76   adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
77   addserver(ads,ia);
78 }
79
80 static void ccf_search(adns_state ads, const char *fn, int lno, const char *buf) {
81   if (!buf) return;
82   adns__diag(ads,-1,0,"warning - `search' ignored fixme");
83 }
84
85 static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) {
86   adns__diag(ads,-1,0,"warning - `sortlist' ignored fixme");
87 }
88
89 static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) {
90   if (!buf) return;
91   adns__diag(ads,-1,0,"warning - `options' ignored fixme");
92 }
93
94 static void ccf_clearnss(adns_state ads, const char *fn, int lno, const char *buf) {
95   ads->nservers= 0;
96 }
97
98 static const struct configcommandinfo {
99   const char *name;
100   void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
101 } configcommandinfos[]= {
102   { "nameserver",        ccf_nameserver  },
103   { "domain",            ccf_search      },
104   { "search",            ccf_search      },
105   { "sortlist",          ccf_sortlist    },
106   { "options",           ccf_options     },
107   { "clearnameservers",  ccf_clearnss    },
108   {  0                                   }
109 };
110
111 static void readconfig(adns_state ads, const char *filename) {
112   char linebuf[2000], *p, *q;
113   FILE *file;
114   int lno, l, c;
115   const struct configcommandinfo *ccip;
116
117   file= fopen(filename,"r");
118   if (!file) {
119     if (errno == ENOENT) {
120       adns__debug(ads,-1,0,"configuration file `%s' does not exist",filename);
121       return;
122     }
123     adns__diag(ads,-1,0,"cannot open configuration file `%s': %s",
124                filename,strerror(errno));
125     return;
126   }
127
128   for (lno=1; fgets(linebuf,sizeof(linebuf),file); lno++) {
129     l= strlen(linebuf);
130     if (!l) continue;
131     if (linebuf[l-1] != '\n' && !feof(file)) {
132       adns__diag(ads,-1,0,"%s:%d: line too long",filename,lno);
133       while ((c= getc(file)) != EOF && c != '\n') { }
134       if (c == EOF) break;
135       continue;
136     }
137     while (l>0 && ctype_whitespace(linebuf[l-1])) l--;
138     linebuf[l]= 0;
139     p= linebuf;
140     while (ctype_whitespace(*p)) p++;
141     if (*p == '#' || *p == '\n') continue;
142     q= p;
143     while (*q && !ctype_whitespace(*q)) q++;
144     for (ccip=configcommandinfos;
145          ccip->name && strncmp(ccip->name,p,q-p);
146          ccip++);
147     if (!ccip->name) {
148       adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
149                  filename,lno,q-p,p);
150       continue;
151     }
152     while (ctype_whitespace(*q)) q++;
153     ccip->fn(ads,filename,lno,q);
154   }
155   if (ferror(file)) {
156     adns__diag(ads,-1,0,"%s:%d: read error: %s",filename,lno,strerror(errno));
157   }
158   fclose(file);
159 }
160
161 static const char *instrum_getenv(adns_state ads, const char *envvar) {
162   const char *value;
163
164   value= getenv(envvar);
165   if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
166   else adns__debug(ads,-1,0,"environment variable %s set to `%s'",envvar,value);
167   return value;
168 }
169
170 static void readconfigenv(adns_state ads, const char *envvar) {
171   const char *filename;
172
173   if (ads->iflags & adns_if_noenv) {
174     adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
175     return;
176   }
177   filename= instrum_getenv(ads,envvar);
178   if (filename) readconfig(ads,filename);
179 }
180
181
182 int adns__setnonblock(adns_state ads, int fd) {
183   int r;
184   
185   r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
186   r |= O_NONBLOCK;
187   r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
188   return 0;
189 }
190
191 int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
192   adns_state ads;
193   const char *res_options, *adns_res_options;
194   struct protoent *proto;
195   int r;
196   
197   ads= malloc(sizeof(*ads)); if (!ads) return errno;
198   ads->iflags= flags;
199   ads->diagfile= diagfile ? diagfile : stderr;
200   LIST_INIT(ads->timew);
201   LIST_INIT(ads->childw);
202   LIST_INIT(ads->output);
203   ads->nextid= 0x311f;
204   ads->udpsocket= ads->tcpsocket= -1;
205   adns__vbuf_init(&ads->tcpsend);
206   adns__vbuf_init(&ads->tcprecv);
207   ads->nservers= ads->tcpserver= 0;
208   ads->tcpstate= server_disconnected;
209   timerclear(&ads->tcptimeout);
210
211   res_options= instrum_getenv(ads,"RES_OPTIONS");
212   adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
213   ccf_options(ads,"RES_OPTIONS",-1,res_options);
214   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
215
216   readconfig(ads,"/etc/resolv.conf");
217   readconfigenv(ads,"RES_CONF");
218   readconfigenv(ads,"ADNS_RES_CONF");
219
220   ccf_options(ads,"RES_OPTIONS",-1,res_options);
221   ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
222
223   ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN"));
224   ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN"));
225
226   if (!ads->nservers) {
227     struct in_addr ia;
228     if (ads->iflags & adns_if_debug)
229       fprintf(stderr,"adns: no nameservers, using localhost\n");
230     ia.s_addr= INADDR_LOOPBACK;
231     addserver(ads,ia);
232   }
233
234   proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
235   ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
236   if (ads->udpsocket<0) { r= errno; goto x_free; }
237
238   r= adns__setnonblock(ads,ads->udpsocket);
239   if (r) { r= errno; goto x_closeudp; }
240   
241   *ads_r= ads;
242   return 0;
243
244  x_closeudp:
245   close(ads->udpsocket);
246  x_free:
247   free(ads);
248   return r;
249 }
250
251 void adns_finish(adns_state ads) {
252   for (;;) {
253     if (ads->timew.head) adns_cancel(ads->timew.head);
254     else if (ads->childw.head) adns_cancel(ads->childw.head);
255     else if (ads->output.head) adns_cancel(ads->output.head);
256     else break;
257   }
258   close(ads->udpsocket);
259   if (ads->tcpsocket >= 0) close(ads->tcpsocket);
260   adns__vbuf_free(&ads->tcpsend);
261   adns__vbuf_free(&ads->tcprecv);
262   free(ads);
263 }