]> git.meshlink.io Git - meshlink/commitdiff
Handle not being able to bind to the configured port at startup.
authorGuus Sliepen <guus@meshlink.io>
Fri, 6 Mar 2020 23:19:57 +0000 (00:19 +0100)
committerGuus Sliepen <guus@meshlink.io>
Fri, 6 Mar 2020 23:19:57 +0000 (00:19 +0100)
When starting a MeshLink node that has already been configured to run on a
certain port, but that port is in use (for one or more of the supported
address families), it would either ignore some address families, or would
try to bind to port 0 if all address families failed. However, this is
problematic, because it makes discovery and invitation URL generation much
harder.

Fix this by checking if any port binding fails for a supported address
family, and if so, try to find another port that does support binding on
all address families. If it fails to find any available port, it will fall
back to binding to port 0, so that outgoing connections are still possible.

src/meshlink.c
src/meshlink_internal.h
src/net.h
src/net_setup.c
src/net_socket.c

index 0090d54ddeadc49c9d6e16646a00af17ae07fd4c..9f9b771dc5589598df24ff18a7a06048199b161a 100644 (file)
@@ -639,7 +639,7 @@ static bool try_bind(int port) {
        return success;
 }
 
-static int check_port(meshlink_handle_t *mesh) {
+int check_port(meshlink_handle_t *mesh) {
        for(int i = 0; i < 1000; i++) {
                int port = 0x1000 + prng(mesh, 0x8000);
 
index 42df8b895cc90323e2391eabf52581746823ccd2..eaa89c5dbd59fb3aa3baf8f8c96db8e130b29e07 100644 (file)
@@ -55,7 +55,6 @@ typedef struct listen_socket_t {
        struct io_t udp;
        sockaddr_t sa;
        sockaddr_t broadcast_sa;
-       bool bindto;
 } listen_socket_t;
 
 typedef enum proxytype_t {
index 7cddebde22016982a6f7cd330e3afbcf2823c64b..63fb922f401f5e9936770c9bb1962ca977a08000 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -84,8 +84,8 @@ extern void handle_incoming_vpn_data(struct event_loop_t *loop, void *, int);
 extern void finish_connecting(struct meshlink_handle *mesh, struct connection_t *);
 extern void do_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *);
 extern void handle_new_meta_connection(struct event_loop_t *loop, void *, int);
-extern int setup_listen_socket(const sockaddr_t *) __attribute__((__warn_unused_result__));
-extern int setup_vpn_in_socket(struct meshlink_handle *mesh, const sockaddr_t *) __attribute__((__warn_unused_result__));
+extern bool setup_listen_socket(struct meshlink_handle *mesh, int fd, int sa_family) __attribute__((__warn_unused_result__));
+extern bool setup_vpn_in_socket(struct meshlink_handle *mesh, int fd, int sa_family) __attribute__((__warn_unused_result__));
 extern bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
 extern bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) __attribute__((__warn_unused_result__));
 extern void send_packet(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *);
@@ -106,6 +106,7 @@ extern bool node_write_config(struct meshlink_handle *mesh, struct node_t *) __a
 extern void send_mtu_probe(struct meshlink_handle *mesh, struct node_t *);
 extern void handle_meta_connection_data(struct meshlink_handle *mesh, struct connection_t *);
 extern void retry(struct meshlink_handle *mesh);
+extern int check_port(struct meshlink_handle *mesh);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
index 1b091db04034bd1a343390f508752d89de571b6a..b9cddeb00ccc2c7bd73dc69710ef7d148b63156c 100644 (file)
@@ -304,22 +304,7 @@ static bool load_node(meshlink_handle_t *mesh, const char *name, void *priv) {
 /*
   Add listening sockets.
 */
-static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bindto) {
-       char *port = mesh->myport;
-
-       if(address) {
-               char *space = strchr(address, ' ');
-
-               if(space) {
-                       *space++ = 0;
-                       port = space;
-               }
-
-               if(!strcmp(address, "*")) {
-                       *address = 0;
-               }
-       }
-
+static bool add_listen_sockets(meshlink_handle_t *mesh) {
        struct addrinfo *ai;
 
        struct addrinfo hint = {
@@ -329,9 +314,7 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                .ai_flags = AI_PASSIVE,
        };
 
-       int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
-
-       free(address);
+       int err = getaddrinfo(NULL, mesh->myport, &hint, &ai);
 
        if(err || !ai) {
                logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
@@ -344,11 +327,12 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                // Ignore duplicate addresses
                bool found = false;
 
-               for(int i = 0; i < mesh->listen_sockets; i++)
+               for(int i = 0; i < mesh->listen_sockets; i++) {
                        if(!memcmp(&mesh->listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
                                found = true;
                                break;
                        }
+               }
 
                if(found) {
                        continue;
@@ -359,17 +343,47 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                        return false;
                }
 
-               int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
+               /* Try to bind to TCP */
+
+               int tcp_fd = socket(aip->ai_family, SOCK_STREAM, IPPROTO_TCP);
 
-               if(tcp_fd < 0) {
+               if(tcp_fd == -1) {
                        continue;
                }
 
-               int udp_fd = setup_vpn_in_socket(mesh, (sockaddr_t *) aip->ai_addr);
+               if(bind(tcp_fd, aip->ai_addr, aip->ai_addrlen)) {
+                       closesocket(tcp_fd);
 
-               if(udp_fd < 0) {
-                       close(tcp_fd);
-                       continue;
+                       if(errno == EADDRINUSE) {
+                               /* If this port is in use for any address family, avoid it. */
+                               success = false;
+                               break;
+                       } else {
+                               continue;
+                       }
+               }
+
+               if(!setup_listen_socket(mesh, tcp_fd, aip->ai_family)) {
+                       closesocket(tcp_fd);
+                       success = false;
+                       break;
+               }
+
+               /* If TCP worked, then we require that UDP works as well. */
+
+               int udp_fd = socket(aip->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+
+               if(udp_fd == -1) {
+                       closesocket(tcp_fd);
+                       success = false;
+                       break;
+               }
+
+               if(bind(udp_fd, aip->ai_addr, aip->ai_addrlen) || !setup_vpn_in_socket(mesh, udp_fd, aip->ai_family)) {
+                       closesocket(tcp_fd);
+                       closesocket(udp_fd);
+                       success = false;
+                       break;
                }
 
                io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].tcp, handle_new_meta_connection, &mesh->listen_socket[mesh->listen_sockets], tcp_fd, IO_READ);
@@ -381,7 +395,6 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                        free(hostname);
                }
 
-               mesh->listen_socket[mesh->listen_sockets].bindto = bindto;
                memcpy(&mesh->listen_socket[mesh->listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
                memcpy(&mesh->listen_socket[mesh->listen_sockets].broadcast_sa, aip->ai_addr, aip->ai_addrlen);
 
@@ -398,6 +411,18 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
        }
 
        freeaddrinfo(ai);
+
+       if(!success) {
+               for(int i = 0; i < mesh->listen_sockets; i++) {
+                       io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
+                       io_del(&mesh->loop, &mesh->listen_socket[i].udp);
+                       close(mesh->listen_socket[i].tcp.fd);
+                       close(mesh->listen_socket[i].udp.fd);
+               }
+
+               mesh->listen_sockets = 0;
+       }
+
        return success;
 }
 
@@ -423,17 +448,17 @@ bool setup_myself(meshlink_handle_t *mesh) {
 
        mesh->listen_sockets = 0;
 
-       if(!add_listen_address(mesh, NULL, NULL)) {
+       if(!add_listen_sockets(mesh)) {
                if(strcmp(mesh->myport, "0")) {
-                       logger(mesh, MESHLINK_INFO, "Could not bind to port %s, asking OS to choose one for us", mesh->myport);
-                       free(mesh->myport);
-                       mesh->myport = strdup("0");
+                       logger(mesh, MESHLINK_WARNING, "Could not bind to port %s, trying to find an alternative port", mesh->myport);
 
-                       if(!mesh->myport) {
-                               return false;
+                       if(!check_port(mesh)) {
+                               logger(mesh, MESHLINK_WARNING, "Could not bind to any port, trying to bind to port 0");
+                               free(mesh->myport);
+                               mesh->myport = xstrdup("0");
                        }
 
-                       if(!add_listen_address(mesh, NULL, NULL)) {
+                       if(!add_listen_sockets(mesh)) {
                                return false;
                        }
                } else {
index 7e05ab9362b4ecd9d6d81b268a25ff75d8f25f93..931f21d111ee9625047d6e35737656611d41df32 100644 (file)
@@ -72,65 +72,17 @@ static void configure_tcp(connection_t *c) {
 #endif
 }
 
-static bool bind_to_address(meshlink_handle_t *mesh, connection_t *c) {
-       int s = -1;
-
-       for(int i = 0; i < mesh->listen_sockets && mesh->listen_socket[i].bindto; i++) {
-               if(mesh->listen_socket[i].sa.sa.sa_family != c->address.sa.sa_family) {
-                       continue;
-               }
-
-               if(s >= 0) {
-                       return false;
-               }
-
-               s = i;
-       }
-
-       if(s < 0) {
-               return false;
-       }
-
-       sockaddr_t sa = mesh->listen_socket[s].sa;
-
-       if(sa.sa.sa_family == AF_INET) {
-               sa.in.sin_port = 0;
-       } else if(sa.sa.sa_family == AF_INET6) {
-               sa.in6.sin6_port = 0;
-       }
-
-       if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
-               logger(mesh, MESHLINK_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
-               return false;
-       }
-
-       return true;
-}
-
-int setup_listen_socket(const sockaddr_t *sa) {
-       int nfd;
-       char *addrstr;
-       int option;
-
-       nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-
-       if(nfd < 0) {
-               logger(NULL, MESHLINK_ERROR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
-               return -1;
-       }
-
+bool setup_listen_socket(meshlink_handle_t *mesh, int nfd, int sa_family) {
 #ifdef FD_CLOEXEC
        fcntl(nfd, F_SETFD, FD_CLOEXEC);
 #endif
 
-       /* Optimize TCP settings */
-
-       option = 1;
+       int option = 1;
        setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
 
 #if defined(IPV6_V6ONLY)
 
-       if(sa->sa.sa_family == AF_INET6) {
+       if(sa_family == AF_INET6) {
                setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
        }
 
@@ -138,35 +90,15 @@ int setup_listen_socket(const sockaddr_t *sa) {
 #warning IPV6_V6ONLY not defined
 #endif
 
-       if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
-               closesocket(nfd);
-               addrstr = sockaddr2hostname(sa);
-               logger(NULL, MESHLINK_ERROR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
-               free(addrstr);
-               return -1;
-       }
-
        if(listen(nfd, 3)) {
-               closesocket(nfd);
-               logger(NULL, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
-               return -1;
+               logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
+               return false;
        }
 
-       return nfd;
+       return true;
 }
 
-int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
-       int nfd;
-       char *addrstr;
-       int option;
-
-       nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
-
-       if(nfd < 0) {
-               logger(mesh, MESHLINK_ERROR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
-               return -1;
-       }
-
+bool setup_vpn_in_socket(meshlink_handle_t *mesh, int nfd, int sa_family) {
 #ifdef FD_CLOEXEC
        fcntl(nfd, F_SETFD, FD_CLOEXEC);
 #endif
@@ -179,7 +111,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
                        closesocket(nfd);
                        logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl",
                               strerror(errno));
-                       return -1;
+                       return false;
                }
        }
 #elif defined(WIN32)
@@ -189,18 +121,18 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
                if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
                        closesocket(nfd);
                        logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
-                       return -1;
+                       return false;
                }
        }
 #endif
 
-       option = 1;
+       int option = 1;
        setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
        setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof(option));
 
 #if defined(IPV6_V6ONLY)
 
-       if(sa->sa.sa_family == AF_INET6) {
+       if(sa_family == AF_INET6) {
                setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
        }
 
@@ -226,15 +158,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
        setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
 #endif
 
-       if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
-               closesocket(nfd);
-               addrstr = sockaddr2hostname(sa);
-               logger(mesh, MESHLINK_ERROR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
-               free(addrstr);
-               return -1;
-       }
-
-       return nfd;
+       return true;
 } /* int setup_vpn_in_socket */
 
 static void retry_outgoing_handler(event_loop_t *loop, void *data) {
@@ -562,8 +486,6 @@ begin:
 
 #endif
 
-       bind_to_address(mesh, c);
-
        /* Connect */
 
        if(!mesh->proxytype) {