]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Convert timeout values to be specified as a timeval, so sub-second timeout
authorOtto <otto.moerbeek@open-xchange.com>
Tue, 25 May 2021 10:05:30 +0000 (12:05 +0200)
committerOtto <otto.moerbeek@open-xchange.com>
Fri, 18 Jun 2021 06:47:42 +0000 (08:47 +0200)
values can be handled correctly.

Also make sure sdig uses a NB socket, to handle timeouts correctly.

19 files changed:
pdns/auth-carbon.cc
pdns/dnsdist-tcp.cc
pdns/dnsdistdist/dnsdist-tcp-downstream.cc
pdns/dnsdistdist/dnsdist-tcp-upstream.hh
pdns/dnsdistdist/test-dnsdisttcp_cc.cc
pdns/dnsproxy.cc
pdns/dolog.hh
pdns/iputils.cc
pdns/iputils.hh
pdns/ixfrdist.cc
pdns/lwres.cc
pdns/misc.cc
pdns/misc.hh
pdns/rec-carbon.cc
pdns/sdig.cc
pdns/sstuff.hh
pdns/tcpiohandler.cc
pdns/tcpiohandler.hh
pdns/ws-recursor.cc

index 8fa3e1596a1ed28cbcd20459b7a55c3c45424f4d..41767bc7bfd4139f906c18efac5c81901f49a811 100644 (file)
@@ -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<<Logger::Warning<<"Unable to write data to carbon server at "<<remote.toStringWithPort()<<": "<<e.what()<<endl;
         continue;
index aa27cb2a51d130febcc2f0fb3b84bd448a4116d9..76d7122dce2d450a09154906bb2b00f4970232fb 100644 (file)
@@ -1290,7 +1290,7 @@ void tcpAcceptorThread(ClientState* cs)
         auto tmp = ci.release();
         try {
           // throws on failure
-          writen2WithTimeout(pipe, &tmp, sizeof(tmp), 0);
+          writen2WithTimeout(pipe, &tmp, sizeof(tmp), timeval{0,0});
         }
         catch (...) {
           delete tmp;
index a43ca1d37ce980735b3e0e33fed76a287f9afd84..88e02c0004815b44459f10aa65d64a6dba7c5b51 100644 (file)
@@ -346,7 +346,7 @@ bool TCPConnectionToBackend::reconnect()
       socket->setNonBlocking();
 
       gettimeofday(&d_connectionStartTime, nullptr);
-      auto handler = std::make_unique<TCPIOHandler>("", socket->releaseHandle(), 0, d_ds->d_tlsCtx, time(nullptr));
+      auto handler = std::make_unique<TCPIOHandler>("", socket->releaseHandle(), timeval{0,0}, d_ds->d_tlsCtx, time(nullptr));
       handler->tryConnect(d_ds->tcpFastOpen && isFastOpenEnabled(), d_ds->remote);
       d_queries = 0;
 
index ae971b070eab796b13c5cfd0d558bef65bcaaafd..4e49421400c9632f6c76d3a6f9e84506b1d536a4 100644 (file)
@@ -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<IOStateHandler>(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<IOStateHandler>(threadData.mplexer, d_ci.fd)), d_connectionStartTime(now)
   {
     d_origDest.reset();
     d_origDest.sin4.sin_family = d_ci.remote.sin4.sin_family;
index 78737a7ca21f586fa92198dc020fbbe2e4aa68f6..55ec5b454bc9181b77f147ebd864ca37a7e75b17 100644 (file)
@@ -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<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) override
+  std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) override
   {
     return std::make_unique<MockupTLSConnection>(socket);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, unsigned int timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
   {
     return std::make_unique<MockupTLSConnection>(socket, true);
   }
index be12827a7ec784ce2081aaae45bc73bdad4bab8f..db07c2a6bbd0139d1f31d1b3187a37550ec1ba90 100644 (file)
@@ -123,7 +123,7 @@ bool DNSProxy::completePacket(std::unique_ptr<DNSPacket>& 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;
   }
index 62e1c6a3de597dc6b5ca12963421290f5e7a0c1e..557c767aadeecb1933c978324e974c854efb469f 100644 (file)
@@ -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<typename T, typename... Args>
 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;
       }
     }
