3 #include "adns-internal.h"
5 static void autosys(adns_state ads
, struct timeval now
) {
6 if (ads
->iflags
& adns_if_noautosys
) return;
7 adns_callback(ads
,-1,0,0,0);
10 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
11 return maxfd
<0 || !fds ?
1 :
12 fd
<maxfd
&& FD_ISSET(fd
,fds
);
15 static void tcpserver_broken(adns_state ads
, const char *what
, const char *why
) {
16 assert(ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_connected
);
17 warn("nameserver %s TCP connection lost: %s: %s",
18 inet_ntoa(ads
->servers
[tcpserver
].addr
,what
,why
));
19 close(ads
->tcpsocket
);
20 ads
->tcpstate
= server_disconnected
;
23 int adns_callback(adns_state ads
, int maxfd
,
24 const fd_set
*readfds
, const fd_set
*writefds
,
25 const fd_set
*exceptfds
) {
26 int skip
, dgramlen
, count
, udpaddrlen
;
27 enum adns__tcpstate oldtcpstate
;
28 unsigned char udpbuf
[UDPMAXDGRAM
];
29 struct sockaddr_in udpaddr
;
32 oldtcpstate
= ads
->tcpstate
;
34 if (ads
->tcpstate
== server_connecting
) {
35 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
37 assert(ads
->tcprecv
.used
==0);
38 vbuf_ensure(&ads
->tcprecv
,1);
39 if (ads
->tcprecv
.buf
) {
40 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
41 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
42 debug("nameserver %s TCP connected",
43 inet_ntoa(ads
->servers
[ads
->tcpserver
].addr
));
44 ads
->tcpstate
= server_connected
;
46 tcpserver_broken(ads
,"connect/read","sent data before first request");
47 } else if (errno
!=EINTR
) {
48 tcpserver_broken(ads
,"connect",strerror(errno
));
53 if (ads
->tcpstate
== server_connected
) {
54 if (oldtcpstate
== server_connected
)
55 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
56 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
57 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
58 if (oldtcpstate
!= server_connected
|| callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
61 if (ads
->tcprecv
.used
<skip
+2) {
64 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
65 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
68 procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
);
69 skip
+= 2+dgramlen
; continue;
72 ads
->tcprecv
.used
-= skip
;
73 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
74 vbuf_ensure(&ads
->tcprecv
,want
);
75 if (ads
->tcprecv
.used
>= ads
->tcprecv
.avail
) break;
76 r
= read(ads
->tcpsocket
,
77 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
78 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
80 ads
->tcprecv
.used
+= r
;
83 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
84 if (errno
==EINTR
) continue;
86 tcpserver_broken(ads
->tcpserver
,"read",r?
strerror(errno
):"closed");
90 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
91 tcpserver_broken(ads
->tcpserver
,"select","exceptional condition detected");
92 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
93 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
95 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
96 tcpserver_broken(ads
->tcpserver
,"write",strerror(errno
));
99 ads
->tcpsend
.used
-= r
;
100 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
105 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
108 udpaddrlen
= sizeof(udpaddr
);
109 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
111 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
112 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
113 warn("datagram receive error: %s",strerror(errno
));
116 if (udpaddrlen
!= sizeof(udpaddr
)) {
117 diag("datagram received with wrong address length %d (expected %d)",
118 udpaddrlen
,sizeof(udpaddr
));
121 if (udpaddr
.sin_family
!= AF_INET
) {
122 diag("datagram received with wrong protocol family %u (expected %u)",
123 udpaddr
.sin_family
,AF_INET
);
126 if (ntohs(udpaddr
.sin_port
) != NSPORT
) {
127 diag("datagram received from wrong port %u (expected %u)",
128 ntohs(udpaddr
.sin_port
),NSPORT
);
132 serv
< ads
->nservers
&&
133 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
135 if (serv
>= ads
->nservers
) {
136 warn("datagram received from unknown nameserver %s",inet_ntoa(udpaddr
.sin_addr
));
139 procdgram(ads
,udpbuf
,r
,serv
);
144 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
145 struct timeval maxto
) {
149 if (!rbuf
) { *tvbuf
= maxto
; *tv_io
= tvbuf
; return; }
150 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
153 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
154 struct timeval now
, struct timeval maxtime
) {
157 maxtime
.tv_sec
-= (now
.tv_sec
-1);
158 maxtime
.tv_usec
+= (1000-now
.tv_usec
);
159 dr
= ldiv(maxtime
.tv_usec
,1000);
160 maxtime
.tv_sec
+= dr
.quot
;
161 maxtime
.tv_usec
-= dr
.rem
;
162 inter_maxto(tv_io
,tvbuf
,maxtime
);
165 static void localresourcerr(struct timeval
**tv_io
, struct timeval
*tvbuf
,
166 const char *syscall
) {
167 struct timeval tvto_lr
;
169 warn(ads
,"local system resources scarce (during %s): %s",syscall
,strerror(errno
));
170 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
171 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
175 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
176 if (fd
>=*maxfd
) *maxfd
= fd
+1;
180 void adns_interest(adns_state ads
, int *maxfd
,
181 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
182 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
187 r
= gettimeofday(&now
,0);
188 if (r
) { localresourcerr(tv_io
,tvbuf
,"gettimeofday"); return; }
190 for (qu
= ads
->timew
; qu
; qu
= nqu
) {
192 if (timercmp(&now
,qu
->timeout
,>)) {
193 DLIST_UNLINK(ads
->timew
,qu
);
194 if (qu
->nextudpserver
== -1) {
195 query_fail(ads
,qu
,adns_s_notresponding
);
197 DLIST_LINKTAIL(ads
->tosend
,qu
);
200 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
204 for (qu
= ads
->tosend
; qu
; qu
= nqu
) {
206 quproc_tosend(ads
,qu
,now
);
209 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
211 switch (ads
->tcpstate
) {
214 case server_connecting
:
215 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
217 case server_connected
:
218 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
219 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
220 if (ads
->opbufused
) inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
226 static int internal_check(adns_state ads
,
227 adns_query
*query_io
,
228 adns_answer
**answer
,
234 if (!ads
->output
.head
) return EWOULDBLOCK
;
235 qu
= ads
->output
.head
;
237 if (qu
->id
>=0) return EWOULDBLOCK
;
239 LIST_UNLINK(ads
->output
,qu
);
241 if (context_r
) *context_r
= qu
->context
;
246 int adns_wait(adns_state ads
,
247 adns_query
*query_io
,
248 adns_answer
**answer_r
,
250 int r
, maxfd
, rsel
, rcb
;
251 fd_set readfds
, writefds
, exceptfds
;
252 struct timeval tvbuf
, *tvp
;
255 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
256 if (r
&& r
!= EWOULDBLOCK
) return r
;
258 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
259 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
260 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
261 if (rsel
==-1) return r
;
262 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
267 int adns_check(adns_state ads
,
268 adns_query
*query_io
,
269 adns_answer
**answer_r
,
272 return internal_check(ads
,query_io
,answer_r
,context_r
);