From: Otto Date: Tue, 25 May 2021 10:05:30 +0000 (+0200) Subject: Convert timeout values to be specified as a timeval, so sub-second timeout X-Git-Tag: dnsdist-1.7.0-alpha1~138^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5011172846a52f4a34b5952b7bb8f8a80350ea43;p=thirdparty%2Fpdns.git Convert timeout values to be specified as a timeval, so sub-second timeout values can be handled correctly. Also make sure sdig uses a NB socket, to handle timeouts correctly. --- diff --git a/pdns/auth-carbon.cc b/pdns/auth-carbon.cc index 8fa3e1596a..41767bc7bf 100644 --- a/pdns/auth-carbon.cc +++ b/pdns/auth-carbon.cc @@ -76,7 +76,7 @@ try s.setNonBlocking(); s.connect(remote, 2); - writen2WithTimeout(s.getHandle(), msg.c_str(), msg.length(), 2); + writen2WithTimeout(s.getHandle(), msg.c_str(), msg.length(), timeval{2,0}); } catch (runtime_error &e){ g_log<setNonBlocking(); gettimeofday(&d_connectionStartTime, nullptr); - auto handler = std::make_unique("", socket->releaseHandle(), 0, d_ds->d_tlsCtx, time(nullptr)); + auto handler = std::make_unique("", socket->releaseHandle(), timeval{0,0}, d_ds->d_tlsCtx, time(nullptr)); handler->tryConnect(d_ds->tcpFastOpen && isFastOpenEnabled(), d_ds->remote); d_queries = 0; diff --git a/pdns/dnsdistdist/dnsdist-tcp-upstream.hh b/pdns/dnsdistdist/dnsdist-tcp-upstream.hh index ae971b070e..4e49421400 100644 --- a/pdns/dnsdistdist/dnsdist-tcp-upstream.hh +++ b/pdns/dnsdistdist/dnsdist-tcp-upstream.hh @@ -58,7 +58,7 @@ struct ConnectionInfo class IncomingTCPConnectionState { public: - IncomingTCPConnectionState(ConnectionInfo&& ci, TCPClientThreadData& threadData, const struct timeval& now): d_buffer(s_maxPacketCacheEntrySize), d_threadData(threadData), d_ci(std::move(ci)), d_handler(d_ci.fd, g_tcpRecvTimeout, d_ci.cs->tlsFrontend ? d_ci.cs->tlsFrontend->getContext() : nullptr, now.tv_sec), d_ioState(make_unique(threadData.mplexer, d_ci.fd)), d_connectionStartTime(now) + IncomingTCPConnectionState(ConnectionInfo&& ci, TCPClientThreadData& threadData, const struct timeval& now): d_buffer(s_maxPacketCacheEntrySize), d_threadData(threadData), d_ci(std::move(ci)), d_handler(d_ci.fd, timeval{g_tcpRecvTimeout,0}, d_ci.cs->tlsFrontend ? d_ci.cs->tlsFrontend->getContext() : nullptr, now.tv_sec), d_ioState(make_unique(threadData.mplexer, d_ci.fd)), d_connectionStartTime(now) { d_origDest.reset(); d_origDest.sin4.sin_family = d_ci.remote.sin4.sin_family; diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index 78737a7ca2..55ec5b454b 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -238,16 +238,16 @@ public: { } - void connect(bool fastOpen, const ComboAddress& remote, unsigned int timeout) override + void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override { } - size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout=0) override + size_t read(void* buffer, size_t bufferSize, const struct timeval&readTimeout, const struct timeval& totalTimeout={0,0}) override { return 0; } - size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) override + size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override { return 0; } @@ -276,12 +276,12 @@ public: { } - std::unique_ptr getConnection(int socket, unsigned int timeout, time_t now) override + std::unique_ptr getConnection(int socket, const struct timeval& timeout, time_t now) override { return std::make_unique(socket); } - std::unique_ptr getClientConnection(const std::string& host, int socket, unsigned int timeout) override + std::unique_ptr getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override { return std::make_unique(socket, true); } diff --git a/pdns/dnsproxy.cc b/pdns/dnsproxy.cc index be12827a7e..db07c2a6bb 100644 --- a/pdns/dnsproxy.cc +++ b/pdns/dnsproxy.cc @@ -123,7 +123,7 @@ bool DNSProxy::completePacket(std::unique_ptr& r, const DNSName& targ uint16_t len=htons(r->getString().length()); string buffer((const char*)&len, 2); buffer.append(r->getString()); - writen2WithTimeout(r->getSocket(), buffer.c_str(), buffer.length(), ::arg().asNum("tcp-idle-timeout")); + writen2WithTimeout(r->getSocket(), buffer.c_str(), buffer.length(), timeval{::arg().asNum("tcp-idle-timeout"),0}); return true; } diff --git a/pdns/dolog.hh b/pdns/dolog.hh index 62e1c6a3de..557c767aad 100644 --- a/pdns/dolog.hh +++ b/pdns/dolog.hh @@ -124,6 +124,11 @@ inline void dolog(Logger::Urgency u, const char* s) g_log << u << s << std::endl; } +inline void dolog(const char* s) +{ + g_log << s << std::endl; +} + template void dolog(Logger::Urgency u, const char* s, T value, Args... args) { @@ -136,7 +141,7 @@ void dolog(Logger::Urgency u, const char* s, T value, Args... args) else { g_log << value; s += 2; - dolog(u, s, args...); + dolog(s, args...); return; } } diff --git a/pdns/iputils.cc b/pdns/iputils.cc index 216945b7cc..c5aa2a87b2 100644 --- a/pdns/iputils.cc +++ b/pdns/iputils.cc @@ -55,20 +55,20 @@ int SConnect(int sockfd, const ComboAddress& remote) return ret; } -int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout) +int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout) { int ret = connect(sockfd, reinterpret_cast(&remote), remote.getSocklen()); if(ret < 0) { int savederrno = errno; if (savederrno == EINPROGRESS) { - if (timeout <= 0) { + if (timeout <= timeval{0,0}) { return savederrno; } /* we wait until the connection has been established */ bool error = false; bool disconnected = false; - int res = waitForRWData(sockfd, false, timeout, 0, &error, &disconnected); + int res = waitForRWData(sockfd, false, timeout.tv_sec, timeout.tv_usec, &error, &disconnected); if (res == 1) { if (error) { savederrno = 0; diff --git a/pdns/iputils.hh b/pdns/iputils.hh index 92055f8c2c..32073637e1 100644 --- a/pdns/iputils.hh +++ b/pdns/iputils.hh @@ -1428,7 +1428,7 @@ int SConnect(int sockfd, const ComboAddress& remote); sockfd should be set to non-blocking beforehand. returns 0 on success (the socket is writable), throw a runtime_error otherwise */ -int SConnectWithTimeout(int sockfd, const ComboAddress& remote, int timeout); +int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout); int SBind(int sockfd, const ComboAddress& local); int SAccept(int sockfd, ComboAddress& remote); int SListen(int sockfd, int limit); diff --git a/pdns/ixfrdist.cc b/pdns/ixfrdist.cc index 6774ed3624..f7d58eb30f 100644 --- a/pdns/ixfrdist.cc +++ b/pdns/ixfrdist.cc @@ -878,7 +878,7 @@ static void tcpWorker(int tid) { uint16_t toRead; readn2(cfd, &toRead, sizeof(toRead)); toRead = std::min(ntohs(toRead), static_cast(sizeof(buf))); - res = readn2WithTimeout(cfd, &buf, toRead, 2); + res = readn2WithTimeout(cfd, &buf, toRead, timeval{2,0}); g_log< 0) { /* there is data available */ } @@ -144,14 +145,15 @@ size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int } } - if (totalTimeout) { - time_t now = time(nullptr); - int elapsed = now - start; - if (elapsed >= remainingTime) { + if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) { + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval elapsed = now - start; + if (remainingTime < elapsed) { throw runtime_error("Timeout while reading data"); } start = now; - remainingTime -= elapsed; + remainingTime = remainingTime - elapsed; } } while (pos < len); @@ -159,7 +161,7 @@ size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int return len; } -size_t writen2WithTimeout(int fd, const void * buffer, size_t len, int timeout) +size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout) { size_t pos = 0; do { @@ -172,7 +174,7 @@ size_t writen2WithTimeout(int fd, const void * buffer, size_t len, int timeout) throw runtime_error("EOF while writing message"); else { if (errno == EAGAIN) { - int res = waitForRWData(fd, false, timeout, 0); + int res = waitForRWData(fd, false, timeout.tv_sec, timeout.tv_usec); if (res > 0) { /* there is room available */ } diff --git a/pdns/misc.hh b/pdns/misc.hh index 9b19924292..0372e8ffae 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -149,8 +149,8 @@ vstringtok (Container &container, string const &in, size_t writen2(int fd, const void *buf, size_t count); inline size_t writen2(int fd, const std::string &s) { return writen2(fd, s.data(), s.size()); } size_t readn2(int fd, void* buffer, size_t len); -size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int totalTimeout=0); -size_t writen2WithTimeout(int fd, const void * buffer, size_t len, int timeout); +size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout={0,0}); +size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout); void toLowerInPlace(string& str); const string toLower(const string &upper); @@ -319,7 +319,11 @@ inline uint64_t uSec(const struct timeval& tv) inline bool operator<(const struct timeval& lhs, const struct timeval& rhs) { - return make_pair(lhs.tv_sec, lhs.tv_usec) < make_pair(rhs.tv_sec, rhs.tv_usec); + return tie(lhs.tv_sec, lhs.tv_usec) < tie(rhs.tv_sec, rhs.tv_usec); +} +inline bool operator<=(const struct timeval& lhs, const struct timeval& rhs) +{ + return tie(lhs.tv_sec, lhs.tv_usec) <= tie(rhs.tv_sec, rhs.tv_usec); } inline bool operator<(const struct timespec& lhs, const struct timespec& rhs) diff --git a/pdns/rec-carbon.cc b/pdns/rec-carbon.cc index 663846eb94..9b55e7b6bf 100644 --- a/pdns/rec-carbon.cc +++ b/pdns/rec-carbon.cc @@ -51,7 +51,7 @@ try Socket s(remote.sin4.sin_family, SOCK_STREAM); s.setNonBlocking(); std::shared_ptr tlsCtx{nullptr}; - const int timeout = (g_networkTimeoutMsec + 999) / 1000; // XXX tcpiohandler's unit is seconds + const struct timeval timeout{g_networkTimeoutMsec / 1000, g_networkTimeoutMsec % 1000 * 1000}; auto handler = std::make_shared("", s.releaseHandle(), timeout, tlsCtx, time(nullptr)); handler->tryConnect(SyncRes::s_tcp_fast_open_connect, remote);// we do the connect so the first attempt happens while we gather stats diff --git a/pdns/sdig.cc b/pdns/sdig.cc index 1d25fba414..12eb822bb3 100644 --- a/pdns/sdig.cc +++ b/pdns/sdig.cc @@ -197,7 +197,7 @@ static void printReply(const string& reply, bool showflags, bool hidesoadetails, int main(int argc, char** argv) try { /* default timeout of 10s */ - int timeout = 10; + struct timeval timeout{10,0}; bool dnssec = false; bool recurse = false; bool tcp = false; @@ -374,7 +374,7 @@ try { mch.insert(std::make_pair("Accept", "application/dns-message")); string question(packet.begin(), packet.end()); // FIXME: how do we use proxyheader here? - reply = mc.postURL(argv[1], question, mch, timeout, fastOpen); + reply = mc.postURL(argv[1], question, mch, timeout.tv_sec, fastOpen); printReply(reply, showflags, hidesoadetails, dumpluaraw); #else throw PDNSException("please link sdig against libcurl for DoH support"); @@ -409,6 +409,7 @@ try { } uint16_t counter = 0; Socket sock(dest.sin4.sin_family, SOCK_STREAM); + sock.setNonBlocking(); setTCPNoDelay(sock.getHandle()); // disable NAGLE, which does not play nicely with delayed ACKs TCPIOHandler handler(subjectName, sock.releaseHandle(), timeout, tlsCtx, time(nullptr)); handler.connect(fastOpen, dest, timeout); @@ -457,7 +458,7 @@ try { Socket sock(dest.sin4.sin_family, SOCK_DGRAM); question = proxyheader + question; sock.sendTo(question, dest); - int result = waitForData(sock.getHandle(), timeout); + int result = waitForData(sock.getHandle(), timeout.tv_sec, timeout.tv_usec); if (result < 0) throw std::runtime_error("Error waiting for data: " + stringerror()); if (!result) diff --git a/pdns/sstuff.hh b/pdns/sstuff.hh index c253046521..5787f41fb7 100644 --- a/pdns/sstuff.hh +++ b/pdns/sstuff.hh @@ -154,7 +154,7 @@ public: //! Connect the socket to a specified endpoint void connect(const ComboAddress &ep, int timeout=0) { - SConnectWithTimeout(d_socket, ep, timeout); + SConnectWithTimeout(d_socket, ep, timeval{timeout,0}); } diff --git a/pdns/tcpiohandler.cc b/pdns/tcpiohandler.cc index 2449e482cf..d4f7bc36e6 100644 --- a/pdns/tcpiohandler.cc +++ b/pdns/tcpiohandler.cc @@ -51,7 +51,7 @@ class OpenSSLTLSConnection: public TLSConnection { public: /* server side connection */ - OpenSSLTLSConnection(int socket, unsigned int timeout, std::shared_ptr feContext): d_feContext(feContext), d_conn(std::unique_ptr(SSL_new(d_feContext->d_tlsCtx.get()), SSL_free)), d_timeout(timeout) + OpenSSLTLSConnection(int socket, const struct timeval& timeout, std::shared_ptr feContext): d_feContext(feContext), d_conn(std::unique_ptr(SSL_new(d_feContext->d_tlsCtx.get()), SSL_free)), d_timeout(timeout) { d_socket = socket; @@ -79,7 +79,7 @@ public: } /* client-side connection */ - OpenSSLTLSConnection(const std::string& hostname, int socket, unsigned int timeout, SSL_CTX* tlsCtx): d_conn(std::unique_ptr(SSL_new(tlsCtx), SSL_free)), d_hostname(hostname), d_timeout(timeout) + OpenSSLTLSConnection(const std::string& hostname, int socket, const struct timeval& timeout, SSL_CTX* tlsCtx): d_conn(std::unique_ptr(SSL_new(tlsCtx), SSL_free)), d_hostname(hostname), d_timeout(timeout) { d_socket = socket; @@ -136,11 +136,11 @@ public: } } - void handleIORequest(int res, unsigned int timeout) + void handleIORequest(int res, const struct timeval& timeout) { auto state = convertIORequestToIOState(res); if (state == IOState::NeedRead) { - res = waitForData(d_socket, timeout); + res = waitForData(d_socket, timeout.tv_sec, timeout.tv_usec); if (res == 0) { throw std::runtime_error("Timeout while reading from TLS connection"); } @@ -149,7 +149,7 @@ public: } } else if (state == IOState::NeedWrite) { - res = waitForRWData(d_socket, false, timeout, 0); + res = waitForRWData(d_socket, false, timeout.tv_sec, timeout.tv_usec); if (res == 0) { throw std::runtime_error("Timeout while writing to TLS connection"); } @@ -176,16 +176,16 @@ public: throw std::runtime_error("Error establishing a TLS connection"); } - void connect(bool fastOpen, const ComboAddress& remote, unsigned int timeout) override + void connect(bool fastOpen, const ComboAddress& remote, const struct timeval &timeout) override { /* sorry */ (void) fastOpen; (void) remote; - time_t start = 0; - unsigned int remainingTime = timeout; - if (timeout) { - start = time(nullptr); + struct timeval start{0,0}; + struct timeval remainingTime = timeout; + if (timeout.tv_sec != 0 || timeout.tv_usec != 0) { + gettimeofday(&start, nullptr); } int res = 0; @@ -195,14 +195,15 @@ public: handleIORequest(res, remainingTime); } - if (timeout) { - time_t now = time(nullptr); - unsigned int elapsed = now - start; - if (now < start || elapsed >= remainingTime) { + if (timeout.tv_sec != 0 || timeout.tv_usec != 0) { + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval elapsed = now - start; + if (now < start || remainingTime < elapsed) { throw runtime_error("Timeout while establishing TLS connection"); } start = now; - remainingTime -= elapsed; + remainingTime = remainingTime - elapsed; } } while (res != 1); @@ -267,13 +268,13 @@ public: return IOState::Done; } - size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout) override + size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout) override { size_t got = 0; - time_t start = 0; - unsigned int remainingTime = totalTimeout; - if (totalTimeout) { - start = time(nullptr); + struct timeval start = {0, 0}; + struct timeval remainingTime = totalTimeout; + if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) { + gettimeofday(&start, nullptr); } do { @@ -285,14 +286,15 @@ public: got += static_cast(res); } - if (totalTimeout) { - time_t now = time(nullptr); - unsigned int elapsed = now - start; - if (now < start || elapsed >= remainingTime) { + if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) { + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval elapsed = now - start; + if (now < start || remainingTime < elapsed) { throw runtime_error("Timeout while reading data"); } start = now; - remainingTime -= elapsed; + remainingTime = remainingTime - elapsed; } } while (got < bufferSize); @@ -300,7 +302,7 @@ public: return got; } - size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) override + size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override { size_t got = 0; do { @@ -379,7 +381,7 @@ private: std::shared_ptr d_feContext; std::unique_ptr d_conn; std::string d_hostname; - unsigned int d_timeout; + struct timeval d_timeout; }; std::atomic_flag OpenSSLTLSConnection::s_initTLSConnIndex = ATOMIC_FLAG_INIT; @@ -525,14 +527,14 @@ public: return libssl_ocsp_stapling_callback(ssl, *ocspMap); } - std::unique_ptr getConnection(int socket, unsigned int timeout, time_t now) override + std::unique_ptr getConnection(int socket, const struct timeval& timeout, time_t now) override { handleTicketsKeyRotation(now); return std::make_unique(socket, timeout, d_feContext); } - std::unique_ptr getClientConnection(const std::string& host, int socket, unsigned int timeout) override + std::unique_ptr getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override { return std::make_unique(host, socket, timeout, d_tlsCtx.get()); } @@ -664,7 +666,7 @@ class GnuTLSConnection: public TLSConnection { public: /* server side connection */ - GnuTLSConnection(int socket, unsigned int timeout, const gnutls_certificate_credentials_t creds, const gnutls_priority_t priorityCache, std::shared_ptr& ticketsKey, bool enableTickets): d_conn(std::unique_ptr(nullptr, gnutls_deinit)), d_ticketsKey(ticketsKey) + GnuTLSConnection(int socket, const struct timeval& timeout, const gnutls_certificate_credentials_t creds, const gnutls_priority_t priorityCache, std::shared_ptr& ticketsKey, bool enableTickets): d_conn(std::unique_ptr(nullptr, gnutls_deinit)), d_ticketsKey(ticketsKey) { unsigned int sslOptions = GNUTLS_SERVER | GNUTLS_NONBLOCK; #ifdef GNUTLS_NO_SIGNAL @@ -699,12 +701,12 @@ public: gnutls_transport_set_int(d_conn.get(), d_socket); /* timeouts are in milliseconds */ - gnutls_handshake_set_timeout(d_conn.get(), timeout * 1000); - gnutls_record_set_timeout(d_conn.get(), timeout * 1000); + gnutls_handshake_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000); + gnutls_record_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000); } /* client-side connection */ - GnuTLSConnection(const std::string& host, int socket, unsigned int timeout, const gnutls_certificate_credentials_t creds, const gnutls_priority_t priorityCache, bool validateCerts): d_conn(std::unique_ptr(nullptr, gnutls_deinit)), d_host(host) + GnuTLSConnection(const std::string& host, int socket, const struct timeval& timeout, const gnutls_certificate_credentials_t creds, const gnutls_priority_t priorityCache, bool validateCerts): d_conn(std::unique_ptr(nullptr, gnutls_deinit)), d_host(host) { unsigned int sslOptions = GNUTLS_CLIENT | GNUTLS_NONBLOCK; #ifdef GNUTLS_NO_SIGNAL @@ -734,8 +736,8 @@ public: gnutls_transport_set_int(d_conn.get(), d_socket); /* timeouts are in milliseconds */ - gnutls_handshake_set_timeout(d_conn.get(), timeout * 1000); - gnutls_record_set_timeout(d_conn.get(), timeout * 1000); + gnutls_handshake_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000); + gnutls_record_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000); #if HAVE_GNUTLS_SESSION_SET_VERIFY_CERT if (validateCerts && !d_host.empty()) { @@ -777,12 +779,12 @@ public: throw std::runtime_error("Error establishing a new connection: " + std::string(gnutls_strerror(ret))); } - void connect(bool fastOpen, const ComboAddress& remote, unsigned int timeout) override + void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override { - time_t start = 0; - unsigned int remainingTime = timeout; - if (timeout) { - start = time(nullptr); + struct timeval start = {0, 0}; + struct timeval remainingTime = timeout; + if (timeout.tv_sec != 0 || timeout.tv_usec != 0) { + gettimeofday(&start, nullptr); } IOState state; @@ -792,26 +794,27 @@ public: return; } else if (state == IOState::NeedRead) { - int result = waitForData(d_socket, remainingTime); + int result = waitForData(d_socket, remainingTime.tv_sec, remainingTime.tv_usec); if (result <= 0) { throw std::runtime_error("Error reading from TLS connection: " + std::to_string(result)); } } else if (state == IOState::NeedWrite) { - int result = waitForRWData(d_socket, false, remainingTime, 0); + int result = waitForRWData(d_socket, false, remainingTime.tv_sec, remainingTime.tv_usec); if (result <= 0) { throw std::runtime_error("Error reading from TLS connection: " + std::to_string(result)); } } - if (timeout) { - time_t now = time(nullptr); - unsigned int elapsed = now - start; - if (now < start || elapsed >= remainingTime) { + if (timeout.tv_sec != 0 || timeout.tv_usec != 0) { + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval elapsed = now - start; + if (now < start || remainingTime < elapsed) { throw runtime_error("Timeout while establishing TLS connection"); } start = now; - remainingTime -= elapsed; + remainingTime = remainingTime - elapsed; } } while (state != IOState::Done); @@ -897,13 +900,13 @@ public: return IOState::Done; } - size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout) override + size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout) override { size_t got = 0; - time_t start = 0; - unsigned int remainingTime = totalTimeout; - if (totalTimeout) { - start = time(nullptr); + struct timeval start{0,0}; + struct timeval remainingTime = totalTimeout; + if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) { + gettimeofday(&start, nullptr); } do { @@ -919,7 +922,7 @@ public: throw std::runtime_error("Fatal error reading from TLS connection: " + std::string(gnutls_strerror(res))); } else if (res == GNUTLS_E_AGAIN) { - int result = waitForData(d_socket, readTimeout); + int result = waitForData(d_socket, readTimeout.tv_sec, readTimeout.tv_usec); if (result <= 0) { throw std::runtime_error("Error while waiting to read from TLS connection: " + std::to_string(result)); } @@ -929,14 +932,15 @@ public: } } - if (totalTimeout) { - time_t now = time(nullptr); - unsigned int elapsed = now - start; - if (now < start || elapsed >= remainingTime) { + if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) { + struct timeval now; + gettimeofday(&now, nullptr); + struct timeval elapsed = now - start; + if (now < start || remainingTime < elapsed) { throw runtime_error("Timeout while reading data"); } start = now; - remainingTime -= elapsed; + remainingTime = remainingTime - elapsed; } } while (got < bufferSize); @@ -944,7 +948,7 @@ public: return got; } - size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) override + size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override { size_t got = 0; @@ -961,7 +965,7 @@ public: throw std::runtime_error("Fatal error writing to TLS connection: " + std::string(gnutls_strerror(res))); } else if (res == GNUTLS_E_AGAIN) { - int result = waitForRWData(d_socket, false, writeTimeout, 0); + int result = waitForRWData(d_socket, false, writeTimeout.tv_sec, writeTimeout.tv_usec); if (result <= 0) { throw std::runtime_error("Error waiting to write to TLS connection: " + std::to_string(result)); } @@ -1145,7 +1149,7 @@ public: } } - std::unique_ptr getConnection(int socket, unsigned int timeout, time_t now) override + std::unique_ptr getConnection(int socket, const struct timeval& timeout, time_t now) override { handleTicketsKeyRotation(now); @@ -1158,7 +1162,7 @@ public: return std::make_unique(socket, timeout, d_creds.get(), d_priorityCache, ticketsKey, d_enableTickets); } - std::unique_ptr getClientConnection(const std::string& host, int socket, unsigned int timeout) override + std::unique_ptr getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override { return std::make_unique(host, socket, timeout, d_creds.get(), d_priorityCache, d_validateCerts); } diff --git a/pdns/tcpiohandler.hh b/pdns/tcpiohandler.hh index cfb7056bb1..33e09773f5 100644 --- a/pdns/tcpiohandler.hh +++ b/pdns/tcpiohandler.hh @@ -16,10 +16,10 @@ public: virtual ~TLSConnection() { } virtual void doHandshake() = 0; virtual IOState tryConnect(bool fastOpen, const ComboAddress& remote) = 0; - virtual void connect(bool fastOpen, const ComboAddress& remote, unsigned int timeout) = 0; + virtual void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) = 0; virtual IOState tryHandshake() = 0; - virtual size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout=0) = 0; - virtual size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) = 0; + virtual size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout={0,0}) = 0; + virtual size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) = 0; virtual IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) = 0; virtual IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead) = 0; virtual bool hasBufferedData() const = 0; @@ -62,8 +62,8 @@ public: d_rotatingTicketsKey.clear(); } virtual ~TLSCtx() {} - virtual std::unique_ptr getConnection(int socket, unsigned int timeout, time_t now) = 0; - virtual std::unique_ptr getClientConnection(const std::string& host, int socket, unsigned int timeout) = 0; + virtual std::unique_ptr getConnection(int socket, const struct timeval& timeout, time_t now) = 0; + virtual std::unique_ptr getClientConnection(const std::string& host, int socket, const struct timeval& timeout) = 0; virtual void rotateTicketsKey(time_t now) = 0; virtual void loadTicketsKeys(const std::string& file) { @@ -192,14 +192,14 @@ class TCPIOHandler public: enum class Type { Client, Server }; - TCPIOHandler(const std::string& host, int socket, unsigned int timeout, std::shared_ptr ctx, time_t now): d_socket(socket) + TCPIOHandler(const std::string& host, int socket, const struct timeval& timeout, std::shared_ptr ctx, time_t now): d_socket(socket) { if (ctx) { d_conn = ctx->getClientConnection(host, d_socket, timeout); } } - TCPIOHandler(int socket, unsigned int timeout, std::shared_ptr ctx, time_t now): d_socket(socket) + TCPIOHandler(int socket, const struct timeval& timeout, std::shared_ptr ctx, time_t now): d_socket(socket) { if (ctx) { d_conn = ctx->getConnection(d_socket, timeout, now); @@ -249,10 +249,10 @@ public: d_fastOpen = true; } else { - SConnectWithTimeout(d_socket, remote, /* no timeout, we will handle it ourselves */ 0); + SConnectWithTimeout(d_socket, remote, /* no timeout, we will handle it ourselves */ timeval{0,0}); } #else - SConnectWithTimeout(d_socket, remote, /* no timeout, we will handle it ourselves */ 0); + SConnectWithTimeout(d_socket, remote, /* no timeout, we will handle it ourselves */ timeval{0,0}); #endif /* MSG_FASTOPEN */ if (d_conn) { @@ -262,7 +262,7 @@ public: return IOState::Done; } - void connect(bool fastOpen, const ComboAddress& remote, unsigned int timeout) + void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) { d_remote = remote; @@ -300,7 +300,7 @@ public: return IOState::Done; } - size_t read(void* buffer, size_t bufferSize, unsigned int readTimeout, unsigned int totalTimeout=0) + size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout = {0,0}) { if (d_conn) { return d_conn->read(buffer, bufferSize, readTimeout, totalTimeout); @@ -400,7 +400,7 @@ public: return IOState::Done; } - size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) + size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) { if (d_conn) { return d_conn->write(buffer, bufferSize, writeTimeout); diff --git a/pdns/ws-recursor.cc b/pdns/ws-recursor.cc index 791d42c595..066460f32b 100644 --- a/pdns/ws-recursor.cc +++ b/pdns/ws-recursor.cc @@ -1250,7 +1250,7 @@ void AsyncWebServer::serveConnection(std::shared_ptr client) const { yarl.initialize(&req); client->setNonBlocking(); - const int timeout = (g_networkTimeoutMsec + 999) / 1000; // XXX tcpiohandler's unit is seconds + const struct timeval timeout{g_networkTimeoutMsec / 1000, g_networkTimeoutMsec % 1000 * 1000}; std::shared_ptr tlsCtx{nullptr}; auto handler = std::make_shared("", client->releaseHandle(), timeout, tlsCtx, time(nullptr));