X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;ds=sidebyside;f=src%2Fevent.c;h=c326c2809714d5313ef6844912cf11ed2e9b6f1e;hb=5d3224e9de99d596dc8d973a75266afc603a46cd;hp=378965630b8559f5875f27021caeff0c7d2925a5;hpb=8c09a4c65fb124c379c349f2ef8b84ad9e440545;p=adns.git diff --git a/src/event.c b/src/event.c index 3789656..c326c28 100644 --- a/src/event.c +++ b/src/event.c @@ -6,7 +6,8 @@ */ /* * This file is part of adns, which is - * Copyright (C) 1997-2000,2003,2006 Ian Jackson + * Copyright (C) 1997-2000,2003,2006,2014-2016,2020 Ian Jackson + * Copyright (C) 2014 Mark Wooding * Copyright (C) 1999-2000,2003,2006 Tony Finch * Copyright (C) 1991 Massachusetts Institute of Technology * (See the file INSTALL for full details.) @@ -35,6 +36,7 @@ #include #include #include +#include #include "internal.h" #include "tvarith.h" @@ -148,6 +150,19 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { /* Timeout handling functions. */ +int adns__gettimeofday(adns_state ads, struct timeval *tv) { + if (!(ads->iflags & adns_if_monotonic)) + return gettimeofday(tv,0); + + struct timespec ts; + int r = clock_gettime(CLOCK_MONOTONIC,&ts); + if (r) return r; + + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + return 0; +} + void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, struct timeval *tv_buf) { const struct timeval *now; @@ -155,8 +170,9 @@ void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, now= *now_io; if (now) return; - r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; } - adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno)); + r= adns__gettimeofday(ads,tv_buf); if (!r) { *now_io= tv_buf; return; } + adns__diag(ads,-1,0,"gettimeofday/clock_gettime failed: %s", + strerror(errno)); adns_globalsystemfailure(ads); return; } @@ -208,11 +224,16 @@ static void timeouts_queue(adns_state ads, int act, struct timeval **tv_io, struct timeval *tvbuf, struct timeval now, struct query_queue *queue) { adns_query qu, nqu; + struct timeval expires; for (qu= queue->head; qu; qu= nqu) { nqu= qu->next; - if (!timercmp(&now,&qu->timeout,>)) { - inter_maxtoabs(tv_io,tvbuf,now,qu->timeout); + if (timercmp(&now,&qu->timeout_started,<)) /* clock rewound */ + qu->timeout_started= now; + expires= qu->timeout_started; + timevaladd(&expires, qu->timeout_ms); + if (!timercmp(&now,&expires,>)) { + inter_maxtoabs(tv_io,tvbuf,now,expires); } else { if (!act) { inter_immed(tv_io,tvbuf); return; } LIST_UNLINK(*queue,qu); @@ -283,7 +304,7 @@ void adns__timeouts(adns_state ads, int act, void adns_firsttimeout(adns_state ads, struct timeval **tv_io, struct timeval *tvbuf, struct timeval now) { - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); adns__timeouts(ads, 0, tv_io,tvbuf, now); adns__returning(ads,0); } @@ -291,7 +312,7 @@ void adns_firsttimeout(adns_state ads, void adns_processtimeouts(adns_state ads, const struct timeval *now) { struct timeval tv_buf; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); adns__must_gettimeofday(ads,&now,&tv_buf); if (now) adns__timeouts(ads, 1, 0,0, *now); adns__returning(ads,0); @@ -338,13 +359,14 @@ int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) { } int adns_processreadable(adns_state ads, int fd, const struct timeval *now) { - int want, dgramlen, r, i, udpaddrlen, serv, old_skip; + int want, dgramlen, r, i, serv, old_skip; + socklen_t udpaddrlen; byte udpbuf[DNS_MAXUDP]; char addrbuf[ADNS_ADDR2TEXT_BUFLEN]; struct udpsocket *udp; adns_sockaddr udpaddr; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); switch (ads->tcpstate) { case server_disconnected: @@ -431,7 +453,7 @@ xit: int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) { int r; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); switch (ads->tcpstate) { case server_disconnected: @@ -442,8 +464,24 @@ int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) { assert(ads->tcprecv.used==0); assert(ads->tcprecv_skip==0); for (;;) { + /* This function can be called even if the fd wasn't actually + * flagged as writeable. For asynch tcp connect we have to + * actually use the writeability to tell us the connect has + * completed (or failed), so we need to double check. */ + fd_set writeable; + struct timeval timeout = { 0,0 }; + FD_ZERO(&writeable); + FD_SET(ads->tcpsocket,&writeable); + r= select(ads->tcpsocket+1,0,&writeable,0,&timeout); + if (r==0) break; + if (r<0) { + if (errno==EINTR) continue; + adns__tcp_broken(ads,"select","failed connecting writeability check"); + r= 0; goto xit; + } + assert(FD_ISSET(ads->tcpsocket,&writeable)); if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; } - r= read(ads->tcpsocket,&ads->tcprecv.buf,1); + r= read(ads->tcpsocket,ads->tcprecv.buf,1); if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) { tcp_connected(ads,*now); r= 0; goto xit; @@ -470,6 +508,7 @@ int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) { adns__tcp_broken(ads,"write",strerror(errno)); r= 0; goto xit; } else if (r>0) { + assert(r <= ads->tcpsend.used); ads->tcpsend.used -= r; memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used); } @@ -487,7 +526,7 @@ xit: int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) { - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); switch (ads->tcpstate) { case server_disconnected: case server_broken: @@ -556,7 +595,7 @@ void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io, struct pollfd pollfds[MAX_POLLFDS]; int i, fd, maxfd, npollfds; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) { /* The caller is planning to sleep. */ @@ -587,7 +626,7 @@ void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, struct pollfd pollfds[MAX_POLLFDS]; int npollfds, i; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); adns__must_gettimeofday(ads,&now,&tv_buf); if (!now) goto xit; adns_processtimeouts(ads,now); @@ -605,10 +644,24 @@ xit: /* General helpful functions. */ void adns_globalsystemfailure(adns_state ads) { - adns__consistency(ads,0,cc_entex); + /* Must not be called by adns during actual processing of a + * particular query, since it reenters adns. Only safe to call in + * situations where it would be safe to call adns_returning. */ + adns__consistency(ads,0,cc_enter); - while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail); - while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail); + for (;;) { + adns_query qu; +#define GSF_QQ(QQ) \ + if ((qu= ads->QQ.head)) { \ + LIST_UNLINK(ads->QQ,qu); \ + adns__query_fail(qu, adns_s_systemfail); \ + continue; \ + } + GSF_QQ(udpw); + GSF_QQ(tcpw); +#undef GSF_QQ + break; + } switch (ads->tcpstate) { case server_connecting: @@ -630,9 +683,9 @@ int adns_processany(adns_state ads) { struct pollfd pollfds[MAX_POLLFDS]; int npollfds; - adns__consistency(ads,0,cc_entex); + adns__consistency(ads,0,cc_enter); - r= gettimeofday(&now,0); + r= adns__gettimeofday(ads,&now); if (!r) adns_processtimeouts(ads,&now); /* We just use adns__fdevents to loop over the fd's trying them. @@ -689,7 +742,7 @@ int adns_wait(adns_state ads, fd_set readfds, writefds, exceptfds; struct timeval tvbuf, *tvp; - adns__consistency(ads,*query_io,cc_entex); + adns__consistency(ads,*query_io,cc_enter); for (;;) { r= adns__internal_check(ads,query_io,answer_r,context_r); if (r != EAGAIN) break; @@ -721,8 +774,8 @@ int adns_check(adns_state ads, struct timeval now; int r; - adns__consistency(ads,*query_io,cc_entex); - r= gettimeofday(&now,0); + adns__consistency(ads,*query_io,cc_enter); + r= adns__gettimeofday(ads,&now); if (!r) adns__autosys(ads,now); r= adns__internal_check(ads,query_io,answer_r,context_r);