3 #include "adns-internal.h"
5 /* TCP connection management */
7 void adns__tcp_broken(adns_state ads
, const char *what
, const char *why
) {
10 assert(ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
);
12 warn("TCP connection lost: %s: %s",serv
,why
);
13 close(ads
->tcpsocket
);
14 ads
->tcpstate
= server_disconnected
;
16 for (qu
= ads
->timew
; qu
; qu
= nqu
) {
18 if (qu
->state
== query_udp
) continue;
19 assert(qu
->state
== query_tcpwait
|| qu
->state
== query_tcpsent
);
20 qu
->state
= query_tcpwait
;
21 qu
->tcpfailed
|= (1<<serv
);
22 if (qu
->tcpfailed
== (1<<ads
->nservers
)-1) {
23 DLIST_UNLINK(ads
->timew
,qu
);
24 adns__query_fail(ads
,qu
,adns_s_allservfail
);
29 ads
->tcpserver
= (serv
+1)%ads
->nservers
;
32 static void tcp_connected(adns_state ads
, struct timeval now
) {
33 debug("TCP connected",ads
->tcpserver
);
34 ads
->tcpstate
= server_connected
;
35 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
37 if (qu
->state
== query_udp
) continue;
38 assert (qu
->state
== query_tcpwait
);
39 adns__query_tcp(ads
,qu
,now
);
43 void adns__tcp_tryconnect(adns_state ads
, struct timeval now
) {
46 /* fixme: single TCP timeout, not once per server */
48 for (tries
=0; tries
<ads
->nservers
; tries
++) {
49 if (ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
) return;
50 assert(ads
->tcpstate
== server_disconnected
);
51 assert(!ads
->tcpbuf
.used
);
53 proto
= getprotobyname("tcp");
54 if (!proto
) { diag(ads
,"unable to find protocol number for TCP !",-1); return; }
55 fd
= socket(AF_INET
,SOCK_STREAM
,proto
->p_proto
);
56 if (fd
<0) { diag(ads
,"cannot create TCP socket: %s",-1,strerror(errno
)); return; }
57 if (!adns__setnonblock(fd
)) return;
58 memset(&addr
,0,sizeof(addr
));
59 addr
.sin_family
= AF_INET
;
60 addr
.sin_port
= htons(NSPORT
);
61 addr
.sin_addr
= ads
->servers
[ads
->tcpserver
].addr
;
62 r
= connect(fd
,&addr
,sizeof(addr
));
64 ads
->tcpstate
= server_connecting
;
65 if (r
==0) { tcp_connected(ads
); continue; }
66 if (errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) return;
67 adns__tcp_broken(ads
,"connect",strerror(errno
));
71 /* Callback procedures - these do the real work of reception and timeout, etc. */
73 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
74 return maxfd
<0 || !fds ?
1 :
75 fd
<maxfd
&& FD_ISSET(fd
,fds
);
78 static int internal_callback(adns_state ads
, int maxfd
,
79 const fd_set
*readfds
, const fd_set
*writefds
,
80 const fd_set
*exceptfds
) {
81 int skip
, dgramlen
, count
, udpaddrlen
, oldtcpsocket
;
82 enum adns__tcpstate oldtcpstate
;
83 unsigned char udpbuf
[UDPMAXDGRAM
];
84 struct sockaddr_in udpaddr
;
88 switch (ads
->tcpstate
) {
89 case server_disconnected
:
91 case server_connecting
:
92 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
94 assert(ads
->tcprecv
.used
==0);
95 vbuf_ensure(&ads
->tcprecv
,1);
96 if (ads
->tcprecv
.buf
) {
97 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
98 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
99 tcpserver_connected(ads
);
101 tcpserver_broken(ads
,"connect/read","sent data before first request");
102 } else if (errno
!=EINTR
) {
103 tcpserver_broken(ads
,"connect/read",strerror(errno
));
109 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
110 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
111 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
112 if (callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
115 if (ads
->tcprecv
.used
<skip
+2) {
118 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
119 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
122 procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
);
123 skip
+= 2+dgramlen
; continue;
126 ads
->tcprecv
.used
-= skip
;
127 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
128 vbuf_ensure(&ads
->tcprecv
,want
);
129 if (ads
->tcprecv
.used
>= ads
->tcprecv
.avail
) break;
130 r
= read(ads
->tcpsocket
,
131 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
132 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
134 ads
->tcprecv
.used
+= r
;
137 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
138 if (errno
==EINTR
) continue;
140 tcpserver_broken(ads
->tcpserver
,"read",r?
strerror(errno
):"closed");
144 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
145 tcpserver_broken(ads
->tcpserver
,"select","exceptional condition detected");
146 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
147 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
149 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
150 tcpserver_broken(ads
->tcpserver
,"write",strerror(errno
));
153 ads
->tcpsend
.used
-= r
;
154 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
161 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
164 udpaddrlen
= sizeof(udpaddr
);
165 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
167 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
168 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
169 warn("datagram receive error: %s",strerror(errno
));
172 if (udpaddrlen
!= sizeof(udpaddr
)) {
173 diag("datagram received with wrong address length %d (expected %d)",
174 udpaddrlen
,sizeof(udpaddr
));
177 if (udpaddr
.sin_family
!= AF_INET
) {
178 diag("datagram received with wrong protocol family %u (expected %u)",
179 udpaddr
.sin_family
,AF_INET
);
182 if (ntohs(udpaddr
.sin_port
) != NSPORT
) {
183 diag("datagram received from wrong port %u (expected %u)",
184 ntohs(udpaddr
.sin_port
),NSPORT
);
188 serv
< ads
->nservers
&&
189 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
191 if (serv
>= ads
->nservers
) {
192 warn("datagram received from unknown nameserver %s",inet_ntoa(udpaddr
.sin_addr
));
195 procdgram(ads
,udpbuf
,r
,serv
);
200 static void checktimeouts(adns_state ads
, struct timeval now
,
201 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
202 for (qu
= ads
->timew
; qu
; qu
= nqu
) {
204 if (timercmp(&now
,qu
->timeout
,>)) {
205 DLIST_UNLINK(ads
->timew
,qu
);
206 if (qu
->state
!= state_udp
) {
207 query_fail(ads
,qu
,adns_s_notresponding
);
209 adns__query_udp(ads
,qu
,now
);
212 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
217 int adns_callback(adns_state ads
, int maxfd
,
218 const fd_set
*readfds
, const fd_set
*writefds
,
219 const fd_set
*exceptfds
) {
222 r
= gettimeofday(&now
,0);
223 if (!r
) checktimeouts(ads
,now
,0,0);
224 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
);
227 /* `Interest' functions - find out which fd's we might be interested in,
228 * and when we want to be called back for a timeout.
231 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
232 struct timeval maxto
) {
237 if (!rbuf
) { *tvbuf
= maxto
; *tv_io
= tvbuf
; return; }
238 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
241 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
242 struct timeval now
, struct timeval maxtime
) {
246 maxtime
.tv_sec
-= (now
.tv_sec
-1);
247 maxtime
.tv_usec
+= (1000-now
.tv_usec
);
248 dr
= ldiv(maxtime
.tv_usec
,1000);
249 maxtime
.tv_sec
+= dr
.quot
;
250 maxtime
.tv_usec
-= dr
.rem
;
251 inter_maxto(tv_io
,tvbuf
,maxtime
);
254 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
255 if (!maxfd
|| !fds
) return;
256 if (fd
>=*maxfd
) *maxfd
= fd
+1;
260 void adns_interest(adns_state ads
, int *maxfd
,
261 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
262 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
264 struct timeval tvto_lr
;
268 r
= gettimeofday(&now
,0);
270 warn(ads
,"gettimeofday failed - will sleep for a bit: %s",-1,strerror(errno
));
271 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
272 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
274 checktimeouts(ads
,now
,tv_io
,tvbuf
);
277 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
279 switch (ads
->tcpstate
) {
282 case server_connecting
:
283 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
285 case server_connected
:
286 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
287 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
288 if (ads
->opbufused
) inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
294 /* User-visible functions and their implementation. */
296 static void autosys(adns_state ads
, struct timeval now
) {
297 if (ads
->iflags
& adns_if_noautosys
) return;
298 adns_callback(ads
,-1,0,0,0);
301 static int internal_check(adns_state ads
,
302 adns_query
*query_io
,
303 adns_answer
**answer
,
309 if (!ads
->output
.head
) return EWOULDBLOCK
;
310 qu
= ads
->output
.head
;
312 if (qu
->id
>=0) return EWOULDBLOCK
;
314 LIST_UNLINK(ads
->output
,qu
);
316 if (context_r
) *context_r
= qu
->context
;
321 int adns_wait(adns_state ads
,
322 adns_query
*query_io
,
323 adns_answer
**answer_r
,
325 int r
, maxfd
, rsel
, rcb
;
326 fd_set readfds
, writefds
, exceptfds
;
327 struct timeval tvbuf
, *tvp
;
330 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
331 if (r
&& r
!= EWOULDBLOCK
) return r
;
333 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
334 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
335 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
336 if (rsel
==-1) return r
;
337 rcb
= internal_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
342 int adns_check(adns_state ads
,
343 adns_query
*query_io
,
344 adns_answer
**answer_r
,
347 return internal_check(ads
,query_io
,answer_r
,context_r
);