]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
netmgr: Don't crash if socket() returns an error in udpconnect
authorOndřej Surý <ondrej@sury.org>
Sat, 7 Nov 2020 19:48:37 +0000 (20:48 +0100)
committerOndřej Surý <ondrej@sury.org>
Wed, 9 Dec 2020 09:46:16 +0000 (10:46 +0100)
socket() call can return an error - e.g. EMFILE, so we need to handle
this nicely and not crash.

Additionally wrap the socket() call inside a platform independent helper
function as the Socket data type on Windows is unsigned integer:

> This means, for example, that checking for errors when the socket and
> accept functions return should not be done by comparing the return
> value with –1, or seeing if the value is negative (both common and
> legal approaches in UNIX). Instead, an application should use the
> manifest constant INVALID_SOCKET as defined in the Winsock2.h header
> file.

(cherry picked from commit 8af7f81d6c1f974ac3305186ae34b9ce423525d0)

lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/udp.c

index d7bdecc633b83827a7d2769dc49d46f2aae6ea53..8efe84f30d41037bffe6a36f35d188bfdd39a6c5 100644 (file)
@@ -952,6 +952,12 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid);
  * Decrement socket-related statistics counters.
  */
 
+isc_result_t
+isc__nm_socket(int domain, int type, int protocol, uv_os_sock_t *sockp);
+/*%<
+ * Platform independent socket() version
+ */
+
 isc_result_t
 isc__nm_socket_freebind(uv_os_sock_t fd, sa_family_t sa_family);
 /*%<
index 42aeb12e32357fcd7dddfdf011412c9ecfa85f5b..5d9cb9992ef9fcf5fa419c08b5d7a54463183f24 100644 (file)
@@ -16,6 +16,7 @@
 #include <isc/atomic.h>
 #include <isc/buffer.h>
 #include <isc/condition.h>
+#include <isc/errno.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
 #include <isc/netmgr.h>
@@ -27,6 +28,7 @@
 #include <isc/result.h>
 #include <isc/sockaddr.h>
 #include <isc/stats.h>
+#include <isc/strerr.h>
 #include <isc/thread.h>
 #include <isc/util.h>
 
@@ -1722,6 +1724,40 @@ isc__nm_decstats(isc_nm_t *mgr, isc_statscounter_t counterid) {
        }
 }
 
+isc_result_t
+isc__nm_socket(int domain, int type, int protocol, uv_os_sock_t *sockp) {
+#ifdef WIN32
+       SOCKET sock;
+       sock = socket(domain, type, protocol);
+       if (sock == INVALID_SOCKET) {
+               char strbuf[ISC_STRERRORSIZE];
+               DWORD socket_errno = WSAGetLastError();
+               switch (socket_errno) {
+               case WSAEMFILE:
+               case WSAENOBUFS:
+                       return (ISC_R_NORESOURCES);
+
+               case WSAEPROTONOSUPPORT:
+               case WSAEPFNOSUPPORT:
+               case WSAEAFNOSUPPORT:
+                       return (ISC_R_FAMILYNOSUPPORT);
+               default:
+                       strerror_r(socket_errno, strbuf, sizeof(strbuf));
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "socket() failed: %s", strbuf);
+                       return (ISC_R_UNEXPECTED);
+               }
+       }
+#else
+       int sock = socket(domain, type, protocol);
+       if (sock < 0) {
+               return (isc_errno_toresult(errno));
+       }
+#endif
+       *sockp = (uv_os_sock_t)sock;
+       return (ISC_R_SUCCESS);
+}
+
 #define setsockopt_on(socket, level, name) \
        setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
 
index 2c3b61965f538b7e43d3aaf1c7ddc5bf55584141..0d085124f79d69839bc160a6287d89ad893a3cec 100644 (file)
@@ -93,8 +93,8 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
                INSIST(csock->recv_cb == NULL && csock->recv_cbarg == NULL);
                csock->recv_cb = cb;
                csock->recv_cbarg = cbarg;
-               csock->fd = socket(family, SOCK_DGRAM, 0);
-               RUNTIME_CHECK(csock->fd >= 0);
+               result = isc__nm_socket(sa_family, SOCK_DGRAM, 0, &csock->fd);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
                result = isc__nm_socket_reuse(csock->fd);
                RUNTIME_CHECK(result == ISC_R_SUCCESS ||
@@ -770,6 +770,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
        isc__netievent_udpconnect_t *event = NULL;
        isc__nm_uvreq_t *req = NULL;
        sa_family_t sa_family;
+       uv_os_sock_t fd;
 
        REQUIRE(VALID_NM(mgr));
        REQUIRE(local != NULL);
@@ -777,6 +778,15 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
 
        sa_family = peer->addr.type.sa.sa_family;
 
+       /*
+        * The socket() call can fail spuriously on FreeBSD 12, so we need to
+        * handle the failure early and gracefully.
+        */
+       result = isc__nm_socket(sa_family, SOCK_DGRAM, 0, &fd);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
+
        sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
        isc__nmsocket_init(sock, mgr, isc_nm_udpsocket, local);
 
@@ -786,11 +796,9 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
        sock->read_timeout = timeout;
        sock->extrahandlesize = extrahandlesize;
        sock->peer = peer->addr;
+       sock->fd = fd;
        atomic_init(&sock->client, true);
 
-       sock->fd = socket(sa_family, SOCK_DGRAM, 0);
-       RUNTIME_CHECK(sock->fd >= 0);
-
        result = isc__nm_socket_reuse(sock->fd);
        RUNTIME_CHECK(result == ISC_R_SUCCESS ||
                      result == ISC_R_NOTIMPLEMENTED);