Commit | Line | Data |
---|---|---|
e576be50 | 1 | /* |
2 | * event.c | |
3 | * - event loop core | |
4 | * - TCP connection management | |
5 | * - user-visible check/wait and event-loop-related functions | |
6 | */ | |
7 | /* | |
ae8cc977 | 8 | * This file is part of adns, which is |
baa953c9 | 9 | * Copyright (C) 1997-2000,2003,2006,2014-2016 Ian Jackson |
17cd4f48 | 10 | * Copyright (C) 2014 Mark Wooding |
ae8cc977 | 11 | * Copyright (C) 1999-2000,2003,2006 Tony Finch |
12 | * Copyright (C) 1991 Massachusetts Institute of Technology | |
13 | * (See the file INSTALL for full details.) | |
e576be50 | 14 | * |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
7f8bbe29 | 17 | * the Free Software Foundation; either version 3, or (at your option) |
e576be50 | 18 | * any later version. |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
8c09a4c6 | 26 | * along with this program; if not, write to the Free Software Foundation. |
e576be50 | 27 | */ |
656b2da9 | 28 | |
4353a5c4 | 29 | #include <errno.h> |
30 | #include <stdlib.h> | |
0f091044 | 31 | #include <unistd.h> |
4353a5c4 | 32 | |
0f091044 | 33 | #include <sys/types.h> |
34 | #include <sys/time.h> | |
4353a5c4 | 35 | #include <netdb.h> |
eec1d9c8 | 36 | #include <sys/socket.h> |
37 | #include <netinet/in.h> | |
4353a5c4 | 38 | #include <arpa/inet.h> |
39 | ||
40 | #include "internal.h" | |
2953d358 | 41 | #include "tvarith.h" |
37e28fde | 42 | |
620c146d | 43 | /* TCP connection management. */ |
44 | ||
f7f83b4a | 45 | static void tcp_close(adns_state ads) { |
620c146d | 46 | close(ads->tcpsocket); |
6a578b2c | 47 | ads->tcpsocket= -1; |
70ad7a2a | 48 | ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0; |
620c146d | 49 | } |
656b2da9 | 50 | |
8402e34c | 51 | void adns__tcp_broken(adns_state ads, const char *what, const char *why) { |
52 | int serv; | |
fb901bf5 | 53 | adns_query qu; |
8402e34c | 54 | |
55 | assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok); | |
ddfda861 | 56 | serv= ads->tcpserver; |
f7f83b4a | 57 | if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why); |
58 | ||
fb901bf5 | 59 | if (ads->tcpstate == server_connecting) { |
60 | /* Counts as a retry for all the queries waiting for TCP. */ | |
61 | for (qu= ads->tcpw.head; qu; qu= qu->next) | |
62 | qu->retries++; | |
63 | } | |
64 | ||
f7f83b4a | 65 | tcp_close(ads); |
66 | ads->tcpstate= server_broken; | |
67 | ads->tcpserver= (serv+1)%ads->nservers; | |
8402e34c | 68 | } |
69 | ||
ddfda861 | 70 | static void tcp_connected(adns_state ads, struct timeval now) { |
4353a5c4 | 71 | adns_query qu, nqu; |
72 | ||
68442019 | 73 | adns__debug(ads,ads->tcpserver,0,"TCP connected"); |
4353a5c4 | 74 | ads->tcpstate= server_ok; |
f7f83b4a | 75 | for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) { |
ddfda861 | 76 | nqu= qu->next; |
f7f83b4a | 77 | assert(qu->state == query_tcpw); |
78 | adns__querysend_tcp(qu,now); | |
ddfda861 | 79 | } |
80 | } | |
81 | ||
237ce710 | 82 | static void tcp_broken_events(adns_state ads) { |
83 | adns_query qu, nqu; | |
84 | ||
85 | assert(ads->tcpstate == server_broken); | |
86 | for (qu= ads->tcpw.head; qu; qu= nqu) { | |
87 | nqu= qu->next; | |
88 | assert(qu->state == query_tcpw); | |
89 | if (qu->retries > ads->nservers) { | |
90 | LIST_UNLINK(ads->tcpw,qu); | |
91 | adns__query_fail(qu,adns_s_allservfail); | |
92 | } | |
93 | } | |
94 | ads->tcpstate= server_disconnected; | |
95 | } | |
96 | ||
ddfda861 | 97 | void adns__tcp_tryconnect(adns_state ads, struct timeval now) { |
8402e34c | 98 | int r, fd, tries; |
f930c455 | 99 | adns_rr_addr *addr; |
4353a5c4 | 100 | struct protoent *proto; |
8402e34c | 101 | |
102 | for (tries=0; tries<ads->nservers; tries++) { | |
f7f83b4a | 103 | switch (ads->tcpstate) { |
104 | case server_connecting: | |
105 | case server_ok: | |
106 | case server_broken: | |
107 | return; | |
108 | case server_disconnected: | |
109 | break; | |
110 | default: | |
111 | abort(); | |
112 | } | |
113 | ||
4353a5c4 | 114 | assert(!ads->tcpsend.used); |
115 | assert(!ads->tcprecv.used); | |
70ad7a2a | 116 | assert(!ads->tcprecv_skip); |
8402e34c | 117 | |
118 | proto= getprotobyname("tcp"); | |
609133ee | 119 | if (!proto) { |
120 | adns__diag(ads,-1,0,"unable to find protocol no. for TCP !"); | |
121 | return; | |
122 | } | |
f930c455 MW |
123 | addr = &ads->servers[ads->tcpserver]; |
124 | fd= socket(addr->addr.sa.sa_family, SOCK_STREAM, proto->p_proto); | |
4353a5c4 | 125 | if (fd<0) { |
68442019 | 126 | adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno)); |
4353a5c4 | 127 | return; |
128 | } | |
129 | r= adns__setnonblock(ads,fd); | |
130 | if (r) { | |
609133ee | 131 | adns__diag(ads,-1,0,"cannot make TCP socket nonblocking:" |
132 | " %s",strerror(r)); | |
4353a5c4 | 133 | close(fd); |
134 | return; | |
135 | } | |
f930c455 | 136 | r= connect(fd,&addr->addr.sa,addr->len); |
8402e34c | 137 | ads->tcpsocket= fd; |
138 | ads->tcpstate= server_connecting; | |
f7f83b4a | 139 | if (r==0) { tcp_connected(ads,now); return; } |
140 | if (errno == EWOULDBLOCK || errno == EINPROGRESS) { | |
141 | ads->tcptimeout= now; | |
142 | timevaladd(&ads->tcptimeout,TCPCONNMS); | |
143 | return; | |
144 | } | |
ddfda861 | 145 | adns__tcp_broken(ads,"connect",strerror(errno)); |
237ce710 | 146 | tcp_broken_events(ads); |
8402e34c | 147 | } |
148 | } | |
149 | ||
620c146d | 150 | /* Timeout handling functions. */ |
151 | ||
152 | void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, | |
153 | struct timeval *tv_buf) { | |
154 | const struct timeval *now; | |
155 | int r; | |
156 | ||
157 | now= *now_io; | |
158 | if (now) return; | |
159 | r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; } | |
160 | adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno)); | |
161 | adns_globalsystemfailure(ads); | |
162 | return; | |
163 | } | |
4353a5c4 | 164 | |
4fad263d | 165 | static void inter_immed(struct timeval **tv_io, struct timeval *tvbuf) { |
166 | struct timeval *rbuf; | |
167 | ||
168 | if (!tv_io) return; | |
169 | ||
170 | rbuf= *tv_io; | |
171 | if (!rbuf) { *tv_io= rbuf= tvbuf; } | |
172 | ||
173 | timerclear(rbuf); | |
174 | } | |
175 | ||
4353a5c4 | 176 | static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf, |
177 | struct timeval maxto) { | |
178 | struct timeval *rbuf; | |
179 | ||
180 | if (!tv_io) return; | |
181 | rbuf= *tv_io; | |
94436798 | 182 | if (!rbuf) { |
183 | *tvbuf= maxto; *tv_io= tvbuf; | |
184 | } else { | |
185 | if (timercmp(rbuf,&maxto,>)) *rbuf= maxto; | |
186 | } | |
ffbda80c | 187 | /*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n", |
188 | maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/ | |
4353a5c4 | 189 | } |
190 | ||
191 | static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf, | |
192 | struct timeval now, struct timeval maxtime) { | |
f7f83b4a | 193 | /* tv_io may be 0 */ |
4353a5c4 | 194 | ldiv_t dr; |
195 | ||
ffbda80c | 196 | /*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n", |
197 | now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/ | |
4353a5c4 | 198 | if (!tv_io) return; |
94436798 | 199 | maxtime.tv_sec -= (now.tv_sec+2); |
200 | maxtime.tv_usec -= (now.tv_usec-2000000); | |
201 | dr= ldiv(maxtime.tv_usec,1000000); | |
4353a5c4 | 202 | maxtime.tv_sec += dr.quot; |
94436798 | 203 | maxtime.tv_usec -= dr.quot*1000000; |
204 | if (maxtime.tv_sec<0) timerclear(&maxtime); | |
4353a5c4 | 205 | inter_maxto(tv_io,tvbuf,maxtime); |
206 | } | |
207 | ||
f7f83b4a | 208 | static void timeouts_queue(adns_state ads, int act, |
209 | struct timeval **tv_io, struct timeval *tvbuf, | |
210 | struct timeval now, struct query_queue *queue) { | |
4353a5c4 | 211 | adns_query qu, nqu; |
f7f83b4a | 212 | |
213 | for (qu= queue->head; qu; qu= nqu) { | |
4353a5c4 | 214 | nqu= qu->next; |
d855b532 | 215 | if (!timercmp(&now,&qu->timeout,>)) { |
620c146d | 216 | inter_maxtoabs(tv_io,tvbuf,now,qu->timeout); |
217 | } else { | |
4fad263d | 218 | if (!act) { inter_immed(tv_io,tvbuf); return; } |
f7f83b4a | 219 | LIST_UNLINK(*queue,qu); |
d8c062fa | 220 | if (qu->state != query_tosend) { |
3955725c | 221 | adns__query_fail(qu,adns_s_timeout); |
4353a5c4 | 222 | } else { |
d8c062fa | 223 | adns__query_send(qu,now); |
4353a5c4 | 224 | } |
f7f83b4a | 225 | nqu= queue->head; |
4353a5c4 | 226 | } |
227 | } | |
f7f83b4a | 228 | } |
229 | ||
230 | static void tcp_events(adns_state ads, int act, | |
231 | struct timeval **tv_io, struct timeval *tvbuf, | |
232 | struct timeval now) { | |
f7f83b4a | 233 | for (;;) { |
234 | switch (ads->tcpstate) { | |
235 | case server_broken: | |
4fad263d | 236 | if (!act) { inter_immed(tv_io,tvbuf); return; } |
237ce710 | 237 | tcp_broken_events(ads); |
f7f83b4a | 238 | case server_disconnected: /* fall through */ |
239 | if (!ads->tcpw.head) return; | |
4fad263d | 240 | if (!act) { inter_immed(tv_io,tvbuf); return; } |
f7f83b4a | 241 | adns__tcp_tryconnect(ads,now); |
242 | break; | |
243 | case server_ok: | |
244 | if (ads->tcpw.head) return; | |
245 | if (!ads->tcptimeout.tv_sec) { | |
246 | assert(!ads->tcptimeout.tv_usec); | |
247 | ads->tcptimeout= now; | |
248 | timevaladd(&ads->tcptimeout,TCPIDLEMS); | |
249 | } | |
250 | case server_connecting: /* fall through */ | |
4fad263d | 251 | if (!act || !timercmp(&now,&ads->tcptimeout,>)) { |
f7f83b4a | 252 | inter_maxtoabs(tv_io,tvbuf,now,ads->tcptimeout); |
253 | return; | |
254 | } { | |
255 | /* TCP timeout has happened */ | |
256 | switch (ads->tcpstate) { | |
257 | case server_connecting: /* failed to connect */ | |
258 | adns__tcp_broken(ads,"unable to make connection","timed out"); | |
259 | break; | |
260 | case server_ok: /* idle timeout */ | |
261 | tcp_close(ads); | |
262 | ads->tcpstate= server_disconnected; | |
263 | return; | |
264 | default: | |
265 | abort(); | |
266 | } | |
267 | } | |
268 | break; | |
269 | default: | |
270 | abort(); | |
271 | } | |
272 | } | |
4fad263d | 273 | return; |
f7f83b4a | 274 | } |
275 | ||
276 | void adns__timeouts(adns_state ads, int act, | |
277 | struct timeval **tv_io, struct timeval *tvbuf, | |
278 | struct timeval now) { | |
279 | timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->udpw); | |
280 | timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->tcpw); | |
281 | tcp_events(ads,act,tv_io,tvbuf,now); | |
282 | } | |
94436798 | 283 | |
620c146d | 284 | void adns_firsttimeout(adns_state ads, |
285 | struct timeval **tv_io, struct timeval *tvbuf, | |
286 | struct timeval now) { | |
28de6442 | 287 | adns__consistency(ads,0,cc_entex); |
620c146d | 288 | adns__timeouts(ads, 0, tv_io,tvbuf, now); |
eb2a930e | 289 | adns__returning(ads,0); |
620c146d | 290 | } |
291 | ||
292 | void adns_processtimeouts(adns_state ads, const struct timeval *now) { | |
293 | struct timeval tv_buf; | |
294 | ||
28de6442 | 295 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 296 | adns__must_gettimeofday(ads,&now,&tv_buf); |
297 | if (now) adns__timeouts(ads, 1, 0,0, *now); | |
eb2a930e | 298 | adns__returning(ads,0); |
a8768e80 | 299 | } |
300 | ||
620c146d | 301 | /* fd handling functions. These are the top-level of the real work of |
302 | * reception and often transmission. | |
303 | */ | |
304 | ||
305 | int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) { | |
306 | /* Returns the number of entries filled in. Always zeroes revents. */ | |
13d889da IJ |
307 | int nwanted=0; |
308 | #define ADD_POLLFD(wantfd, wantevents) do{ \ | |
309 | pollfds_buf[nwanted].fd= (wantfd); \ | |
310 | pollfds_buf[nwanted].events= (wantevents); \ | |
311 | pollfds_buf[nwanted].revents= 0; \ | |
312 | nwanted++; \ | |
313 | }while(0) | |
620c146d | 314 | |
3eb20edd | 315 | int i; |
a8768e80 | 316 | |
3eb20edd MW |
317 | assert(MAX_POLLFDS == MAXUDP + 1); |
318 | ||
97a937c8 IJ |
319 | for (i=0; i<ads->nudpsockets; i++) |
320 | ADD_POLLFD(ads->udpsockets[i].fd, POLLIN); | |
4353a5c4 | 321 | |
322 | switch (ads->tcpstate) { | |
323 | case server_disconnected: | |
6c68a593 | 324 | case server_broken: |
13d889da | 325 | break; |
4353a5c4 | 326 | case server_connecting: |
13d889da | 327 | ADD_POLLFD(ads->tcpsocket, POLLOUT); |
4353a5c4 | 328 | break; |
329 | case server_ok: | |
13d889da IJ |
330 | ADD_POLLFD(ads->tcpsocket, |
331 | ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI); | |
e062dcae | 332 | break; |
4353a5c4 | 333 | default: |
334 | abort(); | |
335 | } | |
13d889da IJ |
336 | assert(nwanted<=MAX_POLLFDS); |
337 | #undef ADD_POLLFD | |
338 | return nwanted; | |
4353a5c4 | 339 | } |
340 | ||
620c146d | 341 | int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { |
8b9cc94c IJ |
342 | int want, dgramlen, r, i, serv, old_skip; |
343 | socklen_t udpaddrlen; | |
98a3f706 | 344 | byte udpbuf[DNS_MAXUDP]; |
3eb20edd MW |
345 | char addrbuf[ADNS_ADDR2TEXT_BUFLEN]; |
346 | struct udpsocket *udp; | |
347 | adns_sockaddr udpaddr; | |
620c146d | 348 | |
28de6442 | 349 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 350 | |
ddfda861 | 351 | switch (ads->tcpstate) { |
352 | case server_disconnected: | |
fb901bf5 | 353 | case server_broken: |
ddfda861 | 354 | case server_connecting: |
ddfda861 | 355 | break; |
356 | case server_ok: | |
620c146d | 357 | if (fd != ads->tcpsocket) break; |
70ad7a2a | 358 | assert(!ads->tcprecv_skip); |
428bd682 | 359 | do { |
70ad7a2a | 360 | if (ads->tcprecv.used >= ads->tcprecv_skip+2) { |
361 | dgramlen= ((ads->tcprecv.buf[ads->tcprecv_skip]<<8) | | |
362 | ads->tcprecv.buf[ads->tcprecv_skip+1]); | |
363 | if (ads->tcprecv.used >= ads->tcprecv_skip+2+dgramlen) { | |
364 | old_skip= ads->tcprecv_skip; | |
365 | ads->tcprecv_skip += 2+dgramlen; | |
366 | adns__procdgram(ads, ads->tcprecv.buf+old_skip+2, | |
367 | dgramlen, ads->tcpserver, 1,*now); | |
368 | continue; | |
656b2da9 | 369 | } else { |
70ad7a2a | 370 | want= 2+dgramlen; |
656b2da9 | 371 | } |
70ad7a2a | 372 | } else { |
373 | want= 2; | |
656b2da9 | 374 | } |
70ad7a2a | 375 | ads->tcprecv.used -= ads->tcprecv_skip; |
609133ee | 376 | memmove(ads->tcprecv.buf, ads->tcprecv.buf+ads->tcprecv_skip, |
377 | ads->tcprecv.used); | |
70ad7a2a | 378 | ads->tcprecv_skip= 0; |
3e2e5fab | 379 | if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; } |
620c146d | 380 | assert(ads->tcprecv.used <= ads->tcprecv.avail); |
381 | if (ads->tcprecv.used == ads->tcprecv.avail) continue; | |
382 | r= read(ads->tcpsocket, | |
383 | ads->tcprecv.buf+ads->tcprecv.used, | |
384 | ads->tcprecv.avail-ads->tcprecv.used); | |
385 | if (r>0) { | |
386 | ads->tcprecv.used+= r; | |
387 | } else { | |
388 | if (r) { | |
3e2e5fab | 389 | if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; } |
620c146d | 390 | if (errno==EINTR) continue; |
3e2e5fab | 391 | if (errno_resources(errno)) { r= errno; goto xit; } |
656b2da9 | 392 | } |
620c146d | 393 | adns__tcp_broken(ads,"read",r?strerror(errno):"closed"); |
656b2da9 | 394 | } |
428bd682 | 395 | } while (ads->tcpstate == server_ok); |
396 | r= 0; goto xit; | |
ddfda861 | 397 | default: |
398 | abort(); | |
656b2da9 | 399 | } |
97a937c8 IJ |
400 | for (i=0; i<ads->nudpsockets; i++) { |
401 | udp= &ads->udpsockets[i]; | |
41536342 IJ |
402 | if (fd != udp->fd) continue; |
403 | for (;;) { | |
404 | udpaddrlen= sizeof(udpaddr); | |
405 | r= recvfrom(fd,udpbuf,sizeof(udpbuf),0, &udpaddr.sa,&udpaddrlen); | |
406 | if (r<0) { | |
407 | if (errno == EAGAIN || errno == EWOULDBLOCK) { r= 0; goto xit; } | |
408 | if (errno == EINTR) continue; | |
409 | if (errno_resources(errno)) { r= errno; goto xit; } | |
410 | adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno)); | |
411 | r= 0; goto xit; | |
37e28fde | 412 | } |
41536342 IJ |
413 | for (serv= 0; |
414 | serv < ads->nservers && | |
415 | !adns__sockaddrs_equal(&udpaddr.sa, | |
416 | &ads->servers[serv].addr.sa); | |
417 | serv++); | |
418 | if (serv >= ads->nservers) { | |
419 | adns__warn(ads,-1,0,"datagram received from unknown nameserver %s", | |
420 | adns__sockaddr_ntoa(&udpaddr.sa, addrbuf)); | |
421 | continue; | |
422 | } | |
423 | adns__procdgram(ads,udpbuf,r,serv,0,*now); | |
656b2da9 | 424 | } |
41536342 | 425 | break; |
37e28fde | 426 | } |
3e2e5fab | 427 | r= 0; |
428 | xit: | |
eb2a930e | 429 | adns__returning(ads,0); |
3e2e5fab | 430 | return r; |
656b2da9 | 431 | } |
656b2da9 | 432 | |
620c146d | 433 | int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) { |
656b2da9 | 434 | int r; |
620c146d | 435 | |
28de6442 | 436 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 437 | |
620c146d | 438 | switch (ads->tcpstate) { |
439 | case server_disconnected: | |
fb901bf5 | 440 | case server_broken: |
620c146d | 441 | break; |
442 | case server_connecting: | |
443 | if (fd != ads->tcpsocket) break; | |
444 | assert(ads->tcprecv.used==0); | |
70ad7a2a | 445 | assert(ads->tcprecv_skip==0); |
620c146d | 446 | for (;;) { |
777763cb IJ |
447 | /* This function can be called even if the fd wasn't actually |
448 | * flagged as writeable. For asynch tcp connect we have to | |
449 | * actually use the writeability to tell us the connect has | |
450 | * completed (or failed), so we need to double check. */ | |
451 | fd_set writeable; | |
452 | struct timeval timeout = { 0,0 }; | |
453 | FD_ZERO(&writeable); | |
454 | FD_SET(ads->tcpsocket,&writeable); | |
455 | r= select(ads->tcpsocket+1,0,&writeable,0,&timeout); | |
456 | if (r==0) break; | |
457 | if (r<0) { | |
458 | if (errno==EINTR) continue; | |
459 | adns__tcp_broken(ads,"select","failed connecting writeability check"); | |
460 | r= 0; goto xit; | |
461 | } | |
462 | assert(FD_ISSET(ads->tcpsocket,&writeable)); | |
3e2e5fab | 463 | if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; } |
620c146d | 464 | r= read(ads->tcpsocket,&ads->tcprecv.buf,1); |
465 | if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) { | |
466 | tcp_connected(ads,*now); | |
3e2e5fab | 467 | r= 0; goto xit; |
620c146d | 468 | } |
469 | if (r>0) { | |
470 | adns__tcp_broken(ads,"connect/read","sent data before first request"); | |
3e2e5fab | 471 | r= 0; goto xit; |
620c146d | 472 | } |
473 | if (errno==EINTR) continue; | |
3e2e5fab | 474 | if (errno_resources(errno)) { r= errno; goto xit; } |
620c146d | 475 | adns__tcp_broken(ads,"connect/read",strerror(errno)); |
3e2e5fab | 476 | r= 0; goto xit; |
620c146d | 477 | } /* not reached */ |
478 | case server_ok: | |
fb901bf5 | 479 | if (fd != ads->tcpsocket) break; |
480 | while (ads->tcpsend.used) { | |
620c146d | 481 | adns__sigpipe_protect(ads); |
482 | r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used); | |
483 | adns__sigpipe_unprotect(ads); | |
484 | if (r<0) { | |
485 | if (errno==EINTR) continue; | |
3e2e5fab | 486 | if (errno==EAGAIN || errno==EWOULDBLOCK) { r= 0; goto xit; } |
487 | if (errno_resources(errno)) { r= errno; goto xit; } | |
620c146d | 488 | adns__tcp_broken(ads,"write",strerror(errno)); |
3e2e5fab | 489 | r= 0; goto xit; |
620c146d | 490 | } else if (r>0) { |
491 | ads->tcpsend.used -= r; | |
492 | memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used); | |
493 | } | |
fb901bf5 | 494 | } |
495 | r= 0; | |
496 | goto xit; | |
620c146d | 497 | default: |
498 | abort(); | |
499 | } | |
3e2e5fab | 500 | r= 0; |
501 | xit: | |
eb2a930e | 502 | adns__returning(ads,0); |
3e2e5fab | 503 | return r; |
620c146d | 504 | } |
505 | ||
609133ee | 506 | int adns_processexceptional(adns_state ads, int fd, |
507 | const struct timeval *now) { | |
28de6442 | 508 | adns__consistency(ads,0,cc_entex); |
620c146d | 509 | switch (ads->tcpstate) { |
510 | case server_disconnected: | |
fb901bf5 | 511 | case server_broken: |
620c146d | 512 | break; |
513 | case server_connecting: | |
514 | case server_ok: | |
515 | if (fd != ads->tcpsocket) break; | |
516 | adns__tcp_broken(ads,"poll/select","exceptional condition detected"); | |
3e2e5fab | 517 | break; |
620c146d | 518 | default: |
519 | abort(); | |
520 | } | |
eb2a930e | 521 | adns__returning(ads,0); |
620c146d | 522 | return 0; |
523 | } | |
37e28fde | 524 | |
620c146d | 525 | static void fd_event(adns_state ads, int fd, |
526 | int revent, int pollflag, | |
527 | int maxfd, const fd_set *fds, | |
609133ee | 528 | int (*func)(adns_state, int fd, |
529 | const struct timeval *now), | |
620c146d | 530 | struct timeval now, int *r_r) { |
656b2da9 | 531 | int r; |
620c146d | 532 | |
533 | if (!(revent & pollflag)) return; | |
534 | if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return; | |
535 | r= func(ads,fd,&now); | |
536 | if (r) { | |
537 | if (r_r) { | |
538 | *r_r= r; | |
539 | } else { | |
609133ee | 540 | adns__diag(ads,-1,0,"process fd failed after select:" |
541 | " %s",strerror(errno)); | |
620c146d | 542 | adns_globalsystemfailure(ads); |
543 | } | |
544 | } | |
545 | } | |
546 | ||
547 | void adns__fdevents(adns_state ads, | |
548 | const struct pollfd *pollfds, int npollfds, | |
549 | int maxfd, const fd_set *readfds, | |
550 | const fd_set *writefds, const fd_set *exceptfds, | |
551 | struct timeval now, int *r_r) { | |
552 | int i, fd, revents; | |
553 | ||
554 | for (i=0; i<npollfds; i++) { | |
555 | fd= pollfds[i].fd; | |
556 | if (fd >= maxfd) maxfd= fd+1; | |
557 | revents= pollfds[i].revents; | |
609133ee | 558 | #define EV(pollfl,fds,how) \ |
559 | fd_event(ads,fd, revents,pollfl, maxfd,fds, adns_process##how,now,r_r) | |
560 | EV( POLLIN, readfds, readable ); | |
561 | EV( POLLOUT, writefds, writeable ); | |
562 | EV( POLLPRI, exceptfds, exceptional ); | |
563 | #undef EV | |
620c146d | 564 | } |
565 | } | |
37e28fde | 566 | |
620c146d | 567 | /* Wrappers for select(2). */ |
568 | ||
569 | void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io, | |
570 | fd_set *writefds_io, fd_set *exceptfds_io, | |
571 | struct timeval **tv_mod, struct timeval *tv_tobuf, | |
572 | const struct timeval *now) { | |
573 | struct timeval tv_nowbuf; | |
574 | struct pollfd pollfds[MAX_POLLFDS]; | |
575 | int i, fd, maxfd, npollfds; | |
576 | ||
28de6442 | 577 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 578 | |
620c146d | 579 | if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) { |
580 | /* The caller is planning to sleep. */ | |
581 | adns__must_gettimeofday(ads,&now,&tv_nowbuf); | |
26cc07a3 | 582 | if (!now) { inter_immed(tv_mod,tv_tobuf); goto xit; } |
6c68a593 | 583 | adns__timeouts(ads, 0, tv_mod,tv_tobuf, *now); |
620c146d | 584 | } |
585 | ||
586 | npollfds= adns__pollfds(ads,pollfds); | |
587 | maxfd= *maxfd_io; | |
588 | for (i=0; i<npollfds; i++) { | |
589 | fd= pollfds[i].fd; | |
590 | if (fd >= maxfd) maxfd= fd+1; | |
591 | if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io); | |
592 | if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io); | |
593 | if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io); | |
594 | } | |
595 | *maxfd_io= maxfd; | |
3e2e5fab | 596 | |
597 | xit: | |
eb2a930e | 598 | adns__returning(ads,0); |
620c146d | 599 | } |
600 | ||
601 | void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, | |
602 | const fd_set *writefds, const fd_set *exceptfds, | |
603 | const struct timeval *now) { | |
fc6a52ae | 604 | struct timeval tv_buf; |
620c146d | 605 | struct pollfd pollfds[MAX_POLLFDS]; |
fc6a52ae | 606 | int npollfds, i; |
620c146d | 607 | |
28de6442 | 608 | adns__consistency(ads,0,cc_entex); |
fc6a52ae | 609 | adns__must_gettimeofday(ads,&now,&tv_buf); |
3e2e5fab | 610 | if (!now) goto xit; |
620c146d | 611 | adns_processtimeouts(ads,now); |
612 | ||
613 | npollfds= adns__pollfds(ads,pollfds); | |
fc6a52ae | 614 | for (i=0; i<npollfds; i++) pollfds[i].revents= POLLIN|POLLOUT|POLLPRI; |
620c146d | 615 | adns__fdevents(ads, |
616 | pollfds,npollfds, | |
617 | maxfd,readfds,writefds,exceptfds, | |
618 | *now, 0); | |
3e2e5fab | 619 | xit: |
eb2a930e | 620 | adns__returning(ads,0); |
620c146d | 621 | } |
622 | ||
623 | /* General helpful functions. */ | |
624 | ||
625 | void adns_globalsystemfailure(adns_state ads) { | |
28de6442 | 626 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 627 | |
f7f83b4a | 628 | while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail); |
629 | while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail); | |
620c146d | 630 | |
631 | switch (ads->tcpstate) { | |
632 | case server_connecting: | |
633 | case server_ok: | |
f7f83b4a | 634 | adns__tcp_broken(ads,0,0); |
620c146d | 635 | break; |
636 | case server_disconnected: | |
fb901bf5 | 637 | case server_broken: |
620c146d | 638 | break; |
639 | default: | |
640 | abort(); | |
641 | } | |
eb2a930e | 642 | adns__returning(ads,0); |
656b2da9 | 643 | } |
644 | ||
620c146d | 645 | int adns_processany(adns_state ads) { |
aedf10f2 | 646 | int r, i; |
620c146d | 647 | struct timeval now; |
648 | struct pollfd pollfds[MAX_POLLFDS]; | |
649 | int npollfds; | |
650 | ||
28de6442 | 651 | adns__consistency(ads,0,cc_entex); |
3e2e5fab | 652 | |
620c146d | 653 | r= gettimeofday(&now,0); |
654 | if (!r) adns_processtimeouts(ads,&now); | |
655 | ||
25913ee2 | 656 | /* We just use adns__fdevents to loop over the fd's trying them. |
657 | * This seems more sensible than calling select, since we're most | |
658 | * likely just to want to do a read on one or two fds anyway. | |
659 | */ | |
620c146d | 660 | npollfds= adns__pollfds(ads,pollfds); |
1ed3dba4 | 661 | for (i=0; i<npollfds; i++) pollfds[i].revents= pollfds[i].events & ~POLLPRI; |
620c146d | 662 | adns__fdevents(ads, |
663 | pollfds,npollfds, | |
664 | 0,0,0,0, | |
665 | now,&r); | |
3e2e5fab | 666 | |
eb2a930e | 667 | adns__returning(ads,0); |
c118a725 | 668 | return 0; |
620c146d | 669 | } |
ddfda861 | 670 | |
4353a5c4 | 671 | void adns__autosys(adns_state ads, struct timeval now) { |
ddfda861 | 672 | if (ads->iflags & adns_if_noautosys) return; |
620c146d | 673 | adns_processany(ads); |
ddfda861 | 674 | } |
675 | ||
940356bd | 676 | int adns__internal_check(adns_state ads, |
677 | adns_query *query_io, | |
678 | adns_answer **answer, | |
679 | void **context_r) { | |
656b2da9 | 680 | adns_query qu; |
681 | ||
682 | qu= *query_io; | |
683 | if (!qu) { | |
4ae90a83 | 684 | if (ads->output.head) { |
685 | qu= ads->output.head; | |
f7f83b4a | 686 | } else if (ads->udpw.head || ads->tcpw.head) { |
4ae90a83 | 687 | return EAGAIN; |
688 | } else { | |
689 | return ESRCH; | |
690 | } | |
656b2da9 | 691 | } else { |
226c5eef | 692 | if (qu->id>=0) return EAGAIN; |
656b2da9 | 693 | } |
694 | LIST_UNLINK(ads->output,qu); | |
8e5b0abb | 695 | *answer= qu->answer; |
a6536d8b | 696 | if (context_r) *context_r= qu->ctx.ext; |
8ce38e76 | 697 | *query_io= qu; |
656b2da9 | 698 | free(qu); |
699 | return 0; | |
700 | } | |
701 | ||
702 | int adns_wait(adns_state ads, | |
703 | adns_query *query_io, | |
704 | adns_answer **answer_r, | |
705 | void **context_r) { | |
620c146d | 706 | int r, maxfd, rsel; |
656b2da9 | 707 | fd_set readfds, writefds, exceptfds; |
26cc07a3 | 708 | struct timeval tvbuf, *tvp; |
656b2da9 | 709 | |
28de6442 | 710 | adns__consistency(ads,*query_io,cc_entex); |
656b2da9 | 711 | for (;;) { |
940356bd | 712 | r= adns__internal_check(ads,query_io,answer_r,context_r); |
226c5eef | 713 | if (r != EAGAIN) break; |
656b2da9 | 714 | maxfd= 0; tvp= 0; |
37e28fde | 715 | FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); |
620c146d | 716 | adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0); |
4fad263d | 717 | assert(tvp); |
656b2da9 | 718 | rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp); |
88372443 | 719 | if (rsel==-1) { |
620c146d | 720 | if (errno == EINTR) { |
3e2e5fab | 721 | if (ads->iflags & adns_if_eintr) { r= EINTR; break; } |
620c146d | 722 | } else { |
723 | adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno)); | |
724 | adns_globalsystemfailure(ads); | |
725 | } | |
726 | } else { | |
727 | assert(rsel >= 0); | |
728 | adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0); | |
88372443 | 729 | } |
656b2da9 | 730 | } |
eb2a930e | 731 | adns__returning(ads,0); |
3e2e5fab | 732 | return r; |
656b2da9 | 733 | } |
734 | ||
735 | int adns_check(adns_state ads, | |
736 | adns_query *query_io, | |
737 | adns_answer **answer_r, | |
738 | void **context_r) { | |
4353a5c4 | 739 | struct timeval now; |
740 | int r; | |
741 | ||
28de6442 | 742 | adns__consistency(ads,*query_io,cc_entex); |
620c146d | 743 | r= gettimeofday(&now,0); |
744 | if (!r) adns__autosys(ads,now); | |
3e2e5fab | 745 | |
940356bd | 746 | r= adns__internal_check(ads,query_io,answer_r,context_r); |
eb2a930e | 747 | adns__returning(ads,0); |
3e2e5fab | 748 | return r; |
656b2da9 | 749 | } |