]> 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)
committerEvan Hunt <each@isc.org>
Sun, 8 Nov 2020 21:36:12 +0000 (13:36 -0800)
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.

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

index f4ff2186b1d3f3f32e66974979f58d31b8bc7e9c..053ad7fc639f524622b3d370eb4f08cdaa8b6a18 100644 (file)
@@ -950,6 +950,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 a5981c217eef41c6b049ead12bbf7ebe8871c7d4..ffadb431824335d238d5f2df0ec8bead4837fb61 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 05e490a6f27bcc22cdbd49ae0541d5be1d9195e0..9f8a37340e8c76fecef924e24fa22b1c78c38632 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(sa_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 ||
@@ -772,6 +772,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);
@@ -779,6 +780,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);
 
@@ -788,11 +798,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);