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
->input
.head
= ads
->input
.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
));
284 LIST_LINK_TAIL(ads
->input
,qu
);
287 int adns_finish(adns_state ads
) {
291 void adns_interest(adns_state ads
, int *maxfd
,
292 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
293 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
297 static void autosys(adns_state ads
) {
298 if (ads
->iflags
& adns_if_noautosys
) return;
299 adns_callback(ads
,-1,0,0,0);
302 void adns_cancel(adns_state ads
, adns_query query
) {
306 int adns_callback(adns_state ads
, int maxfd
,
307 const fd_set
*readfds
, const fd_set
*writefds
,
308 const fd_set
*exceptfds
) {
312 static int internal_check(adns_state ads
,
313 adns_query
*query_io
,
319 int adns_wait(adns_state ads
,
320 adns_query
*query_io
,
323 int r
, maxfd
, rsel
, rcb
;
324 fd_set readfds
, writefds
, exceptfds
;
325 struct timeval tvbuf
, *tvp
;
328 r
= internal_check(ads
,query_io
,answer
,context_r
);
329 if (r
&& r
!= EWOULDBLOCK
) return r
;
330 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
332 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
333 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
334 if (rsel
==-1) return r
;
335 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
340 int adns_check(adns_state ads
,
341 adns_query
*query_io
,
345 return internal_check(ads
,query_io
,answer
,context_r
);
348 int adns_synchronous(adns_state ads
,
351 adns_queryflags flags
,
352 adns_answer
*answer
) {
356 r
= adns_submit(ads
,owner
,type
,flags
,0,&qu
);
360 r
= adns_wait(ads
,&qu
,answer
,0);
362 if (r
) adns_cancel(ads
,qu
);
366 static adns_status
mkquery(adns_state ads
, const char *owner
, int ol
, int id
,
367 adns_rrtype type
, adns_queryflags flags
, int *qml_r
) {
368 int ll
, c
, nlabs
, qbufreq
;
369 unsigned char label
[255], *nqbuf
;
372 #define MKQUERY_ADDB(b) *nqbuf++= (b)
373 #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
375 qbufreq
= 12+strlen(owner
)+3;
376 if (ads
->qbufavail
< qbufreq
) {
377 nqbuf
= realloc(ads
->qbuf
,qbufreq
);
378 if (!nqbuf
) return adns_s_nolocalmem
;
379 ads
->qbuf
= nqbuf
; ads
->qbufavail
= qbufreq
;
384 MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
385 MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
386 MKQUERY_ADDW(1); /* QDCOUNT=1 */
387 MKQUERY_ADDW(0); /* ANCOUNT=0 */
388 MKQUERY_ADDW(0); /* NSCOUNT=0 */
389 MKQUERY_ADDW(0); /* ARCOUNT=0 */
390 p
= owner
; pe
= owner
+ol
;
392 if (!*p
) return adns_s_invaliddomain
;
395 while (p
!=pe
&& (c
= *p
++)!='.') {
397 if (!(flags
& adns_f_anyquote
)) return adns_s_invaliddomain
;
398 if (ctype_digit(p
[0])) {
399 if (ctype_digit(p
[1]) && ctype_digit(p
[2])) {
400 c
= (*p
++ - '0')*100 + (*p
++ - '0')*10 + (*p
++ - '0');
401 if (c
>= 256) return adns_s_invaliddomain
;
403 return adns_s_invaliddomain
;
405 } else if (!(c
= *p
++)) {
406 return adns_s_invaliddomain
;
409 if (!(flags
& adns_f_anyquote
)) {
410 if ((c
>= '0' && c
<= '9') || c
== '-') {
411 if (!ll
) return adns_s_invaliddomain
;
412 } else if ((c
< 'a' || c
> 'z') && (c
< 'A' && c
> 'Z')) {
413 return adns_s_invaliddomain
;
416 if (ll
== sizeof(label
)) return adns_s_invaliddomain
;
419 if (!ll
) return adns_s_invaliddomain
;
420 if (nlabs
++ > 63) return adns_s_invaliddomain
;
422 memcpy(nqbuf
,label
,ll
); nqbuf
+= ll
;
426 MKQUERY_ADDW(type
& adns__rrt_typemask
); /* QTYPE */
427 MKQUERY_ADDW(1); /* QCLASS=IN */
429 *qml_r
= nqbuf
- ads
->qbuf
;
434 static adns_query
allocquery(adns_state ads
, const char *owner
, int ol
,
435 int qml
, int id
, adns_rrtype type
,
436 adns_queryflags flags
, void *context
) {
440 qu
= malloc(sizeof(*qu
)+ol
+1+qml
); if (!qu
) return 0;
441 qu
->next
= qu
->back
= qu
->parent
= qu
->child
= 0;
446 qu
->context
= context
;
448 qu
->sentudp
= qu
->senttcp
= 0;
450 memcpy(qu
->owner
,owner
,ol
); qu
->owner
[ol
]= 0;
451 qu
->querymsg
= qm
= qu
->owner
+ol
+1;
452 memcpy(qm
,ads
->qbuf
,qml
);
457 static int failsubmit(adns_state ads
, void *context
, adns_query
*query_r
,
458 adns_rrtype type
, adns_queryflags flags
,
459 int id
, adns_status stat
) {
462 qu
= allocquery(ads
,0,0,0,id
,type
,flags
,context
); if (!qu
) return errno
;
463 query_fail(ads
,qu
,stat
);
468 int adns_submit(adns_state ads
,
471 adns_queryflags flags
,
473 adns_query
*query_r
) {
481 if (ol
<=1 || ol
>MAXDNAME
+1)
482 return failsubmit(ads
,context
,query_r
,type
,flags
,id
,adns_s_invaliddomain
);
483 if (owner
[ol
-1]=='.' && owner
[ol
-2]!='\\') { flags
&= ~adns_f_search
; ol
--; }
485 stat
= mkquery(ads
,owner
,ol
,id
,type
,flags
,&qml
);
486 if (stat
) return failsubmit(ads
,context
,query_r
,type
,flags
,id
,stat
);
488 qu
= allocquery(ads
,owner
,ol
,qml
,id
,type
,flags
,context
); if (!qu
) return errno
;
490 LIST_LINK_TAIL(ads
->input
,qu
);