4 * - TCP connection management
5 * - user-visible check/wait and event-loop-related functions
8 * This file is part of adns, which is Copyright (C) 1997, 1998 Ian Jackson
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #include <arpa/inet.h>
34 /* TCP connection management */
36 void adns__tcp_broken(adns_state ads
, const char *what
, const char *why
) {
40 assert(ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
);
42 adns__warn(ads
,serv
,0,"TCP connection lost: %s: %s",what
,why
);
43 close(ads
->tcpsocket
);
44 ads
->tcpstate
= server_disconnected
;
46 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
48 if (qu
->state
== query_udp
) continue;
49 assert(qu
->state
== query_tcpwait
|| qu
->state
== query_tcpsent
);
50 qu
->state
= query_tcpwait
;
51 qu
->tcpfailed
|= (1<<serv
);
52 if (qu
->tcpfailed
== (1<<ads
->nservers
)-1) {
53 LIST_UNLINK(ads
->timew
,qu
);
54 adns__query_fail(ads
,qu
,adns_s_allservfail
);
58 ads
->tcprecv
.used
= ads
->tcpsend
.used
= 0;
59 ads
->tcpserver
= (serv
+1)%ads
->nservers
;
62 static void tcp_connected(adns_state ads
, struct timeval now
) {
65 adns__debug(ads
,ads
->tcpserver
,0,"TCP connected");
66 ads
->tcpstate
= server_ok
;
67 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
69 if (qu
->state
== query_udp
) continue;
70 assert (qu
->state
== query_tcpwait
);
71 adns__query_tcp(ads
,qu
,now
);
75 void adns__tcp_tryconnect(adns_state ads
, struct timeval now
) {
77 struct sockaddr_in addr
;
78 struct protoent
*proto
;
79 /* fixme: single TCP timeout, not once per server */
81 for (tries
=0; tries
<ads
->nservers
; tries
++) {
82 if (ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
) return;
83 assert(ads
->tcpstate
== server_disconnected
);
84 assert(!ads
->tcpsend
.used
);
85 assert(!ads
->tcprecv
.used
);
87 proto
= getprotobyname("tcp");
88 if (!proto
) { adns__diag(ads
,-1,0,"unable to find protocol no. for TCP !"); return; }
89 fd
= socket(AF_INET
,SOCK_STREAM
,proto
->p_proto
);
91 adns__diag(ads
,-1,0,"cannot create TCP socket: %s",strerror(errno
));
94 r
= adns__setnonblock(ads
,fd
);
96 adns__diag(ads
,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r
));
100 memset(&addr
,0,sizeof(addr
));
101 addr
.sin_family
= AF_INET
;
102 addr
.sin_port
= htons(DNS_PORT
);
103 addr
.sin_addr
= ads
->servers
[ads
->tcpserver
].addr
;
104 r
= connect(fd
,&addr
,sizeof(addr
));
106 ads
->tcpstate
= server_connecting
;
107 if (r
==0) { tcp_connected(ads
,now
); continue; }
108 if (errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) return;
109 adns__tcp_broken(ads
,"connect",strerror(errno
));
113 /* `Interest' functions - find out which fd's we might be interested in,
114 * and when we want to be called back for a timeout.
117 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
118 struct timeval maxto
) {
119 struct timeval
*rbuf
;
124 *tvbuf
= maxto
; *tv_io
= tvbuf
;
126 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
128 fprintf(stderr
,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
129 maxto
.tv_sec
,maxto
.tv_usec
,(**tv_io
).tv_sec
,(**tv_io
).tv_usec
);
132 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
133 struct timeval now
, struct timeval maxtime
) {
136 fprintf(stderr
,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
137 now
.tv_sec
,now
.tv_usec
,maxtime
.tv_sec
,maxtime
.tv_usec
);
139 maxtime
.tv_sec
-= (now
.tv_sec
+2);
140 maxtime
.tv_usec
-= (now
.tv_usec
-2000000);
141 dr
= ldiv(maxtime
.tv_usec
,1000000);
142 maxtime
.tv_sec
+= dr
.quot
;
143 maxtime
.tv_usec
-= dr
.quot
*1000000;
144 if (maxtime
.tv_sec
<0) timerclear(&maxtime
);
145 inter_maxto(tv_io
,tvbuf
,maxtime
);
148 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
149 if (!maxfd
|| !fds
) return;
150 if (fd
>=*maxfd
) *maxfd
= fd
+1;
154 static void checktimeouts(adns_state ads
, struct timeval now
,
155 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
158 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
160 if (timercmp(&now
,&qu
->timeout
,>)) {
161 LIST_UNLINK(ads
->timew
,qu
);
162 if (qu
->state
!= query_udp
) {
163 adns__query_fail(ads
,qu
,adns_s_timeout
);
165 adns__query_udp(ads
,qu
,now
);
168 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
173 void adns_interest(adns_state ads
, int *maxfd
,
174 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
175 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
177 struct timeval tvto_lr
;
180 fprintf(stderr
,"adns_interest\n");
182 r
= gettimeofday(&now
,0);
184 adns__warn(ads
,-1,0,"gettimeofday failed - will sleep for a bit: %s",
186 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
187 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
189 checktimeouts(ads
,now
,tv_io
,tvbuf
);
192 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
194 switch (ads
->tcpstate
) {
195 case server_disconnected
:
197 case server_connecting
:
198 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
201 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
202 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
203 if (ads
->tcpsend
.used
) inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
209 /* Callback procedures - these do the real work of reception and timeout, etc. */
211 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
212 return maxfd
<0 || !fds ?
1 :
213 fd
<maxfd
&& FD_ISSET(fd
,fds
);
216 static int internal_callback(adns_state ads
, int maxfd
,
217 const fd_set
*readfds
, const fd_set
*writefds
,
218 const fd_set
*exceptfds
,
219 struct timeval now
) {
220 int skip
, want
, dgramlen
, count
, udpaddrlen
, r
, serv
;
221 byte udpbuf
[DNS_MAXUDP
];
222 struct sockaddr_in udpaddr
;
226 switch (ads
->tcpstate
) {
227 case server_disconnected
:
229 case server_connecting
:
230 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
232 assert(ads
->tcprecv
.used
==0);
233 adns__vbuf_ensure(&ads
->tcprecv
,1);
234 if (ads
->tcprecv
.buf
) {
235 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
236 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
237 tcp_connected(ads
,now
);
239 adns__tcp_broken(ads
,"connect/read","sent data before first request");
240 } else if (errno
!=EINTR
) {
241 adns__tcp_broken(ads
,"connect/read",strerror(errno
));
247 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
248 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
249 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
250 if (callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
253 if (ads
->tcprecv
.used
<skip
+2) {
256 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
257 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
260 adns__procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
,now
);
261 skip
+= 2+dgramlen
; continue;
264 ads
->tcprecv
.used
-= skip
;
265 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
266 adns__vbuf_ensure(&ads
->tcprecv
,want
);
267 if (ads
->tcprecv
.used
>= ads
->tcprecv
.avail
) break;
268 r
= read(ads
->tcpsocket
,
269 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
270 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
272 ads
->tcprecv
.used
+= r
;
275 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
276 if (errno
==EINTR
) continue;
278 adns__tcp_broken(ads
,"read",r?
strerror(errno
):"closed");
282 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
283 adns__tcp_broken(ads
,"select","exceptional condition detected");
284 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
285 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
287 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
288 adns__tcp_broken(ads
,"write",strerror(errno
));
291 ads
->tcpsend
.used
-= r
;
292 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
299 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
302 udpaddrlen
= sizeof(udpaddr
);
303 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
305 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
306 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
307 adns__warn(ads
,-1,0,"datagram receive error: %s",strerror(errno
));
310 if (udpaddrlen
!= sizeof(udpaddr
)) {
311 adns__diag(ads
,-1,0,"datagram received with wrong address length %d"
312 " (expected %d)", udpaddrlen
,sizeof(udpaddr
));
315 if (udpaddr
.sin_family
!= AF_INET
) {
316 adns__diag(ads
,-1,0,"datagram received with wrong protocol family"
317 " %u (expected %u)",udpaddr
.sin_family
,AF_INET
);
320 if (ntohs(udpaddr
.sin_port
) != DNS_PORT
) {
321 adns__diag(ads
,-1,0,"datagram received from wrong port %u (expected %u)",
322 ntohs(udpaddr
.sin_port
),DNS_PORT
);
326 serv
< ads
->nservers
&&
327 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
329 if (serv
>= ads
->nservers
) {
330 adns__warn(ads
,-1,0,"datagram received from unknown nameserver %s",
331 inet_ntoa(udpaddr
.sin_addr
));
334 adns__procdgram(ads
,udpbuf
,r
,serv
,now
);
340 int adns_callback(adns_state ads
, int maxfd
,
341 const fd_set
*readfds
, const fd_set
*writefds
,
342 const fd_set
*exceptfds
) {
346 r
= gettimeofday(&now
,0); if (r
) return -1;
347 checktimeouts(ads
,now
,0,0);
348 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
,now
);
351 /* User-visible functions and their implementation. */
353 void adns__autosys(adns_state ads
, struct timeval now
) {
354 if (ads
->iflags
& adns_if_noautosys
) return;
355 adns_callback(ads
,-1,0,0,0);
358 static int internal_check(adns_state ads
,
359 adns_query
*query_io
,
360 adns_answer
**answer
,
366 if (!ads
->output
.head
) return EWOULDBLOCK
;
367 qu
= ads
->output
.head
;
369 if (qu
->id
>=0) return EWOULDBLOCK
;
371 LIST_UNLINK(ads
->output
,qu
);
373 if (context_r
) *context_r
= qu
->context
.ext
;
378 int adns_wait(adns_state ads
,
379 adns_query
*query_io
,
380 adns_answer
**answer_r
,
382 int r
, maxfd
, rsel
, rcb
;
383 fd_set readfds
, writefds
, exceptfds
;
384 struct timeval tvbuf
, *tvp
;
387 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
388 if (r
!= EWOULDBLOCK
) return r
;
390 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
391 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
392 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
393 if (rsel
==-1) return r
;
394 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
399 int adns_check(adns_state ads
,
400 adns_query
*query_io
,
401 adns_answer
**answer_r
,
406 r
= gettimeofday(&now
,0); if (r
) return errno
;
407 adns__autosys(ads
,now
);
408 return internal_check(ads
,query_io
,answer_r
,context_r
);