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 <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
36 /* TCP connection management */
38 void adns__tcp_broken(adns_state ads
, const char *what
, const char *why
) {
42 assert(ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
);
44 adns__warn(ads
,serv
,0,"TCP connection lost: %s: %s",what
,why
);
45 close(ads
->tcpsocket
);
46 ads
->tcpstate
= server_disconnected
;
48 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
50 if (qu
->state
== query_udp
) continue;
51 assert(qu
->state
== query_tcpwait
|| qu
->state
== query_tcpsent
);
52 qu
->state
= query_tcpwait
;
53 qu
->tcpfailed
|= (1<<serv
);
54 if (qu
->tcpfailed
== (1<<ads
->nservers
)-1) {
55 LIST_UNLINK(ads
->timew
,qu
);
56 adns__query_fail(qu
,adns_s_allservfail
);
60 ads
->tcprecv
.used
= ads
->tcpsend
.used
= 0;
61 ads
->tcpserver
= (serv
+1)%ads
->nservers
;
64 static void tcp_connected(adns_state ads
, struct timeval now
) {
67 adns__debug(ads
,ads
->tcpserver
,0,"TCP connected");
68 ads
->tcpstate
= server_ok
;
69 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
71 if (qu
->state
== query_udp
) continue;
72 assert (qu
->state
== query_tcpwait
);
73 adns__query_tcp(qu
,now
);
77 void adns__tcp_tryconnect(adns_state ads
, struct timeval now
) {
79 struct sockaddr_in addr
;
80 struct protoent
*proto
;
82 for (tries
=0; tries
<ads
->nservers
; tries
++) {
83 if (ads
->tcpstate
== server_connecting
|| ads
->tcpstate
== server_ok
) return;
84 assert(ads
->tcpstate
== server_disconnected
);
85 assert(!ads
->tcpsend
.used
);
86 assert(!ads
->tcprecv
.used
);
88 proto
= getprotobyname("tcp");
89 if (!proto
) { adns__diag(ads
,-1,0,"unable to find protocol no. for TCP !"); return; }
90 fd
= socket(AF_INET
,SOCK_STREAM
,proto
->p_proto
);
92 adns__diag(ads
,-1,0,"cannot create TCP socket: %s",strerror(errno
));
95 r
= adns__setnonblock(ads
,fd
);
97 adns__diag(ads
,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r
));
101 memset(&addr
,0,sizeof(addr
));
102 addr
.sin_family
= AF_INET
;
103 addr
.sin_port
= htons(DNS_PORT
);
104 addr
.sin_addr
= ads
->servers
[ads
->tcpserver
].addr
;
105 r
= connect(fd
,(const struct sockaddr
*)&addr
,sizeof(addr
));
107 ads
->tcpstate
= server_connecting
;
108 if (r
==0) { tcp_connected(ads
,now
); continue; }
109 if (errno
== EWOULDBLOCK
|| errno
== EINPROGRESS
) return;
110 adns__tcp_broken(ads
,"connect",strerror(errno
));
114 /* `Interest' functions - find out which fd's we might be interested in,
115 * and when we want to be called back for a timeout.
118 static void inter_maxto(struct timeval
**tv_io
, struct timeval
*tvbuf
,
119 struct timeval maxto
) {
120 struct timeval
*rbuf
;
125 *tvbuf
= maxto
; *tv_io
= tvbuf
;
127 if (timercmp(rbuf
,&maxto
,>)) *rbuf
= maxto
;
129 /*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
130 maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/
133 static void inter_maxtoabs(struct timeval
**tv_io
, struct timeval
*tvbuf
,
134 struct timeval now
, struct timeval maxtime
) {
137 /*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
138 now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/
140 maxtime
.tv_sec
-= (now
.tv_sec
+2);
141 maxtime
.tv_usec
-= (now
.tv_usec
-2000000);
142 dr
= ldiv(maxtime
.tv_usec
,1000000);
143 maxtime
.tv_sec
+= dr
.quot
;
144 maxtime
.tv_usec
-= dr
.quot
*1000000;
145 if (maxtime
.tv_sec
<0) timerclear(&maxtime
);
146 inter_maxto(tv_io
,tvbuf
,maxtime
);
149 static void inter_addfd(int *maxfd
, fd_set
*fds
, int fd
) {
150 if (!maxfd
|| !fds
) return;
151 if (fd
>=*maxfd
) *maxfd
= fd
+1;
155 static void checktimeouts(adns_state ads
, struct timeval now
,
156 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
159 for (qu
= ads
->timew
.head
; qu
; qu
= nqu
) {
161 if (timercmp(&now
,&qu
->timeout
,>)) {
162 LIST_UNLINK(ads
->timew
,qu
);
163 if (qu
->state
!= query_udp
) {
164 adns__query_fail(qu
,adns_s_timeout
);
166 adns__query_udp(qu
,now
);
169 inter_maxtoabs(tv_io
,tvbuf
,now
,qu
->timeout
);
174 void adns_interest(adns_state ads
, int *maxfd
,
175 fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
176 struct timeval
**tv_io
, struct timeval
*tvbuf
) {
178 struct timeval tvto_lr
;
181 /*fprintf(stderr,"adns_interest\n");*/
183 r
= gettimeofday(&now
,0);
185 adns__warn(ads
,-1,0,"gettimeofday failed - will sleep for a bit: %s",
187 timerclear(&tvto_lr
); timevaladd(&tvto_lr
,LOCALRESOURCEMS
);
188 inter_maxto(tv_io
, tvbuf
, tvto_lr
);
190 checktimeouts(ads
,now
,tv_io
,tvbuf
);
193 inter_addfd(maxfd
,readfds
,ads
->udpsocket
);
195 switch (ads
->tcpstate
) {
196 case server_disconnected
:
198 case server_connecting
:
199 inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
202 inter_addfd(maxfd
,readfds
,ads
->tcpsocket
);
203 inter_addfd(maxfd
,exceptfds
,ads
->tcpsocket
);
204 if (ads
->tcpsend
.used
) inter_addfd(maxfd
,writefds
,ads
->tcpsocket
);
211 /* Callback procedures - these do the real work of reception and timeout, etc. */
213 static int callb_checkfd(int maxfd
, const fd_set
*fds
, int fd
) {
214 return maxfd
<0 || !fds ?
1 :
215 fd
<maxfd
&& FD_ISSET(fd
,fds
);
218 static int internal_callback(adns_state ads
, int maxfd
,
219 const fd_set
*readfds
, const fd_set
*writefds
,
220 const fd_set
*exceptfds
,
221 struct timeval now
) {
222 int skip
, want
, dgramlen
, count
, udpaddrlen
, r
, serv
;
223 byte udpbuf
[DNS_MAXUDP
];
224 struct sockaddr_in udpaddr
;
228 switch (ads
->tcpstate
) {
229 case server_disconnected
:
231 case server_connecting
:
232 if (callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
234 assert(ads
->tcprecv
.used
==0);
235 if (!adns__vbuf_ensure(&ads
->tcprecv
,1)) return -1;
236 if (ads
->tcprecv
.buf
) {
237 r
= read(ads
->tcpsocket
,&ads
->tcprecv
.buf
,1);
238 if (r
==0 || (r
<0 && (errno
==EAGAIN
|| errno
==EWOULDBLOCK
))) {
239 tcp_connected(ads
,now
);
241 adns__tcp_broken(ads
,"connect/read","sent data before first request");
242 } else if (errno
!=EINTR
) {
243 adns__tcp_broken(ads
,"connect/read",strerror(errno
));
249 count
+= callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
) +
250 callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
) +
251 (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
));
252 if (callb_checkfd(maxfd
,readfds
,ads
->tcpsocket
)) {
255 if (ads
->tcprecv
.used
<skip
+2) {
258 dgramlen
= (ads
->tcprecv
.buf
[skip
]<<8) | ads
->tcprecv
.buf
[skip
+1];
259 if (ads
->tcprecv
.used
<skip
+2+dgramlen
) {
262 adns__procdgram(ads
,ads
->tcprecv
.buf
+skip
+2,dgramlen
,ads
->tcpserver
,now
);
263 skip
+= 2+dgramlen
; continue;
266 ads
->tcprecv
.used
-= skip
;
267 memmove(ads
->tcprecv
.buf
,ads
->tcprecv
.buf
+skip
,ads
->tcprecv
.used
);
269 if (!adns__vbuf_ensure(&ads
->tcprecv
,want
)) return -1;
270 assert(ads
->tcprecv
.used
<= ads
->tcprecv
.avail
);
271 if (ads
->tcprecv
.used
== ads
->tcprecv
.avail
) continue;
272 r
= read(ads
->tcpsocket
,
273 ads
->tcprecv
.buf
+ads
->tcprecv
.used
,
274 ads
->tcprecv
.avail
-ads
->tcprecv
.used
);
276 ads
->tcprecv
.used
+= r
;
279 if (errno
==EAGAIN
|| errno
==EWOULDBLOCK
|| errno
==ENOMEM
) break;
280 if (errno
==EINTR
) continue;
282 adns__tcp_broken(ads
,"read",r?
strerror(errno
):"closed");
286 } else if (callb_checkfd(maxfd
,exceptfds
,ads
->tcpsocket
)) {
287 adns__tcp_broken(ads
,"select","exceptional condition detected");
288 } else if (ads
->tcpsend
.used
&& callb_checkfd(maxfd
,writefds
,ads
->tcpsocket
)) {
289 adns__sigpipe_protect(ads
);
290 r
= write(ads
->tcpsocket
,ads
->tcpsend
.buf
,ads
->tcpsend
.used
);
291 adns__sigpipe_unprotect(ads
);
293 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
&& errno
!=ENOMEM
&& errno
!=EINTR
) {
294 adns__tcp_broken(ads
,"write",strerror(errno
));
297 ads
->tcpsend
.used
-= r
;
298 memmove(ads
->tcpsend
.buf
,ads
->tcpsend
.buf
+r
,ads
->tcpsend
.used
);
306 if (callb_checkfd(maxfd
,readfds
,ads
->udpsocket
)) {
309 udpaddrlen
= sizeof(udpaddr
);
310 r
= recvfrom(ads
->udpsocket
,udpbuf
,sizeof(udpbuf
),0,
311 (struct sockaddr
*)&udpaddr
,&udpaddrlen
);
313 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
||
314 errno
== EINTR
|| errno
== ENOMEM
|| errno
== ENOBUFS
))
315 adns__warn(ads
,-1,0,"datagram receive error: %s",strerror(errno
));
318 if (udpaddrlen
!= sizeof(udpaddr
)) {
319 adns__diag(ads
,-1,0,"datagram received with wrong address length %d"
320 " (expected %d)", udpaddrlen
,sizeof(udpaddr
));
323 if (udpaddr
.sin_family
!= AF_INET
) {
324 adns__diag(ads
,-1,0,"datagram received with wrong protocol family"
325 " %u (expected %u)",udpaddr
.sin_family
,AF_INET
);
328 if (ntohs(udpaddr
.sin_port
) != DNS_PORT
) {
329 adns__diag(ads
,-1,0,"datagram received from wrong port %u (expected %u)",
330 ntohs(udpaddr
.sin_port
),DNS_PORT
);
334 serv
< ads
->nservers
&&
335 ads
->servers
[serv
].addr
.s_addr
!= udpaddr
.sin_addr
.s_addr
;
337 if (serv
>= ads
->nservers
) {
338 adns__warn(ads
,-1,0,"datagram received from unknown nameserver %s",
339 inet_ntoa(udpaddr
.sin_addr
));
342 adns__procdgram(ads
,udpbuf
,r
,serv
,now
);
348 int adns_callback(adns_state ads
, int maxfd
,
349 const fd_set
*readfds
, const fd_set
*writefds
,
350 const fd_set
*exceptfds
) {
354 r
= gettimeofday(&now
,0); if (r
) return -1;
355 checktimeouts(ads
,now
,0,0);
356 return internal_callback(ads
,maxfd
,readfds
,writefds
,exceptfds
,now
);
359 /* User-visible functions and their implementation. */
361 void adns__autosys(adns_state ads
, struct timeval now
) {
362 if (ads
->iflags
& adns_if_noautosys
) return;
363 adns_callback(ads
,-1,0,0,0);
366 static int internal_check(adns_state ads
,
367 adns_query
*query_io
,
368 adns_answer
**answer
,
374 if (!ads
->output
.head
) return EWOULDBLOCK
;
375 qu
= ads
->output
.head
;
377 if (qu
->id
>=0) return EWOULDBLOCK
;
379 LIST_UNLINK(ads
->output
,qu
);
381 if (context_r
) *context_r
= qu
->ctx
.ext
;
386 int adns_wait(adns_state ads
,
387 adns_query
*query_io
,
388 adns_answer
**answer_r
,
390 int r
, maxfd
, rsel
, rcb
;
391 fd_set readfds
, writefds
, exceptfds
;
392 struct timeval tvbuf
, *tvp
;
395 r
= internal_check(ads
,query_io
,answer_r
,context_r
);
396 if (r
!= EWOULDBLOCK
) return r
;
398 FD_ZERO(&readfds
); FD_ZERO(&writefds
); FD_ZERO(&exceptfds
);
399 adns_interest(ads
,&maxfd
,&readfds
,&writefds
,&exceptfds
,&tvp
,&tvbuf
);
400 rsel
= select(maxfd
,&readfds
,&writefds
,&exceptfds
,tvp
);
402 if (errno
== EINTR
&& !(ads
->iflags
& adns_if_eintr
)) continue;
405 rcb
= adns_callback(ads
,maxfd
,&readfds
,&writefds
,&exceptfds
);
410 int adns_check(adns_state ads
,
411 adns_query
*query_io
,
412 adns_answer
**answer_r
,
417 r
= gettimeofday(&now
,0); if (r
) return errno
;
418 adns__autosys(ads
,now
);
419 return internal_check(ads
,query_io
,answer_r
,context_r
);