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