index 216945b7cc73f5fba94c2d6d57db5d96c3f24114..c5aa2a87b23582de904025cea58b2fa58c0a4b5f 100644 (file)
@@ -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<const struct sockaddr*>(&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;
index 92055f8c2cc6b1256b76311fa45912a916da55f3..32073637e1a1a798558624578106ce334cc3a5f6 100644 (file)
@@ -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);
index 6774ed362492993042986785713bafe9c885897e..f7d58eb30fd299cd48a44475716e5b953cccfedf 100644 (file)
@@ -878,7 +878,7 @@ static void tcpWorker(int tid) {
       uint16_t toRead;
       readn2(cfd, &toRead, sizeof(toRead));
       toRead = std::min(ntohs(toRead), static_cast<uint16_t>(sizeof(buf)));
-      res = readn2WithTimeout(cfd, &buf, toRead, 2);
+      res = readn2WithTimeout(cfd, &buf, toRead, timeval{2,0});
       g_log<<Logger::Debug<<prefix<<"Had message of "<<std::to_string(toRead)<<" bytes from "<<saddr.toStringWithPort()<<endl;
     } catch (runtime_error &e) {
       g_log<<Logger::Warning<<prefix<<"Could not read message from "<<saddr.toStringWithPort()<<": "<<e.what()<<endl;
index 5b9fd2d7b5633488db3915c164ff97c55c516b0c..0317544cdba78cdeb213da773b1a2dc00868caa8 100644 (file)
@@ -341,7 +341,7 @@ LWResult::Result asyncresolve(const ComboAddress& ip, const DNSName& domain, int
   }
   else {
     try {
-      const int timeout = (g_networkTimeoutMsec + 999) / 1000; // XXX tcpiohandler's unit is seconds
+      const struct timeval timeout{ g_networkTimeoutMsec / 1000, g_networkTimeoutMsec % 1000 * 1000};
 
       Socket s(ip.sin4.sin_family, SOCK_STREAM);
       s.setNonBlocking();
index 899e32ed6de5ab17619bedc6d53d613be639b388..0948803af925873bd8fde81fdb0b08984f62c1b9 100644 (file)
@@ -110,13 +110,13 @@ size_t readn2(int fd, void* buffer, size_t len)
   return len;
 }
 
-size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int totalTimeout)
+size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout)
 {
   size_t pos = 0;
-  time_t start = 0;
-  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 {
@@ -129,7 +129,8 @@ size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int
     }
     else {
       if (errno == EAGAIN) {
-        int res = waitForData(fd, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime);
+        struct timeval w = ((totalTimeout.tv_sec == 0 && totalTimeout.tv_usec == 0) || idleTimeout <= remainingTime) ? idleTimeout : remainingTime;
+        int res = waitForData(fd, w.tv_sec, w.tv_usec);
         if (res > 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 */
         }
index 9b19924292df17328315244306a7745db915bf34..0372e8ffae41b38aed6789426bd15803818b9c6b 100644 (file)
@@ -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)
index 663846eb944aaca3973afa4284a619a4123952bb..9b55e7b6bfeeafc344520a1da85db5a33dee82b9 100644 (file)
@@ -51,7 +51,7 @@ try
     Socket s(remote.sin4.sin_family, SOCK_STREAM);
     s.setNonBlocking();
     std::shared_ptr<TLSCtx> 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<TCPIOHandler>("", 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
 
index 1d25fba414d7c83ef215bba712fd6e4c4a72f7c9..12eb822bb3c86044b8ceeee2992a8aa586e1088f 100644 (file)
@@ -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)
index c25304652186e38ea0b8e414de0ebc0c0f4112ac..5787f41fb7431438317fb63ae12f12a78151fce6 100644 (file)
@@ -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});
   }
 
 
index 2449e482cf951384787b36c7704f0e7a3d27d533..d4f7bc36e62f594e9c184e37880dd80edeb5ddce 100644 (file)
@@ -51,7 +51,7 @@ class OpenSSLTLSConnection: public TLSConnection
 {
 public:
   /* server side connection */
-  OpenSSLTLSConnection(int socket, unsigned int timeout, std::shared_ptr<OpenSSLFrontendContext> feContext): d_feContext(feContext), d_conn(std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(d_feContext->d_tlsCtx.get()), SSL_free)), d_timeout(timeout)
+  OpenSSLTLSConnection(int socket, const struct timeval& timeout, std::shared_ptr<OpenSSLFrontendContext> feContext): d_feContext(feContext), d_conn(std::unique_ptr<SSL, void(*)(SSL*)>(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, void(*)(SSL*)>(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, void(*)(SSL*)>(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<size_t>(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<OpenSSLFrontendContext> d_feContext;
   std::unique_ptr<SSL, void(*)(SSL*)> 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<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) override
+  std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) override
   {
     handleTicketsKeyRotation(now);
 
     return std::make_unique<OpenSSLTLSConnection>(socket, timeout, d_feContext);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, unsigned int timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
   {
     return std::make_unique<OpenSSLTLSConnection>(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<GnuTLSTicketsKey>& ticketsKey, bool enableTickets): d_conn(std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(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<GnuTLSTicketsKey>& ticketsKey, bool enableTickets): d_conn(std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(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<gnutls_session_int, void(*)(gnutls_session_t)>(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<gnutls_session_int, void(*)(gnutls_session_t)>(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<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) override
+  std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) override
   {
     handleTicketsKeyRotation(now);
 
@@ -1158,7 +1162,7 @@ public:
     return std::make_unique<GnuTLSConnection>(socket, timeout, d_creds.get(), d_priorityCache, ticketsKey, d_enableTickets);
   }
 
-  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, unsigned int timeout) override
+  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, const struct timeval& timeout) override
   {
     return std::make_unique<GnuTLSConnection>(host, socket, timeout, d_creds.get(), d_priorityCache, d_validateCerts);
   }
index cfb7056bb1e26e94736bd9d042c9941deb46f7cb..33e09773f533363ddc341a0cdab2974363c5dc48 100644 (file)
@@ -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<TLSConnection> getConnection(int socket, unsigned int timeout, time_t now) = 0;
-  virtual std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, int socket, unsigned int timeout) = 0;
+  virtual std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) = 0;
+  virtual std::unique_ptr<TLSConnection> 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<TLSCtx> ctx, time_t now): d_socket(socket)
+  TCPIOHandler(const std::string& host, int socket, const struct timeval& timeout, std::shared_ptr<TLSCtx> 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<TLSCtx> ctx, time_t now): d_socket(socket)
+  TCPIOHandler(int socket, const struct timeval& timeout, std::shared_ptr<TLSCtx> 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);
index 791d42c595f0dfcb25dd45c00d187e3511796914..066460f32bb0780637f4d137e05062d956839f20 100644 (file)
@@ -1250,7 +1250,7 @@ void AsyncWebServer::serveConnection(std::shared_ptr<Socket> 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> tlsCtx{nullptr};
     auto handler = std::make_shared<TCPIOHandler>("", client->releaseHandle(), timeout, tlsCtx, time(nullptr));