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-1999 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(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(qu
,now
);
75 void adns__tcp_tryconnect(adns_state ads
, struct timeval now
) {
77 struct sockaddr_in addr
;
78 struct protoent
*proto
;
80 for (tries
=0; tries
<ads
->nservers
; tries
++) {
81 if (ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
) return;
82 assert(ads
->tcpstate
== server_disconnected
);
83 assert(!ads
->tcpsend
.used
);
84 assert(!ads
->tcprecv
.used
);
86 proto
= getprotobyname("tcp");
87 if (!proto
) { adns__diag(ads
,-1,0,"unable to find protocol no. for TCP !"); return; }
88 fd
= socket(AF_INET
,SOCK_STREAM
,proto
->p_proto
);
90 adns__diag(ads
,-1,0,"cannot create TCP socket: %s",strerror(errno
));
93 r
= adns__setnonblock(ads
,fd
);
95 adns__diag(ads
,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r
));
99 memset(&addr
,0,sizeof(addr
));
100 addr
.sin_family
= AF_INET
;
101 addr
.sin_port
= htons(DNS_PORT
);
102 addr
.sin_addr
= ads
->servers
[ads
->tcpserver
].addr
;
103 r
= connect(fd
,&addr
,sizeof(addr
));
105 ads
->tcpstate
= server_connecting
;
106 if (r
==0) { tcp_connected(ads
,now
); continue; }
107 if (errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) return;
108 adns__tcp_broken(ads
,"connect",strerror(errno
));
112 /* `Interest' functions - find out which fd's we might be interested in,
113 * and when we want to be called back for a timeout.
116 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
117 struct timeval maxto
) {
118 struct timeval
*rbuf
;
123 *tvbuf
= maxto
; *tv_io
= tvbuf
;
125 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
127 /*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
128 maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/
131 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
132 struct timeval now
, struct timeval maxtime
) {
135 /*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
136 now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/
138 maxtime
.tv_sec
-= (now
.tv_sec
+2);
139 maxtime
.tv_usec
-= (now
.tv_usec
-2000000);
140 dr
= ldiv(maxtime
.tv_usec
,1000000);
141 maxtime
.tv_sec
+= dr
.quot
;
142 maxtime
.tv_usec
-= dr
.quot
*1000000;
143 if (maxtime
.tv_sec
<0) timerclear(&maxtime
);
144 inter_maxto(tv_io
,tvbuf
,maxtime
);
147 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
148 if (!maxfd
|| !fds
) return;
149 if (fd
>=*maxfd
) *maxfd
= fd
+1;
153 static void checktimeouts(adns_state ads
, struct timeval now
,
154 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
157 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
159 if (timercmp(&now
,&qu
->timeout
,>)) {
160 LIST_UNLINK(ads
->timew
,qu
);
161 if (qu
->state
!= query_udp
) {
162 adns__query_fail(qu
,adns_s_timeout
);
164 adns__query_udp(qu
,now
);
167 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
172 static void checkfds(adns_state ads
, int *maxfd
,
173 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
174 int avail
, struct pollfd
*buf
, int *count_r
) {
178 inter_addfd(ads
->udpsocket
,maxfd
,readfds
);
179 poll_addfd(ads
->udpsocket
,avail
,buf
,&count
,POLLIN
);
181 switch (ads
->tcpstate
) {
182 case server_disconnected
:
184 case server_connecting
:
185 inter_addfd(ads
->tcpsocket
,maxfd
,writefds
);
186 poll_addfd(ads
->tcpsocket
,avail
,buf
,&count
,POLLOUT
);
189 inter_addfd(ads
->tcpsocket
,maxfd
,readfds
);
190 inter_addfd(ads
->tcpsocket
,maxfd
,exceptfds
);
191 if (ads
->tcpsend
.used
) inter_addfd(ads
->tcpsocket
,maxfd
,writefds
);
192 poll_addfd(ads
->tcpsocket
,avail
,buf
,&count
,
193 ads
->tcpsend
.used ? POLLIN
|POLLOUT
: POLLIN
);
200 struct pollfd
*adns_pollfds(adns_state ads
, struct pollfd
*buf
,
201 int *len_io
, int *timeout_io
) {
202 struct timeval now
, tvbuf
, *tvp
;
205 r
= gettimeofday(&now
,0);
208 timeout
= *timeout_io
;
212 tvbuf
.tv_sec
= now
.tv_sec
+ (timeout
/1000);
213 tvbuf
.tv_usec
= now
.tv_usec
+ (timeout
%1000)*1000;
214 if (tvbuf
.tv_sec
>= 1000000) {
216 tvbuf
.tv_usec
-= 1000000;
220 checktimouts(ads
,now
,&tvp
,&tvbuf
);
223 assert(tvbuf
.tv_sec
<INT_MAX
/1000);
224 *timeout_io
= (tvp
->tv_sec
- now
.tv_sec
)*1000 + (tvp
->tv_usec
- now
.tv_usec
)/1000;
231 buf
= ads
->pollfdsbuf
;
234 buf
= ads
->pollfdsbuf
= malloc(sizeof(struct pollfd
)*avail
);
238 checkfds(ads
, 0,0,0,0, avail
,buf
,len_io
);
239 if (*len_io
> avail
) { errno
= ENOSPC
; return 0; }
244 void adns_interest(adns_state ads
, int *maxfd
,
245 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
246 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
248 struct timeval tvto_lr
;
251 /*fprintf(stderr,"adns_interest\n");*/
253 r
= gettimeofday(&now
,0);
255 adns__warn(ads
,-1,0,"gettimeofday failed - will sleep for a bit: %s",
257 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
258 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
260 checktimeouts(ads
,now
,tv_io
,tvbuf
);
263 checkfds(ads
, maxfd
,readfds
,writefds
,exceptfds
, 0,0,0);
266 /* Callback procedures - these do the real work of reception and timeout, etc. */
268 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
269 return maxfd
<0 || !fds ?
1 :
270 fd
<maxfd
&& FD_ISSET(fd
,fds
);
273 static int internal_callback(adns_state ads
, int maxfd
,
274 const fd_set
*readfds
, const fd_set
*writefds
,
275 const fd_set
*exceptfds
,
276 struct timeval now
) {
277 int skip
, want
, dgramlen
, count
, udpaddrlen
, r
, serv
;
278 byte udpbuf
[DNS_MAXUDP
];
279 struct sockaddr_in udpaddr
;
283 switch (ads
->tcpstate
) {
284 case server_disconnected
:
286 case server_connecting
:
287 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
289 assert(ads
->tcprecv
.used
==0);
290 if (!adns__vbuf_ensure(&ads
->tcprecv
,1)) return -1;
291 if (ads
->tcprecv
.buf
) {
292 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
293 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
294 tcp_connected(ads
,now
);
296 adns__tcp_broken(ads
,"connect/read","sent data before first request");
297 } else if (errno
!=EINTR
) {
298 adns__tcp_broken(ads
,"connect/read",strerror(errno
));
304 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
305 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
306 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
307 if (callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
310 if (ads
->tcprecv
.used
<skip
+2) {
313 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
314 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
317 adns__procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
,now
);
318 skip
+= 2+dgramlen
; continue;
321 ads
->tcprecv
.used
-= skip
;
322 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
324 if (!adns__vbuf_ensure(&ads
->tcprecv
,want
)) return -1;
325 assert(ads
->tcprecv
.used
<= ads
->tcprecv
.avail
);
326 if (ads
->tcprecv
.used
== ads
->tcprecv
.avail
) continue;
327 r
= read(ads
->tcpsocket
,
328 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
329 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
331 ads
->tcprecv
.used
+= r
;
334 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
335 if (errno
==EINTR
) continue;
337 adns__tcp_broken(ads
,"read",r?
strerror(errno
):"closed");
341 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
342 adns__tcp_broken(ads
,"select","exceptional condition detected");
343 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
344 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
346 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
347 adns__tcp_broken(ads
,"write",strerror(errno
));
350 ads
->tcpsend
.used
-= r
;
351 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
359 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
362 udpaddrlen
= sizeof(udpaddr
);
363 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
365 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
366 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
367 adns__warn(ads
,-1,0,"datagram receive error: %s",strerror(errno
));
370 if (udpaddrlen
!= sizeof(udpaddr
)) {
371 adns__diag(ads
,-1,0,"datagram received with wrong address length %d"
372 " (expected %d)", udpaddrlen
,sizeof(udpaddr
));
375 if (udpaddr
.sin_family
!= AF_INET
) {
376 adns__diag(ads
,-1,0,"datagram received with wrong protocol family"
377 " %u (expected %u)",udpaddr
.sin_family
,AF_INET
);
380 if (ntohs(udpaddr
.sin_port
) != DNS_PORT
) {
381 adns__diag(ads
,-1,0,"datagram received from wrong port %u (expected %u)",
382 ntohs(udpaddr
.sin_port
),DNS_PORT
);
386 serv
< ads
->nservers
&&
387 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
389 if (serv
>= ads
->nservers
) {
390 adns__warn(ads
,-1,0,"datagram received from unknown nameserver %s",
391 inet_ntoa(udpaddr
.sin_addr
));
394 adns__procdgram(ads
,udpbuf
,r
,serv
,now
);
400 int adns_callback(adns_state ads
, int maxfd
,
401 const fd_set
*readfds
, const fd_set
*writefds
,
402 const fd_set
*exceptfds
) {
406 r
= gettimeofday(&now
,0); if (r
) return -1;
407 checktimeouts(ads
,now
,0,0);
408 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
,now
);
411 /* User-visible functions and their implementation. */
413 void adns__autosys(adns_state ads
, struct timeval now
) {
414 if (ads
->iflags
& adns_if_noautosys
) return;
415 adns_callback(ads
,-1,0,0,0);
418 static int internal_check(adns_state ads
,
419 adns_query
*query_io
,
420 adns_answer
**answer
,
426 if (!ads
->output
.head
) return EWOULDBLOCK
;
427 qu
= ads
->output
.head
;
429 if (qu
->id
>=0) return EWOULDBLOCK
;
431 LIST_UNLINK(ads
->output
,qu
);
433 if (context_r
) *context_r
= qu
->ctx
.ext
;
438 int adns_wait(adns_state ads
,
439 adns_query
*query_io
,
440 adns_answer
**answer_r
,
442 int r
, maxfd
, rsel
, rcb
;
443 fd_set readfds
, writefds
, exceptfds
;
444 struct timeval tvbuf
, *tvp
;
447 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
448 if (r
!= EWOULDBLOCK
) return r
;
450 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
451 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
452 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
454 if (errno
== EINTR
&& !(ads
->iflags
& adns_if_eintr
)) continue;
457 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
462 int adns_check(adns_state ads
,
463 adns_query
*query_io
,
464 adns_answer
**answer_r
,
469 r
= gettimeofday(&now
,0); if (r
) return errno
;
470 adns__autosys(ads
,now
);
471 return internal_check(ads
,query_io
,answer_r
,context_r
);