]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix #371: unbound-control timeout when Unbound is not running.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 11 Dec 2020 08:33:56 +0000 (09:33 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Fri, 11 Dec 2020 08:33:56 +0000 (09:33 +0100)
doc/Changelog
smallapp/unbound-control.c

index 3aaa19338d5e28648aeae3068634dc45feb6704e..af111d8a0a38e905311228891f00b3f3cc95f8cf 100644 (file)
@@ -1,3 +1,6 @@
+11 December 2020: Wouter
+       - Fix #371: unbound-control timeout when Unbound is not running.
+
 3 December 2020: Wouter
        - make depend.
        - iana portlist updated.
index 842dbe0d85b9bef50249c9cb0454779d49b5f308..93281736a12ce2c923e161857ad3833ae6b8559f 100644 (file)
@@ -82,6 +82,9 @@ static void usage(void) ATTR_NORETURN;
 static void ssl_err(const char* s) ATTR_NORETURN;
 static void ssl_path_err(const char* s, const char *path) ATTR_NORETURN;
 
+/** timeout to wait for connection over stream, in msec */
+#define UNBOUND_CONTROL_CONNECT_TIMEOUT 5000
+
 /** Give unbound-control usage, and exit (1). */
 static void
 usage(void)
@@ -545,6 +548,30 @@ setup_ctx(struct config_file* cfg)
        return ctx;
 }
 
+/** check connect error */
+static void
+checkconnecterr(int err, const char* svr, struct sockaddr_storage* addr,
+       socklen_t addrlen, int statuscmd, int useport)
+{
+#ifndef USE_WINSOCK
+       if(!useport) log_err("connect: %s for %s", strerror(err), svr);
+       else log_err_addr("connect", strerror(err), addr, addrlen);
+       if(err == ECONNREFUSED && statuscmd) {
+               printf("unbound is stopped\n");
+               exit(3);
+       }
+#else
+       int wsaerr = err;
+       if(!useport) log_err("connect: %s for %s", wsa_strerror(wsaerr), svr);
+       else log_err_addr("connect", wsa_strerror(wsaerr), addr, addrlen);
+       if(wsaerr == WSAECONNREFUSED && statuscmd) {
+               printf("unbound is stopped\n");
+               exit(3);
+       }
+#endif
+       exit(1);
+}
+
 /** contact the server with TCP connect */
 static int
 contact_server(const char* svr, struct config_file* cfg, int statuscmd)
@@ -598,26 +625,75 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
        if(fd == -1) {
                fatal_exit("socket: %s", sock_strerror(errno));
        }
+       fd_set_nonblock(fd);
        if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
 #ifndef USE_WINSOCK
-               int err = errno;
-               if(!useport) log_err("connect: %s for %s", strerror(err), svr);
-               else log_err_addr("connect", strerror(err), &addr, addrlen);
-               if(err == ECONNREFUSED && statuscmd) {
-                       printf("unbound is stopped\n");
-                       exit(3);
+#ifdef EINPROGRESS
+               if(errno != EINPROGRESS) {
+                       checkconnecterr(errno, svr, &addr,
+                               addrlen, statuscmd, useport);
                }
+#endif
 #else
-               int wsaerr = WSAGetLastError();
-               if(!useport) log_err("connect: %s for %s", wsa_strerror(wsaerr), svr);
-               else log_err_addr("connect", wsa_strerror(wsaerr), &addr, addrlen);
-               if(wsaerr == WSAECONNREFUSED && statuscmd) {
-                       printf("unbound is stopped\n");
-                       exit(3);
+               if(WSAGetLastError() != WSAEINPROGRESS &&
+                       WSAGetLastError() != WSAEWOULDBLOCK) {
+                       checkconnecterr(WSAGetLastError(), svr, &addr,
+                               addrlen, statuscmd, useport);
                }
 #endif
-               exit(1);
        }
+       while(1) {
+               fd_set rset, wset, eset;
+               struct timeval tv;
+               FD_ZERO(&rset);
+               FD_SET(FD_SET_T fd, &rset);
+               FD_ZERO(&wset);
+               FD_SET(FD_SET_T fd, &wset);
+               FD_ZERO(&eset);
+               FD_SET(FD_SET_T fd, &eset);
+               tv.tv_sec = UNBOUND_CONTROL_CONNECT_TIMEOUT/1000;
+               tv.tv_usec= (UNBOUND_CONTROL_CONNECT_TIMEOUT%1000)*1000;
+               if(select(fd+1, &rset, &wset, &eset, &tv) == -1) {
+                       fatal_exit("select: %s", sock_strerror(errno));
+               }
+               if(!FD_ISSET(fd, &rset) && !FD_ISSET(fd, &wset) &&
+                       !FD_ISSET(fd, &eset)) {
+                       fatal_exit("timeout: could not connect to server");
+               } else {
+                       /* check nonblocking connect error */
+                       int error = 0;
+                       socklen_t len = (socklen_t)sizeof(error);
+                       if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
+                               &len) < 0) {
+#ifndef USE_WINSOCK
+                               error = errno; /* on solaris errno is error */
+#else
+                               error = WSAGetLastError();
+#endif
+                       }
+                       if(error != 0) {
+#ifndef USE_WINSOCK
+#ifdef EINPROGRESS
+                               if(error == EINPROGRESS)
+                                       continue; /* try again later */
+#endif
+#ifdef EWOULDBLOCK
+                               if(error == EWOULDBLOCK)
+                                       continue; /* try again later */
+#endif
+#else
+                               if(error == WSAEINPROGRESS)
+                                       continue; /* try again later */
+                               if(error == WSAEWOULDBLOCK)
+                                       continue; /* try again later */
+#endif
+                               checkconnecterr(error, svr, &addr, addrlen,
+                                       statuscmd, useport);
+                       }
+               }
+               break;
+       }
+       fd_set_block(fd);
        return fd;
 }