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 ctype_whitespace(int c
) { return c
==' ' || c
=='\n' || c
=='\t'; }
144 static int ctype_digit(int c
) { return c
>='0' && c
<='9'; }
146 static void readconfig(adns_state ads
, const char *filename
) {
147 char linebuf
[2000], *p
, *q
;
150 const struct configcommandinfo
*ccip
;
152 file
= fopen(filename
,"r");
154 if (errno
== ENOENT
) {
155 debug(ads
,"configuration file `%s' does not exist",filename
);
158 diag(ads
,"cannot open configuration file `%s': %s",filename
,strerror(errno
));
162 for (lno
=1; fgets(linebuf
,sizeof(linebuf
),file
); lno
++) {
165 if (linebuf
[l
-1] != '\n' && !feof(file
)) {
166 diag(ads
,"%s:%d: line too long",filename
,lno
);
167 while ((c
= getc(file
)) != EOF
&& c
!= '\n') { }
171 while (l
>0 && ctype_whitespace(linebuf
[l
-1])) l
--;
174 while (ctype_whitespace(*p
)) p
++;
175 if (*p
== '#' || *p
== '\n') continue;
177 while (*q
&& !ctype_whitespace(*q
)) q
++;
178 for (ccip
=configcommandinfos
;
179 ccip
->name
&& strncmp(ccip
->name
,p
,q
-p
);
182 diag(ads
,"%s:%d: unknown configuration directive `%.*s'",filename
,lno
,q
-p
,p
);
185 while (ctype_whitespace(*q
)) q
++;
186 ccip
->fn(ads
,filename
,lno
,q
);
189 diag(ads
,"%s:%d: read error: %s",filename
,lno
,strerror(errno
));
194 static const char *instrum_getenv(adns_state ads
, const char *envvar
) {
197 value
= getenv(envvar
);
198 if (!value
) debug(ads
,"environment variable %s not set",envvar
);
199 else debug(ads
,"environment variable %s set to `%s'",envvar
,value
);
203 static void readconfigenv(adns_state ads
, const char *envvar
) {
204 const char *filename
;
206 if (ads
->iflags
& adns_if_noenv
) {
207 debug(ads
,"not checking environment variable `%s'",envvar
);
210 filename
= instrum_getenv(ads
,envvar
);
211 if (filename
) readconfig(ads
,filename
);
214 int adns_init(adns_state
*ads_r
, adns_initflags flags
) {
216 const char *res_options
, *adns_res_options
;
217 struct protoent
*proto
;
220 ads
= malloc(sizeof(*ads
)); if (!ads
) return errno
;
221 ads
->tosend
.head
= ads
->tosend
.tail
= 0;
222 ads
->timew
.head
= ads
->timew
.tail
= 0;
223 ads
->childw
.head
= ads
->childw
.tail
= 0;
224 ads
->output
.head
= ads
->output
.tail
= 0;
229 ads
->tcpbufavail
= ads
->tcpbufused
= ads
->tcpbufdone
= 0;
235 res_options
= instrum_getenv(ads
,"RES_OPTIONS");
236 adns_res_options
= instrum_getenv(ads
,"ADNS_RES_OPTIONS");
237 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
238 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
240 readconfig(ads
,"/etc/resolv.conf");
241 readconfigenv(ads
,"RES_CONF");
242 readconfigenv(ads
,"ADNS_RES_CONF");
244 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
245 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
247 ccf_search(ads
,"LOCALDOMAIN",-1,instrum_getenv(ads
,"LOCALDOMAIN"));
248 ccf_search(ads
,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads
,"ADNS_LOCALDOMAIN"));
250 if (!ads
->nservers
) {
252 if (ads
->iflags
& adns_if_debug
)
253 fprintf(stderr
,"adns: no nameservers, using localhost\n");
254 ia
.s_addr
= INADDR_LOOPBACK
;
258 proto
= getprotobyname("udp"); if (!proto
) { r
= ENOPROTOOPT
; goto x_free
; }
259 ads
->udpsocket
= socket(AF_INET
,SOCK_DGRAM
,proto
->p_proto
);
260 if (!ads
->udpsocket
) { r
= errno
; goto x_closeudp
; }
266 close(ads
->udpsocket
);
272 static void query_fail(adns_state ads
, adns_query qu
, adns_status stat
) {
276 if (!ans
) ans
= malloc(sizeof(*qu
->answer
));
285 LIST_LINK_TAIL(ads
->output
,qu
);
288 int adns_finish(adns_state ads
) {
292 void adns_interest(adns_state ads
, int *maxfd
,
293 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
294 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
298 static void autosys(adns_state ads
) {
299 if (ads
->iflags
& adns_if_noautosys
) return;
300 adns_callback(ads
,-1,0,0,0);
303 void adns_cancel(adns_state ads
, adns_query query
) {
307 int adns_callback(adns_state ads
, int maxfd
,
308 const fd_set
*readfds
, const fd_set
*writefds
,
309 const fd_set
*exceptfds
) {
313 static int internal_check(adns_state ads
,
314 adns_query
*query_io
,
315 adns_answer
**answer
,
321 if (!ads
->output
.head
) return EWOULDBLOCK
;
322 qu
= ads
->output
.head
;
324 if (qu
->id
>=0) return EWOULDBLOCK
;
326 LIST_UNLINK(ads
->output
,qu
);
328 if (context_r
) *context_r
= qu
->context
;
333 int adns_wait(adns_state ads
,
334 adns_query
*query_io
,
335 adns_answer
**answer_r
,
337 int r
, maxfd
, rsel
, rcb
;
338 fd_set readfds
, writefds
, exceptfds
;
339 struct timeval tvbuf
, *tvp
;
342 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
343 if (r
&& r
!= EWOULDBLOCK
) return r
;
344 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
346 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
347 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
348 if (rsel
==-1) return r
;
349 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
354 int adns_check(adns_state ads
,
355 adns_query
*query_io
,
356 adns_answer
**answer_r
,
359 return internal_check(ads
,query_io
,answer_r
,context_r
);
362 int adns_synchronous(adns_state ads
,
365 adns_queryflags flags
,
366 adns_answer
**answer_r
) {
370 r
= adns_submit(ads
,owner
,type
,flags
,0,&qu
);
374 r
= adns_wait(ads
,&qu
,answer_r
,0);
376 if (r
) adns_cancel(ads
,qu
);
380 static adns_status
mkquery(adns_state ads
, const char *owner
, int ol
, int id
,
381 adns_rrtype type
, adns_queryflags flags
, int *qml_r
) {
382 int ll
, c
, nlabs
, qbufreq
;
383 unsigned char label
[255], *nqbuf
;
386 #define MKQUERY_ADDB(b) *nqbuf++= (b)
387 #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
389 qbufreq
= 12+strlen(owner
)+3;
390 if (ads
->qbufavail
< qbufreq
) {
391 nqbuf
= realloc(ads
->qbuf
,qbufreq
);
392 if (!nqbuf
) return adns_s_nolocalmem
;
393 ads
->qbuf
= nqbuf
; ads
->qbufavail
= qbufreq
;
398 MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
399 MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
400 MKQUERY_ADDW(1); /* QDCOUNT=1 */
401 MKQUERY_ADDW(0); /* ANCOUNT=0 */
402 MKQUERY_ADDW(0); /* NSCOUNT=0 */
403 MKQUERY_ADDW(0); /* ARCOUNT=0 */
404 p
= owner
; pe
= owner
+ol
;
406 if (!*p
) return adns_s_invaliddomain
;
409 while (p
!=pe
&& (c
= *p
++)!='.') {
411 if (!(flags
& adns_f_anyquote
)) return adns_s_invaliddomain
;
412 if (ctype_digit(p
[0])) {
413 if (ctype_digit(p
[1]) && ctype_digit(p
[2])) {
414 c
= (*p
++ - '0')*100 + (*p
++ - '0')*10 + (*p
++ - '0');
415 if (c
>= 256) return adns_s_invaliddomain
;
417 return adns_s_invaliddomain
;
419 } else if (!(c
= *p
++)) {
420 return adns_s_invaliddomain
;
423 if (!(flags
& adns_f_anyquote
)) {
424 if ((c
>= '0' && c
<= '9') || c
== '-') {
425 if (!ll
) return adns_s_invaliddomain
;
426 } else if ((c
< 'a' || c
> 'z') && (c
< 'A' && c
> 'Z')) {
427 return adns_s_invaliddomain
;
430 if (ll
== sizeof(label
)) return adns_s_invaliddomain
;
433 if (!ll
) return adns_s_invaliddomain
;
434 if (nlabs
++ > 63) return adns_s_invaliddomain
;
436 memcpy(nqbuf
,label
,ll
); nqbuf
+= ll
;
440 MKQUERY_ADDW(type
& adns__rrt_typemask
); /* QTYPE */
441 MKQUERY_ADDW(1); /* QCLASS=IN */
443 *qml_r
= nqbuf
- ads
->qbuf
;
448 static adns_query
allocquery(adns_state ads
, const char *owner
, int ol
,
449 int qml
, int id
, adns_rrtype type
,
450 adns_queryflags flags
, void *context
) {
454 qu
= malloc(sizeof(*qu
)+ol
+1+qml
); if (!qu
) return 0;
455 qu
->next
= qu
->back
= qu
->parent
= qu
->child
= 0;
460 qu
->context
= context
;
462 qu
->sentudp
= qu
->senttcp
= 0;
464 memcpy(qu
->owner
,owner
,ol
); qu
->owner
[ol
]= 0;
465 qu
->querymsg
= qm
= qu
->owner
+ol
+1;
466 memcpy(qm
,ads
->qbuf
,qml
);
471 static int failsubmit(adns_state ads
, void *context
, adns_query
*query_r
,
472 adns_rrtype type
, adns_queryflags flags
,
473 int id
, adns_status stat
) {
476 qu
= allocquery(ads
,0,0,0,id
,type
,flags
,context
); if (!qu
) return errno
;
477 query_fail(ads
,qu
,stat
);
482 static void trysendudp(adns_state ads
, adns_query qu
) {
483 struct sockaddr_in servaddr
;
484 /* FIXME: _f_usevc not implemented */
485 memset(&servaddr
,0,sizeof(servaddr
));
486 servaddr
.sin_family
= AF_INET
;
487 servaddr
.sin_addr
= ads
->servers
[qu
->nextserver
].addr
;
488 servaddr
.sin_port
= htons(53);
489 sendto(ads
->udpsocket
,qu
->querymsg
,qu
->querylen
,0,&servaddr
,sizeof(servaddr
));
492 int adns_submit(adns_state ads
,
495 adns_queryflags flags
,
497 adns_query
*query_r
) {
505 if (ol
<=1 || ol
>MAXDNAME
+1)
506 return failsubmit(ads
,context
,query_r
,type
,flags
,id
,adns_s_invaliddomain
);
507 if (owner
[ol
-1]=='.' && owner
[ol
-2]!='\\') { flags
&= ~adns_f_search
; ol
--; }
509 stat
= mkquery(ads
,owner
,ol
,id
,type
,flags
,&qml
);
510 if (stat
) return failsubmit(ads
,context
,query_r
,type
,flags
,id
,stat
);
512 qu
= allocquery(ads
,owner
,ol
,qml
,id
,type
,flags
,context
); if (!qu
) return errno
;
514 LIST_LINK_TAIL(ads
->tosend
,qu
);