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_PART(list,node,part) \
21 if ((node)->back) (node)->back->part next= (node)->part next; \
22 else (list).head= (node)->part next; \
23 if ((node)->next) (node)->next->part back= (node)->part back; \
24 else (list).tail= (node)->part back; \
27 #define LIST_LINK_TAIL_PART(list,node,part) \
29 (node)->part back= 0; \
30 (node)->part next= (list).tail; \
31 if ((list).tail) (list).tail->part back= (node); else (list).part head= (node); \
32 (list).tail= (node); \
35 #define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
36 #define LIST_LINK_TAIL_PART(list,node) LIST_LINK_TAIL(list,node,)
38 static void vdebug(adns_state ads
, const char *fmt
, va_list al
) {
39 if (!(ads
->iflags
& adns_if_debug
)) return;
40 fputs("adns debug: ",stderr
);
41 vfprintf(stderr
,fmt
,al
);
45 static void debug(adns_state ads
, const char *fmt
, ...) {
53 static void vdiag(adns_state ads
, const char *fmt
, va_list al
) {
54 if (ads
->iflags
& adns_if_noerrprint
) return;
55 fputs("adns: ",stderr
);
56 vfprintf(stderr
,fmt
,al
);
60 static void diag(adns_state ads
, const char *fmt
, ...) {
68 static void addserver(adns_state ads
, struct in_addr addr
) {
72 for (i
=0; i
<ads
->nservers
; i
++) {
73 if (ads
->servers
[i
].addr
.s_addr
== addr
.s_addr
) {
74 debug(ads
,"duplicate nameserver %s ignored",inet_ntoa(addr
));
79 if (ads
->nservers
>=MAXSERVERS
) {
80 diag(ads
,"too many nameservers, ignoring %s",inet_ntoa(addr
));
84 ss
= ads
->servers
+ads
->nservers
;
86 ss
->state
= server_disc
;
87 ss
->connw
.head
= ss
->connw
.tail
= 0;
91 static void configparseerr(adns_state ads
, const char *fn
, int lno
,
92 const char *fmt
, ...) {
95 if (ads
->iflags
& adns_if_noerrprint
) return;
96 if (lno
==-1) fprintf(stderr
,"adns: %s: ",fn
);
97 else fprintf(stderr
,"adns: %s:%d: ",fn
,lno
);
99 vfprintf(stderr
,fmt
,al
);
104 static void ccf_nameserver(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
107 if (!inet_aton(buf
,&ia
)) {
108 configparseerr(ads
,fn
,lno
,"invalid nameserver address `%s'",buf
);
111 debug(ads
,"using nameserver %s",inet_ntoa(ia
));
115 static void ccf_search(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
117 diag(ads
,"warning - `search' ignored FIXME");
120 static void ccf_sortlist(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
121 diag(ads
,"warning - `sortlist' ignored FIXME");
124 static void ccf_options(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
126 diag(ads
,"warning - `options' ignored FIXME");
129 static void ccf_clearnss(adns_state ads
, const char *fn
, int lno
, const char *buf
) {
133 static const struct configcommandinfo
{
135 void (*fn
)(adns_state ads
, const char *fn
, int lno
, const char *buf
);
136 } configcommandinfos
[]= {
137 { "nameserver", ccf_nameserver
},
138 { "domain", ccf_search
},
139 { "search", ccf_search
},
140 { "sortlist", ccf_sortlist
},
141 { "options", ccf_options
},
142 { "clearnameservers", ccf_clearnss
},
146 static int ctype_whitespace(int c
) { return c
==' ' || c
=='\n' || c
=='\t'; }
147 static int ctype_digit(int c
) { return c
>='0' && c
<='9'; }
149 static void readconfig(adns_state ads
, const char *filename
) {
150 char linebuf
[2000], *p
, *q
;
153 const struct configcommandinfo
*ccip
;
155 file
= fopen(filename
,"r");
157 if (errno
== ENOENT
) {
158 debug(ads
,"configuration file `%s' does not exist",filename
);
161 diag(ads
,"cannot open configuration file `%s': %s",filename
,strerror(errno
));
165 for (lno
=1; fgets(linebuf
,sizeof(linebuf
),file
); lno
++) {
168 if (linebuf
[l
-1] != '\n' && !feof(file
)) {
169 diag(ads
,"%s:%d: line too long",filename
,lno
);
170 while ((c
= getc(file
)) != EOF
&& c
!= '\n') { }
174 while (l
>0 && ctype_whitespace(linebuf
[l
-1])) l
--;
177 while (ctype_whitespace(*p
)) p
++;
178 if (*p
== '#' || *p
== '\n') continue;
180 while (*q
&& !ctype_whitespace(*q
)) q
++;
181 for (ccip
=configcommandinfos
;
182 ccip
->name
&& strncmp(ccip
->name
,p
,q
-p
);
185 diag(ads
,"%s:%d: unknown configuration directive `%.*s'",filename
,lno
,q
-p
,p
);
188 while (ctype_whitespace(*q
)) q
++;
189 ccip
->fn(ads
,filename
,lno
,q
);
192 diag(ads
,"%s:%d: read error: %s",filename
,lno
,strerror(errno
));
197 static const char *instrum_getenv(adns_state ads
, const char *envvar
) {
200 value
= getenv(envvar
);
201 if (!value
) debug(ads
,"environment variable %s not set",envvar
);
202 else debug(ads
,"environment variable %s set to `%s'",envvar
,value
);
206 static void readconfigenv(adns_state ads
, const char *envvar
) {
207 const char *filename
;
209 if (ads
->iflags
& adns_if_noenv
) {
210 debug(ads
,"not checking environment variable `%s'",envvar
);
213 filename
= instrum_getenv(ads
,envvar
);
214 if (filename
) readconfig(ads
,filename
);
217 int adns_init(adns_state
*ads_r
, adns_initflags flags
) {
219 const char *res_options
, *adns_res_options
;
220 struct protoent
*proto
;
223 ads
= malloc(sizeof(*ads
)); if (!ads
) return errno
;
224 ads
->tosend
.head
= ads
->tosend
.tail
= 0;
225 ads
->timew
.head
= ads
->timew
.tail
= 0;
226 ads
->childw
.head
= ads
->childw
.tail
= 0;
227 ads
->output
.head
= ads
->output
.tail
= 0;
232 ads
->tcpbufavail
= ads
->tcpbufused
= ads
->tcpbufdone
= 0;
238 res_options
= instrum_getenv(ads
,"RES_OPTIONS");
239 adns_res_options
= instrum_getenv(ads
,"ADNS_RES_OPTIONS");
240 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
241 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
243 readconfig(ads
,"/etc/resolv.conf");
244 readconfigenv(ads
,"RES_CONF");
245 readconfigenv(ads
,"ADNS_RES_CONF");
247 ccf_options(ads
,"RES_OPTIONS",-1,res_options
);
248 ccf_options(ads
,"ADNS_RES_OPTIONS",-1,adns_res_options
);
250 ccf_search(ads
,"LOCALDOMAIN",-1,instrum_getenv(ads
,"LOCALDOMAIN"));
251 ccf_search(ads
,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads
,"ADNS_LOCALDOMAIN"));
253 if (!ads
->nservers
) {
255 if (ads
->iflags
& adns_if_debug
)
256 fprintf(stderr
,"adns: no nameservers, using localhost\n");
257 ia
.s_addr
= INADDR_LOOPBACK
;
261 proto
= getprotobyname("udp"); if (!proto
) { r
= ENOPROTOOPT
; goto x_free
; }
262 ads
->udpsocket
= socket(AF_INET
,SOCK_DGRAM
,proto
->p_proto
);
263 if (!ads
->udpsocket
) { r
= errno
; goto x_closeudp
; }
269 close(ads
->udpsocket
);
275 static void query_fail(adns_state ads
, adns_query qu
, adns_status stat
) {
279 if (!ans
) ans
= malloc(sizeof(*qu
->answer
));
288 LIST_LINK_TAIL(ads
->output
,qu
);
291 int adns_finish(adns_state ads
) {
295 static void autosys(adns_state ads
, struct timeval now
) {
296 if (ads
->iflags
& adns_if_noautosys
) return;
297 adns_callback(ads
,-1,0,0,0);
300 void adns_cancel(adns_state ads
, adns_query query
) {
304 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
305 return maxfd
<0 || !fds ?
1 :
306 fd
<maxfd
&& FD_ISSET(fd
,fds
);
309 int adns_callback(adns_state ads
, int maxfd
,
310 const fd_set
*readfds
, const fd_set
*writefds
,
311 const fd_set
*exceptfds
) {
312 int skip
, dgramlen
, count
;
313 enum adns__tcpstate oldtcpstate
;
316 oldtcpstate
= ads
->tcpstate
;
318 if (ads
->tcpstate
== server_connecting
) {
319 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
321 assert(ads
->tcprecv
.used
==0);
322 vbuf_ensure(&ads
->tcprecv
,1);
323 if (ads
->tcprecv
.buf
) {
324 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
325 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
326 diag("nameserver %s TCP connection made",
327 inet_ntoa(ads
->servers
[ads
->tcpserver
].addr
));
328 ads
->tcpstate
= server_connected
;
330 tcpserver_broken(ads
,"connect/read","sent data before first request");
331 } else if (errno
!=EINTR
) {
332 tcpserver_broken(ads
,"connect",strerror(errno
));
337 if (ads
->tcpstate
== server_connected
) {
338 if (oldtcpstate
== server_connected
)
339 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
340 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
341 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
342 if (oldtcpstate
!= server_connected
|| callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
345 if (ads
->tcprecv
.used
<skip
+2) {
348 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
349 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
352 procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,-1);
353 skip
+= 2+dgramlen
; continue;
356 Ads
->tcprecv
.used
-= skip
;
357 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
358 vbuf_ensure(&ads
->tcprecv
,want
);
359 if (ads
->tcprecv
.used
>= ads
->tcprecv
.avail
) break;
360 r
= read(ads
->tcpsocket
,
361 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
362 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
364 ads
->tcprecv
.used
+= r
;
367 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
368 if (errno
==EINTR
) continue;
370 tcpserver_broken(ads
->tcpserver
,"read",r?
strerror(errno
):"closed");
374 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
375 tcpserver_broken(ads
->tcpserver
,"select","exceptional condition detected");
376 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
377 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
379 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
380 tcpserver_broken(ads
->tcpserver
,"write",strerror(errno
));
383 ads
->tcpsend
.used
-= r
;
384 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
400 vbuf_ensure(&ads
->tcprecv
,2);
401 vbuf_ensure(&ads
->tcprecv
,
402 if (ads
->tcprecv
.avail
<2) break;
403 if (ads
->tcprecv
.used
405 if (ads
->tcprecv
.used
<2 && ads
->tcprecv
.avail
406 if (ads
->tcprecv
.used
<2 && ads
->tcprecv
.avail
407 r
= read(ads
->tcpsocket
,
408 if (adns
->tcprecv
.used
<2) {
411 if (ads
->tcpstate
!= server_disc
) {
415 if (maxfd
<0 || !readfds
|| (FD_ISSET
420 diag("nameserver #%d (%s) TCP connection died: %s",
421 inet_ntoa(ads
->servers
[tcpserver
].addr
),
423 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
424 struct timeval maxto
) {
428 if (!rbuf
) { *tvbuf
= maxto
; *tv_io
= tvbuf
; return; }
429 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
432 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
433 struct timeval now
, struct timeval maxtime
) {
436 maxtime
.tv_sec
-= (now
.tv_sec
-1);
437 maxtime
.tv_usec
+= (1000-now
.tv_usec
);
438 dr
= ldiv(maxtime
.tv_usec
,1000);
439 maxtime
.tv_sec
+= dr
.quot
;
440 maxtime
.tv_usec
-= dr
.rem
;
441 inter_maxto(tv_io
,tvbuf
,maxtime
);
444 static void localresourcerr(struct timeval
**tv_io
, struct timeval
*tvbuf
,
445 const char *syscall
) {
446 struct timeval tvto_lr
;
448 diag(ads
,"local system resources scarce (during %s): %s",syscall
,strerror(errno
));
449 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
450 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
454 static inline void timevaladd(struct timeval
*tv_io
, long ms
) {
458 tmp
.tv_usec
+= (ms
%1000)*1000;
459 tmp
.tv_sec
+= ms
/1000;
460 if (tmp
.tv_usec
>= 1000) { tmp
.tv_sec
++; tmp
.tv_usec
-= 1000; }
464 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
465 if (fd
>=*maxfd
) *maxfd
= fd
+1;
469 void adns_interest(adns_state ads
, int *maxfd
,
470 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
471 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
476 r
= gettimeofday(&now
,0);
477 if (r
) { localresourcerr(tv_io
,tvbuf
,"gettimeofday"); return; }
479 for (qu
= ads
->timew
; qu
; qu
= nqu
) {
481 if (timercmp(&now
,qu
->timeout
,>)) {
482 DLIST_UNLINK(ads
->timew
,qu
);
483 if (qu
->nextudpserver
== -1) {
484 query_fail(ads
,qu
,adns_s_notresponding
);
486 DLIST_LINKTAIL(ads
->tosend
,qu
);
489 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
493 for (qu
= ads
->tosend
; qu
; qu
= nqu
) {
495 quproc_tosend(ads
,qu
,now
);
498 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
499 switch (ads
->tcpstate
) {
502 case server_connecting
:
503 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
505 case server_connected
:
506 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
507 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
508 if (ads
->opbufused
) inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
515 static int internal_check(adns_state ads
,
516 adns_query
*query_io
,
517 adns_answer
**answer
,
523 if (!ads
->output
.head
) return EWOULDBLOCK
;
524 qu
= ads
->output
.head
;
526 if (qu
->id
>=0) return EWOULDBLOCK
;
528 LIST_UNLINK(ads
->output
,qu
);
530 if (context_r
) *context_r
= qu
->context
;
535 int adns_wait(adns_state ads
,
536 adns_query
*query_io
,
537 adns_answer
**answer_r
,
539 int r
, maxfd
, rsel
, rcb
;
540 fd_set readfds
, writefds
, exceptfds
;
541 struct timeval tvbuf
, *tvp
;
544 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
545 if (r
&& r
!= EWOULDBLOCK
) return r
;
546 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
548 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
549 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
550 if (rsel
==-1) return r
;
551 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
556 int adns_check(adns_state ads
,
557 adns_query
*query_io
,
558 adns_answer
**answer_r
,
561 return internal_check(ads
,query_io
,answer_r
,context_r
);
564 int adns_synchronous(adns_state ads
,
567 adns_queryflags flags
,
568 adns_answer
**answer_r
) {
572 r
= adns_submit(ads
,owner
,type
,flags
,0,&qu
);
576 r
= adns_wait(ads
,&qu
,answer_r
,0);
578 if (r
) adns_cancel(ads
,qu
);
582 static adns_status
mkquery(adns_state ads
, const char *owner
, int ol
, int id
,
583 adns_rrtype type
, adns_queryflags flags
, int *qml_r
) {
584 int ll
, c
, nlabs
, qbufreq
;
585 unsigned char label
[255], *nqbuf
;
588 #define MKQUERY_ADDB(b) *nqbuf++= (b)
589 #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
591 qbufreq
= 12+strlen(owner
)+3;
592 if (ads
->qbufavail
< qbufreq
) {
593 nqbuf
= realloc(ads
->qbuf
,qbufreq
);
594 if (!nqbuf
) return adns_s_nolocalmem
;
595 ads
->qbuf
= nqbuf
; ads
->qbufavail
= qbufreq
;
600 MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
601 MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
602 MKQUERY_ADDW(1); /* QDCOUNT=1 */
603 MKQUERY_ADDW(0); /* ANCOUNT=0 */
604 MKQUERY_ADDW(0); /* NSCOUNT=0 */
605 MKQUERY_ADDW(0); /* ARCOUNT=0 */
606 p
= owner
; pe
= owner
+ol
;
608 if (!*p
) return adns_s_invaliddomain
;
611 while (p
!=pe
&& (c
= *p
++)!='.') {
613 if (!(flags
& adns_f_anyquote
)) return adns_s_invaliddomain
;
614 if (ctype_digit(p
[0])) {
615 if (ctype_digit(p
[1]) && ctype_digit(p
[2])) {
616 c
= (*p
++ - '0')*100 + (*p
++ - '0')*10 + (*p
++ - '0');
617 if (c
>= 256) return adns_s_invaliddomain
;
619 return adns_s_invaliddomain
;
621 } else if (!(c
= *p
++)) {
622 return adns_s_invaliddomain
;
625 if (!(flags
& adns_f_anyquote
)) {
626 if ((c
>= '0' && c
<= '9') || c
== '-') {
627 if (!ll
) return adns_s_invaliddomain
;
628 } else if ((c
< 'a' || c
> 'z') && (c
< 'A' && c
> 'Z')) {
629 return adns_s_invaliddomain
;
632 if (ll
== sizeof(label
)) return adns_s_invaliddomain
;
635 if (!ll
) return adns_s_invaliddomain
;
636 if (nlabs
++ > 63) return adns_s_invaliddomain
;
638 memcpy(nqbuf
,label
,ll
); nqbuf
+= ll
;
642 MKQUERY_ADDW(type
& adns__rrt_typemask
); /* QTYPE */
643 MKQUERY_ADDW(1); /* QCLASS=IN */
645 *qml_r
= nqbuf
- ads
->qbuf
;
650 static adns_query
allocquery(adns_state ads
, const char *owner
, int ol
,
651 int qml
, int id
, adns_rrtype type
,
652 adns_queryflags flags
, void *context
) {
656 qu
= malloc(sizeof(*qu
)+ol
+1+qml
); if (!qu
) return 0;
657 qu
->next
= qu
->back
= qu
->parent
= 0;
658 qu
->children
.head
= qu
->children
.tail
= 0;
659 qu
->siblings
.next
= qu
->siblings
.back
= 0;
664 qu
->context
= context
;
666 qu
->sentudp
= qu
->senttcp
= 0;
668 memcpy(qu
->owner
,owner
,ol
); qu
->owner
[ol
]= 0;
669 qu
->querymsg
= qm
= qu
->owner
+ol
+1;
670 memcpy(qm
,ads
->qbuf
,qml
);
675 static int failsubmit(adns_state ads
, void *context
, adns_query
*query_r
,
676 adns_rrtype type
, adns_queryflags flags
,
677 int id
, adns_status stat
) {
680 qu
= allocquery(ads
,0,0,0,id
,type
,flags
,context
); if (!qu
) return errno
;
681 query_fail(ads
,qu
,stat
);
686 static void quproc_tosend(adns_state ads
, adns_query qu
, struct timeval now
) {
687 /* Query must be on the `tosend' queue, and guarantees to remove it. */
688 struct sockaddr_in servaddr
;
691 if (qu
->nextudpserver
!= -1) {
692 if (qu
->udpretries
>= UDPMAXRETRIES
) {
693 DLIST_UNLINK(ads
->tosend
,qu
);
694 query_fail(ads
,qu
,adns_s_notresponding
);
697 serv
= qu
->nextudpserver
;
698 memset(&servaddr
,0,sizeof(servaddr
));
699 servaddr
.sin_family
= AF_INET
;
700 servaddr
.sin_addr
= ads
->servers
[serv
].addr
;
701 servaddr
.sin_port
= htons(53);
702 r
= sendto(ads
->udpsocket
,qu
->querymsg
,qu
->querylen
,0,&servaddr
,sizeof(servaddr
));
703 if (r
<0 && errno
== EMSGSIZE
) {
704 qu
->nextudpserver
= -1;
707 diag("sendto %s failed: %s",inet_ntoa(servaddr
.sin_addr
),strerror(errno
));
709 DLIST_UNLINK(ads
->tosend
,qu
);
710 timevaladd(&now
,UDPRETRYMS
);
712 qu
->sentudp
|= (1<<serv
);
713 qu
->nextudpserver
= (serv
+1)%ads
->nservers
;
715 DLIST_LINKTAIL(ads
->timew
,qu
);
721 serv
= tcpserver_get(ads
);
722 if (serv
<0) { r
=0; break; }
723 if (ads
->opbufused
) { r
=0; break; }
724 r
= write(ads
->tcpsocket
,qu
->querymsg
,qu
->querylen
);
726 if (errno
== EAGAIN
|| errno
== EINTR
|| errno
== ENOSPC
||
727 errno
== ENOBUFS
|| errno
== ENOMEM
) {
730 tcpserver_broken(serv
);
732 if (r
< qu
->querylen
) {
733 newopbufused
= qu
->opbufused
+ (qu
->querylen
-r
);
734 if (newopbufused
> ads
->opbufavail
) {
735 newopbufavail
= ads
->newopbufused
<<1;
736 newopbuf
= realloc(newopbufavail
);
738 DLIST_UNLINK(ads
->tosend
,qu
);
739 query_fail(ads
,qu
,adns_s_nolocalmem
);
742 ads
->opbuf
= newopbuf
;
743 ads
->opbufavail
= newopbufavail
;
745 memcpy(ads
->opbuf
+ads
->opbufused
,qu
->querymsg
+r
,qu
->querylen
-r
);
746 ads
->opbufused
= newopbufused
;
748 DLIST_UNLINK(ads
->tosend
,qu
);
749 timevaladd(&now
,TCPMS
);
751 qu
->senttcp
|= (1<<qu
->nextserver
);
752 DLIST_LINKTAIL(ads
->timew
,qu
);
755 int adns_submit(adns_state ads
,
758 adns_queryflags flags
,
760 adns_query
*query_r
) {
768 if (ol
<=1 || ol
>MAXDNAME
+1)
769 return failsubmit(ads
,context
,query_r
,type
,flags
,id
,adns_s_invaliddomain
);
770 if (owner
[ol
-1]=='.' && owner
[ol
-2]!='\\') { flags
&= ~adns_f_search
; ol
--; }
772 stat
= mkquery(ads
,owner
,ol
,id
,type
,flags
,&qml
);
773 if (stat
) return failsubmit(ads
,context
,query_r
,type
,flags
,id
,stat
);
775 qu
= allocquery(ads
,owner
,ol
,qml
,id
,type
,flags
,context
); if (!qu
) return errno
;
776 if (qu
->flags
& adns_f_usevc
) qu
->udpretries
= -1;
777 LIST_LINK_TAIL(ads
->tosend
,qu
);
779 r
= gettimeofday(&now
,0); if (r
) return;
780 quproc_tosend(ads
,qu
,now
);