From: W.C.A. Wijngaards Date: Wed, 31 Aug 2022 09:54:11 +0000 (+0200) Subject: - Fix to wait for blocked write on UDP sockets, with a timeout if it X-Git-Tag: release-1.17.0rc1~34 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=ec5812a748bdade749409cba331c9f32cac9a15a;p=thirdparty%2Funbound.git - Fix to wait for blocked write on UDP sockets, with a timeout if it takes too long the packet is dropped. --- diff --git a/config.h.in b/config.h.in index cc1fbe864..2a5214803 100644 --- a/config.h.in +++ b/config.h.in @@ -457,6 +457,12 @@ /* 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 header file. */ +#undef HAVE_POLL_H + /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD diff --git a/configure b/configure index f40187910..5e489d4ba 100755 --- 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" diff --git a/configure.ac b/configure.ac index bf8aa9d8c..63902646e 100644 --- a/configure.ac +++ b/configure.ac @@ -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])]) diff --git a/doc/Changelog b/doc/Changelog index 10d16e6eb..212df9a88 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/util/netevent.c b/util/netevent.c index 6e2412f5a..cf9a38f61 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -60,6 +60,9 @@ #ifdef HAVE_NETDB_H #include #endif +#ifdef HAVE_POLL_H +#include +#endif #ifdef HAVE_OPENSSL_SSL_H #include @@ -107,6 +110,9 @@ #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); } }