12 #include <arpa/nameser.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
17 #include "adns-internal.h"
19 #define LIST_UNLINK(list,node) \
21 if ((node)->back) (node)->back->next= (node)->next; \
22 else (list).head= (node)->next; \
23 if ((node)->next) (node)->next->back= (node)->back; \
24 else (list).tail= (node)->back; \
27 #define LIST_LINK_TAIL(list,node) \
30 (node)->next= (list).tail; \
31 if ((list).tail) (list).tail->back= (node); else (list).head= (node); \
32 (list).tail= (node); \
35 static void vdebug(adns_state ads
, const char *fmt
, va_list al
) {
36 if (!(ads
->iflags
& adns_if_debug
)) return;
37 fputs("adns debug: ",stderr
);
38 vfprintf(stderr
,fmt
,al
);
42 static void debug(adns_state ads
, const char *fmt
, ...) {
50 static void vdiag(adns_state ads
, const char *fmt
, va_list al
) {
51 if (ads
->iflags
& adns_if_noerrprint
) return;
52 fputs("adns: ",stderr
);
53 vfprintf(stderr
,fmt
,al
);
57 static void diag(adns_state ads
, const char *fmt
, ...) {
65 static void addserver(adns_state ads
, struct in_addr addr
) {
69 for (i
=0; i
<ads
->nservers
; i
++) {
70 if (ads
->servers
[i
].addr
.s_addr
== addr
.s_addr
) {
71 debug(ads
,"duplicate nameserver %s ignored",inet_ntoa(addr
));
76 if (ads
->nservers
>=MAXSERVERS
) {
77 diag(ads
,"too many nameservers, ignoring %s",inet_ntoa(addr
));
81 ss
= ads
->servers
+ads
->nservers
;
83 ss
->state
= server_disc
;
84 ss
->connw
.head
= ss
->connw
.tail
= 0;
88 static void configparseerr(adns_state ads
, const char *fn
, int lno
,
89 const char *fmt
, ...) {
92 if (ads
->iflags
& adns_if_noerrprint
) return;
93 if (lno
==-1) fprintf(stderr
,"adns: %s: ",fn
);
94 else fprintf(stderr
,"adns: %s:%d: ",fn
,lno
);
96 vfprintf(stderr
,fmt
,al
);
101 static void ccf_nameserver(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
104 if (!inet_aton(buf
,&ia
)) {
105 configparseerr(ads
,fn
,lno
,"invalid nameserver address `%s'",buf
);
108 debug(ads
,"using nameserver %s",inet_ntoa(ia
));
112 static void ccf_search(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
114 diag(ads
,"warning - `search' ignored FIXME");
117 static void ccf_sortlist(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
118 diag(ads
,"warning - `sortlist' ignored FIXME");
121 static void ccf_options(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
123 diag(ads
,"warning - `options' ignored FIXME");
126 static void ccf_clearnss(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
130 static const struct configcommandinfo
{
132 void (*fn
)(adns_state ads
, const char *fn
, int lno
, const char *buf
);
133 } configcommandinfos
[]= {
134 { "nameserver", ccf_nameserver
},
135 { "domain", ccf_search
},
136 { "search", ccf_search
},
137 { "sortlist", ccf_sortlist
},
138 { "options", ccf_options
},
139 { "clearnameservers", ccf_clearnss
},
143 static int whitespace(int c
) {
144 return c
==' ' || c
=='\n' || c
=='\t';
147 static void readconfig(adns_state ads
, const char *filename
) {
148 char linebuf
[2000], *p
, *q
;
151 const struct configcommandinfo
*ccip
;
153 file
= fopen(filename
,"r");
155 if (errno
== ENOENT
) {
156 debug(ads
,"configuration file `%s' does not exist",filename
);
159 diag(ads
,"cannot open configuration file `%s': %s",filename
,strerror(errno
));
163 for (lno
=1; fgets(linebuf
,sizeof(linebuf
),file
); lno
++) {
166 if (linebuf
[l
-1] != '\n' && !feof(file
)) {
167 diag(ads
,"%s:%d: line too long",filename
,lno
);
168 while ((c
= getc(file
)) != EOF
&& c
!= '\n') { }
172 while (l
>0 && whitespace(linebuf
[l
-1])) l
--;
175 while (whitespace(*p
)) p
++;
176 if (*p
== '#' || *p
== '\n') continue;
178 while (*q
&& !whitespace(*q
)) q
++;
179 for (ccip
=configcommandinfos
;
180 ccip
->name
&& strncmp(ccip
->name
,p
,q
-p
);
183 diag(ads
,"%s:%d: unknown configuration directive `%.*s'",filename
,lno
,q
-p
,p
);
186 while (whitespace(*q
)) q
++;
187 ccip
->fn(ads
,filename
,lno
,q
);
190 diag(ads
,"%s:%d: read error: %s",filename
,lno
,strerror(errno
));
195 static const char *instrum_getenv(adns_state ads
, const char *envvar
) {
198 value
= getenv(envvar
);
199 if (!value
) debug(ads
,"environment variable %s not set",envvar
);
200 else debug(ads
,"environment variable %s set to `%s'",envvar
,value
);
204 static void readconfigenv(adns_state ads
, const char *envvar
) {
205 const char *filename
;
207 if (ads
->iflags
& adns_if_noenv
) {
208 debug(ads
,"not checking environment variable `%s'",envvar
);
211 filename
= instrum_getenv(ads
,envvar
);
212 if (filename
) readconfig(ads
,filename
);
215 int adns_init(adns_state
*ads_r
, adns_initflags flags
) {
217 const char *res_options
, *adns_res_options
;
218 struct protoent
*proto
;
219 struct sockaddr_in udpaddr
;
222 ads
= malloc(sizeof(*ads
)); if (!ads
) return errno
;
223 ads
->input
.head
= ads
->input
.tail
= 0;
224 ads
->timew
.head
= ads
->timew
.tail
= 0;
225 ads
->childw
.head
= ads
->childw
.tail
= 0;
226 ads
->output
.head
= ads
->output
.tail
= 0;
231 ads
->tcpbufavail
= ads
->tcpbufused
= ads
->tcpbufdone
= 0;
237 res_options
= instrum_getenv(ads
,"RES_OPTIONS");
238 adns_res_options
= instrum_getenv(ads
,"ADNS_RES_OPTIONS");
239 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
240 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
242 readconfig(ads
,"/etc/resolv.conf");
243 readconfigenv(ads
,"RES_CONF");
244 readconfigenv(ads
,"ADNS_RES_CONF");
246 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
247 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
249 ccf_search(ads
,"LOCALDOMAIN",-1,instrum_getenv(ads
,"LOCALDOMAIN"));
250 ccf_search(ads
,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads
,"ADNS_LOCALDOMAIN"));
252 if (!ads
->nservers
) {
254 if (ads
->iflags
& adns_if_debug
)
255 fprintf(stderr
,"adns: no nameservers, using localhost\n");
256 ia
.s_addr
= INADDR_LOOPBACK
;
260 proto
= getprotobyname("udp"); if (!proto
) { r
= ENOPROTOOPT
; goto x_free
; }
261 ads
->udpsocket
= socket(AF_INET
,SOCK_DGRAM
,proto
->p_proto
);
262 if (!ads
->udpsocket
) { r
= errno
; goto x_closeudp
; }
264 memset(&udpaddr
,0,sizeof(udpaddr
));
265 udpaddr
.sin_family
= AF_INET
;
266 udpaddr
.sin_addr
.s_addr
= INADDR_ANY
;
268 r
= bind(ads
->udpsocket
,&udpaddr
,sizeof(udpaddr
));
269 if (r
) { r
= errno
; goto x_closeudp
; }
271 udpaddrlen
= sizeof(udpaddr
);
272 r
= getsockname(ads
->udpsocket
,&udpaddr
,&udpaddrlen
);
273 if (r
) { r
= errno
; goto x_closeudp
; }
274 if (udpaddr
.sin_family
!= AF_INET
) {
275 diag(ads
,"network API error: UDP socket not AF_INET but %lu",
276 (unsigned long)udpaddr
.sin_family
);
277 r
= EPROTOTYPE
; goto x_closeudp
;
279 debug(ads
,"UDP socket is %s:%u",inet_ntoa(udpaddr
.sin_addr
),ntohs(udpaddr
.sin_port
));
285 close(ads
->udpsocket
);
291 static void query_fail(adns_state ads
, adns_query qu
, adns_status stat
) {
295 if (!ans
) ans
= malloc(sizeof(*qu
->answer
));
303 LIST_LINK_TAIL(ads
->input
,qu
);
306 int adns_finish(adns_state ads
) {
310 void adns_interest(adns_state ads
, int *maxfd
,
311 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
312 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
316 static void autosys(adns_state ads
) {
317 if (ads
->iflags
& adns_if_noautosys
) return;
318 adns_callback(ads
,-1,0,0,0);
321 void adns_cancel(adns_state ads
, adns_query query
) {
325 int adns_callback(adns_state ads
, int maxfd
,
326 const fd_set
*readfds
, const fd_set
*writefds
,
327 const fd_set
*exceptfds
) {
331 static int internal_check(adns_state ads
,
332 adns_query
*query_io
,
338 int adns_wait(adns_state ads
,
339 adns_query
*query_io
,
342 int r
, maxfd
, rsel
, rcb
;
343 fd_set readfds
, writefds
, exceptfds
;
344 struct timeval tvbuf
, *tvp
;
347 r
= internal_check(ads
,query_io
,answer
,context_r
);
348 if (r
&& r
!= EWOULDBLOCK
) return r
;
349 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
351 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
352 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
353 if (rsel
==-1) return r
;
354 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
359 int adns_check(adns_state ads
,
360 adns_query
*query_io
,
364 return internal_check(ads
,query_io
,answer
,context_r
);
367 int adns_synchronous(adns_state ads
,
370 adns_queryflags flags
,
371 adns_answer
*answer
) {
375 r
= adns_submit(ads
,owner
,type
,flags
,0,&qu
);
379 r
= adns_wait(ads
,&qu
,answer
,0);
381 if (r
) adns_cancel(ads
,qu
);
385 static int mkquery(adns_state ads
,
391 int adns_submit(adns_state ads
,
394 adns_queryflags flags
,
396 adns_query
*query_r
) {
403 if (ol
>MAXDNAME
+1) { stat
= adns_s_invaliddomain
; ol
= 0; }
404 if (ol
>0 && owner
[ol
-1]=='.') { flags
&= ~adns_f_search
; ol
--; }
405 qu
= malloc(sizeof(*qu
)+ol
+1); if (!qu
) return errno
;
406 qu
->next
= qu
->back
= qu
->parent
= qu
->child
= 0;
407 qu
->id
= ads
->nextid
++;
411 qu
->context
= context
;
413 qu
->sentudp
= qu
->senttcp
= 0;
415 memcpy(qu
->owner
,owner
,ol
); qu
->owner
[ol
]= 0;
418 mkquery(ads
,owner
,type
);
421 query_fail(ads
,qu
,stat
);
423 LIST_LINK_TAIL(ads
->input
,qu
);