]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix to wait for blocked write on UDP sockets, with a timeout if it
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 31 Aug 2022 09:54:11 +0000 (11:54 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 31 Aug 2022 09:54:11 +0000 (11:54 +0200)
  takes too long the packet is dropped.

config.h.in
configure
configure.ac
doc/Changelog
util/netevent.c

index cc1fbe864818e30e55bb41fe88b877fd24015949..2a5214803347d3248d7202ddf8b0859a337eeafe 100644 (file)
 /* Define to 1 if you have the `OSSL_PARAM_BLD_new' function. */
 #undef HAVE_OSSL_PARAM_BLD_NEW
 
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
 /* Define if you have POSIX threads libraries and header files. */
 #undef HAVE_PTHREAD
 
index f40187910eccb634d59029560230fb06a3d4e72f..5e489d4baed177444effa07003d0896dae071b52 100755 (executable)
--- a/configure
+++ b/configure
@@ -14772,7 +14772,7 @@ fi
 fi
 
 # Checks for header files.
-for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h
+for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h poll.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
@@ -20591,7 +20591,7 @@ if test "$ac_res" != no; then :
 
 fi
 
-for ac_func in tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex
+for ac_func in tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex poll
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
index bf8aa9d8cdb0b709ecacc14c713feece84deb4c8..63902646eb3b9c41657df633c69ca7eceb7dc2ed 100644 (file)
@@ -397,7 +397,7 @@ PKG_PROG_PKG_CONFIG
 fi
 
 # Checks for header files.
-AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h],,, [AC_INCLUDES_DEFAULT])
+AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h poll.h],,, [AC_INCLUDES_DEFAULT])
 # net/if.h portability for Darwin see:
 # https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Header-Portability.html
 AC_CHECK_HEADERS([net/if.h],,, [
@@ -1644,7 +1644,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([
   AC_MSG_RESULT(no))
 
 AC_SEARCH_LIBS([setusercontext], [util])
-AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex])
+AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex poll])
 AC_CHECK_FUNCS([setresuid],,[AC_CHECK_FUNCS([setreuid])])
 AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])
 
index 10d16e6ebec932595c18abfdc9059a389c251ea3..212df9a888b8219a7fb2c7e0af8716a041d4ee44 100644 (file)
@@ -4,6 +4,8 @@
        - Patch from Vadim Fedorenko that adds MSG_DONTWAIT to receive
          operations, so that instruction reordering does not cause mistakenly
          blocking socket operations.
+       - Fix to wait for blocked write on UDP sockets, with a timeout if it
+         takes too long the packet is dropped.
 
 22 August 2022: Wouter
        - Fix #741: systemd socket activation fails on IPv6.
index 6e2412f5a781da4523db8a7db68f6832a8baec39..cf9a38f61fb108679f6bdabf649532d3af8b0755 100644 (file)
@@ -60,6 +60,9 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
 
 #ifdef HAVE_OPENSSL_SSL_H
 #include <openssl/ssl.h>
 #define NUM_UDP_PER_SELECT 1
 #endif
 
+/** timeout in millisec to wait for write to unblock, packets dropped after.*/
+#define SEND_BLOCKED_WAIT_TIMEOUT 200
+
 /**
  * The internal event structure for keeping ub_event info for the event.
  * Possibly other structures (list, tree) this is part of.
@@ -369,13 +375,14 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
                 * we want to send the answer, and we will wait for
                 * the ethernet interface buffer to have space. */
 #ifndef USE_WINSOCK
