8f3fb4c1b7ff354de827bfe33dbc31c1fd23bfd7
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(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
;
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(qu
,adns_s_timeout
);
165 adns__query_udp(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
);
210 /* Callback procedures - these do the real work of reception and timeout, etc. */
212 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
213 return maxfd
<0 || !fds ?
1 :
214 fd
<maxfd
&& FD_ISSET(fd
,fds
);
217 static int internal_callback(adns_state ads
, int maxfd
,
218 const fd_set
*readfds
, const fd_set
*writefds
,
219 const fd_set
*exceptfds
,
220 struct timeval now
) {
221 int skip
, want
, dgramlen
, count
, udpaddrlen
, r
, serv
;
222 byte udpbuf
[DNS_MAXUDP
];
223 struct sockaddr_in udpaddr
;
227 switch (ads
->tcpstate
) {
228 case server_disconnected
:
230 case server_connecting
:
231 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
233 assert(ads
->tcprecv
.used
==0);
234 if (!adns__vbuf_ensure(&ads
->tcprecv
,1)) return -1;
235 if (ads
->tcprecv
.buf
) {
236 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
237 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
238 tcp_connected(ads
,now
);
240 adns__tcp_broken(ads
,"connect/read","sent data before first request");
241 } else if (errno
!=EINTR
) {
242 adns__tcp_broken(ads
,"connect/read",strerror(errno
));
248 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
249 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
250 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
251 if (callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
254 if (ads
->tcprecv
.used
<skip
+2) {
257 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
258 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
261 adns__procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
,now
);
262 skip
+= 2+dgramlen
; continue;
265 ads
->tcprecv
.used
-= skip
;
266 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
268 if (!adns__vbuf_ensure(&ads
->tcprecv
,want
)) return -1;
269 assert(ads
->tcprecv
.used
<= ads
->tcprecv
.avail
);
270 if (ads
->tcprecv
.used
== ads
->tcprecv
.avail
) continue;
271 r
= read(ads
->tcpsocket
,
272 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
273 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
275 ads
->tcprecv
.used
+= r
;
278 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
279 if (errno
==EINTR
) continue;
281 adns__tcp_broken(ads
,"read",r?
strerror(errno
):"closed");
285 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
286 adns__tcp_broken(ads
,"select","exceptional condition detected");
287 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
288 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
290 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
291 adns__tcp_broken(ads
,"write",strerror(errno
));
294 ads
->tcpsend
.used
-= r
;
295 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
303 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
306 udpaddrlen
= sizeof(udpaddr
);
307 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,&udpaddr
,&udpaddrlen
);
309 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
310 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
311 adns__warn(ads
,-1,0,"datagram receive error: %s",strerror(errno
));
314 if (udpaddrlen
!= sizeof(udpaddr
)) {
315 adns__diag(ads
,-1,0,"datagram received with wrong address length %d"
316 " (expected %d)", udpaddrlen
,sizeof(udpaddr
));
319 if (udpaddr
.sin_family
!= AF_INET
) {
320 adns__diag(ads
,-1,0,"datagram received with wrong protocol family"
321 " %u (expected %u)",udpaddr
.sin_family
,AF_INET
);
324 if (ntohs(udpaddr
.sin_port
) != DNS_PORT
) {
325 adns__diag(ads
,-1,0,"datagram received from wrong port %u (expected %u)",
326 ntohs(udpaddr
.sin_port
),DNS_PORT
);
330 serv
< ads
->nservers
&&
331 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
333 if (serv
>= ads
->nservers
) {
334 adns__warn(ads
,-1,0,"datagram received from unknown nameserver %s",
335 inet_ntoa(udpaddr
.sin_addr
));
338 adns__procdgram(ads
,udpbuf
,r
,serv
,now
);
344 int adns_callback(adns_state ads
, int maxfd
,
345 const fd_set
*readfds
, const fd_set
*writefds
,
346 const fd_set
*exceptfds
) {
350 r
= gettimeofday(&now
,0); if (r
) return -1;
351 checktimeouts(ads
,now
,0,0);
352 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
,now
);
355 /* User-visible functions and their implementation. */
357 void adns__autosys(adns_state ads
, struct timeval now
) {
358 if (ads
->iflags
& adns_if_noautosys
) return;
359 adns_callback(ads
,-1,0,0,0);
362 static int internal_check(adns_state ads
,
363 adns_query
*query_io
,
364 adns_answer
**answer
,
370 if (!ads
->output
.head
) return EWOULDBLOCK
;
371 qu
= ads
->output
.head
;
373 if (qu
->id
>=0) return EWOULDBLOCK
;
375 LIST_UNLINK(ads
->output
,qu
);
377 if (context_r
) *context_r
= qu
->context
.ext
;
382 int adns_wait(adns_state ads
,
383 adns_query
*query_io
,
384 adns_answer
**answer_r
,
386 int r
, maxfd
, rsel
, rcb
;
387 fd_set readfds
, writefds
, exceptfds
;
388 struct timeval tvbuf
, *tvp
;
391 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
392 if (r
!= EWOULDBLOCK
) return r
;
394 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
395 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
396 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
398 if (errno
== EINTR
&& !(ads
->iflags
& adns_if_eintr
)) continue;
401 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
406 int adns_check(adns_state ads
,
407 adns_query
*query_io
,
408 adns_answer
**answer_r
,
413 r
= gettimeofday(&now
,0); if (r
) return errno
;
414 adns__autosys(ads
,now
);
415 return internal_check(ads
,query_io
,answer_r
,context_r
);