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 void adns_interest(adns_state ads
, int *maxfd
,
173 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
174 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
176 struct timeval tvto_lr
;
179 /*fprintf(stderr,"adns_interest\n");*/
181 r
= gettimeofday(&now
,0);
183 adns__warn(ads
,-1,0,"gettimeofday failed - will sleep for a bit: %s",
185 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
186 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
188 checktimeouts(ads
,now
,tv_io
,tvbuf
);
191 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
193 switch (ads
->tcpstate
) {
194 case server_disconnected
:
196 case server_connecting
:
197 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
200 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
201 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
202 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 if (!adns__vbuf_ensure(&ads
->tcprecv
,1)) return -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
);
267 if (!adns__vbuf_ensure(&ads
->tcprecv
,want
)) return -1;
268 assert(ads
->tcprecv
.used
<= ads
->tcprecv
.avail
);
269 if (ads
->tcprecv
.used
== ads
->tcprecv
.avail
) continue;
270 r
= read(ads
->tcpsocket
,
271 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
272 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
274 ads
->tcprecv
.used
+= r
;
277 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
278 if (errno
==EINTR
) continue;
280 adns__tcp_broken(ads
,"read",r?
strerror(errno
):"closed");
284 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
285 adns__tcp_broken(ads
,"select","exceptional condition detected");
286 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
287 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
289 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
290 adns__tcp_broken(ads
,"write",strerror(errno
));
293 ads
->tcpsend
.used
-= r
;
294 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
302 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
305 udpaddrlen
= sizeof(udpaddr
);
306 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
308 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
309 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
310 adns__warn(ads
,-1,0,"datagram receive error: %s",strerror(errno
));
313 if (udpaddrlen
!= sizeof(udpaddr
)) {
314 adns__diag(ads
,-1,0,"datagram received with wrong address length %d"
315 " (expected %d)", udpaddrlen
,sizeof(udpaddr
));
318 if (udpaddr
.sin_family
!= AF_INET
) {
319 adns__diag(ads
,-1,0,"datagram received with wrong protocol family"
320 " %u (expected %u)",udpaddr
.sin_family
,AF_INET
);
323 if (ntohs(udpaddr
.sin_port
) != DNS_PORT
) {
324 adns__diag(ads
,-1,0,"datagram received from wrong port %u (expected %u)",
325 ntohs(udpaddr
.sin_port
),DNS_PORT
);
329 serv
< ads
->nservers
&&
330 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
332 if (serv
>= ads
->nservers
) {
333 adns__warn(ads
,-1,0,"datagram received from unknown nameserver %s",
334 inet_ntoa(udpaddr
.sin_addr
));
337 adns__procdgram(ads
,udpbuf
,r
,serv
,now
);
343 int adns_callback(adns_state ads
, int maxfd
,
344 const fd_set
*readfds
, const fd_set
*writefds
,
345 const fd_set
*exceptfds
) {
349 r
= gettimeofday(&now
,0); if (r
) return -1;
350 checktimeouts(ads
,now
,0,0);
351 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
,now
);
354 /* User-visible functions and their implementation. */
356 void adns__autosys(adns_state ads
, struct timeval now
) {
357 if (ads
->iflags
& adns_if_noautosys
) return;
358 adns_callback(ads
,-1,0,0,0);
361 static int internal_check(adns_state ads
,
362 adns_query
*query_io
,
363 adns_answer
**answer
,
369 if (!ads
->output
.head
) return EWOULDBLOCK
;
370 qu
= ads
->output
.head
;
372 if (qu
->id
>=0) return EWOULDBLOCK
;
374 LIST_UNLINK(ads
->output
,qu
);
376 if (context_r
) *context_r
= qu
->ctx
.ext
;
381 int adns_wait(adns_state ads
,
382 adns_query
*query_io
,
383 adns_answer
**answer_r
,
385 int r
, maxfd
, rsel
, rcb
;
386 fd_set readfds
, writefds
, exceptfds
;
387 struct timeval tvbuf
, *tvp
;
390 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
391 if (r
!= EWOULDBLOCK
) return r
;
393 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
394 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
395 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
397 if (errno
== EINTR
&& !(ads
->iflags
& adns_if_eintr
)) continue;
400 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
405 int adns_check(adns_state ads
,
406 adns_query
*query_io
,
407 adns_answer
**answer_r
,
412 r
= gettimeofday(&now
,0); if (r
) return errno
;
413 adns__autosys(ads
,now
);
414 return internal_check(ads
,query_io
,answer_r
,context_r
);