-               if(errno == EAGAIN || 
+               if(errno == EAGAIN || errno == EINTR ||
 #  ifdef EWOULDBLOCK
                        errno == EWOULDBLOCK ||
 #  endif
                        errno == ENOBUFS) {
 #else
                if(WSAGetLastError() == WSAEINPROGRESS ||
+                       WSAGetLastError() == WSAEINTR ||
                        WSAGetLastError() == WSAENOBUFS ||
                        WSAGetLastError() == WSAEWOULDBLOCK) {
 #endif
@@ -383,17 +390,54 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
                         * have a blocking fd that they operate on */
                        while(
 #ifndef USE_WINSOCK
-                               errno == EAGAIN ||
+                               errno == EAGAIN || errno == EINTR ||
 #  ifdef EWOULDBLOCK
                                errno == EWOULDBLOCK ||
 #  endif
                                errno == ENOBUFS
 #else
                                WSAGetLastError() == WSAEINPROGRESS ||
+                               WSAGetLastError() == WSAEINTR ||
                                WSAGetLastError() == WSAENOBUFS ||
                                WSAGetLastError() == WSAEWOULDBLOCK
 #endif
                        ) {
+#if defined(HAVE_POLL) || defined(USE_WINSOCK)
+                               struct pollfd p;
+                               int pret;
+                               memset(&p, 0, sizeof(p));
+                               p.fd = c->fd;
+                               p.events = POLLOUT | POLLERR | POLLHUP;
+#  ifndef USE_WINSOCK
+                               pret = poll(&p, 1, SEND_BLOCKED_WAIT_TIMEOUT);
+#  else
+                               pret = WSAPoll(&p, 1,
+                                       SEND_BLOCKED_WAIT_TIMEOUT);
+#  endif
+                               if(pret == 0) {
+                                       /* timer expired */
+                                       verbose(VERB_OPS, "send udp blocked "
+                                               "for long, dropping packet.");
+                                       return 0;
+                               } else if(pret < 0 &&
+#ifndef USE_WINSOCK
+                                       errno != EAGAIN && errno != EINTR &&
+#  ifdef EWOULDBLOCK
+                                       errno != EWOULDBLOCK &&
+#  endif
+                                       errno != ENOBUFS
+#else
+                                       WSAGetLastError() != WSAEINPROGRESS &&
+                                       WSAGetLastError() != WSAEINTR &&
+                                       WSAGetLastError() != WSAENOBUFS &&
+                                       WSAGetLastError() != WSAEWOULDBLOCK
+#endif
+                                       ) {
+                                       log_err("poll udp out failed: %s",
+                                               sock_strerror(errno));
+                                       return 0;
+                               }
+#endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */
                                if (!is_connected) {
                                        sent = sendto(c->fd, (void*)sldns_buffer_begin(packet),
                                                sldns_buffer_remaining(packet), 0,
@@ -569,29 +613,67 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet,
                 * we want to send the answer, and we will wait for
                 * the ethernet interface buffer to have space. */
 #ifndef USE_WINSOCK
-               if(errno == EAGAIN || 
+               if(errno == EAGAIN || errno == EINTR ||
 #  ifdef EWOULDBLOCK
                        errno == EWOULDBLOCK ||
 #  endif
                        errno == ENOBUFS) {
 #else
                if(WSAGetLastError() == WSAEINPROGRESS ||
+                       WSAGetLastError() == WSAEINTR ||
                        WSAGetLastError() == WSAENOBUFS ||
                        WSAGetLastError() == WSAEWOULDBLOCK) {
 #endif
                        while(
 #ifndef USE_WINSOCK
-                               errno == EAGAIN ||
+                               errno == EAGAIN || errno == EINTR ||
 #  ifdef EWOULDBLOCK
                                errno == EWOULDBLOCK ||
 #  endif
                                errno == ENOBUFS
 #else
                                WSAGetLastError() == WSAEINPROGRESS ||
+                               WSAGetLastError() == WSAEINTR ||
                                WSAGetLastError() == WSAENOBUFS ||
                                WSAGetLastError() == WSAEWOULDBLOCK
 #endif
                        ) {
+#if defined(HAVE_POLL) || defined(USE_WINSOCK)
+                               struct pollfd p;
+                               int pret;
+                               memset(&p, 0, sizeof(p));
+                               p.fd = c->fd;
+                               p.events = POLLOUT | POLLERR | POLLHUP;
+#  ifndef USE_WINSOCK
+                               pret = poll(&p, 1, SEND_BLOCKED_WAIT_TIMEOUT);
+#  else
+                               pret = WSAPoll(&p, 1,
+                                       SEND_BLOCKED_WAIT_TIMEOUT);
+#  endif
+                               if(pret == 0) {
+                                       /* timer expired */
+                                       verbose(VERB_OPS, "send udp blocked "
+                                               "for long, dropping packet.");
+                                       return 0;
+                               } else if(pret < 0 &&
+#ifndef USE_WINSOCK
+                                       errno != EAGAIN && errno != EINTR &&
+#  ifdef EWOULDBLOCK
+                                       errno != EWOULDBLOCK &&
+#  endif
+                                       errno != ENOBUFS
+#else
+                                       WSAGetLastError() != WSAEINPROGRESS &&
+                                       WSAGetLastError() != WSAEINTR &&
+                                       WSAGetLastError() != WSAENOBUFS &&
+                                       WSAGetLastError() != WSAEWOULDBLOCK
+#endif
+                                       ) {
+                                       log_err("poll udp out failed: %s",
+                                               sock_strerror(errno));
+                                       return 0;
+                               }
+#endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */
                                sent = sendmsg(c->fd, &msg, 0);
                        }
                }