[tor-commits] [tor/master] Try rebinding new listener after closing old one if first bind failed with EADDRINUSE

nickm at torproject.org nickm at torproject.org
Wed Sep 12 13:06:41 UTC 2018


commit c99bb8b6ea2b533e8ed99e6a41d74fc3bbeddb94
Author: rl1987 <rl1987 at sdf.lonestar.org>
Date:   Wed Apr 25 16:17:11 2018 +0200

    Try rebinding new listener after closing old one if first bind failed with EADDRINUSE
---
 src/core/mainloop/connection.c | 106 +++++++++++++++++++++++++++++++++++------
 1 file changed, 92 insertions(+), 14 deletions(-)

diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index bb4130640..22fa3fb79 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -137,7 +137,11 @@ static connection_t *connection_listener_new(
                                const struct sockaddr *listensockaddr,
                                socklen_t listensocklen, int type,
                                const char *address,
-                               const port_cfg_t *portcfg);
+                               const port_cfg_t *portcfg,
+                               int *addr_in_use);
+static connection_t *connection_listener_new_for_port(
+                               const port_cfg_t *port,
+                               int *defer, int *addr_in_use);
 static void connection_init(time_t now, connection_t *conn, int type,
                             int socket_family);
 static int connection_handle_listener_read(connection_t *conn, int new_type);
@@ -1188,7 +1192,8 @@ static connection_t *
 connection_listener_new(const struct sockaddr *listensockaddr,
                         socklen_t socklen,
                         int type, const char *address,
-                        const port_cfg_t *port_cfg)
+                        const port_cfg_t *port_cfg,
+                        int *addr_in_use)
 {
   listener_connection_t *lis_conn;
   connection_t *conn = NULL;
@@ -1204,6 +1209,8 @@ connection_listener_new(const struct sockaddr *listensockaddr,
   tor_addr_t addr;
   int exhaustion = 0;
 
+  if (addr_in_use) *addr_in_use = 0;
+
   if (listensockaddr->sa_family == AF_INET ||
       listensockaddr->sa_family == AF_INET6) {
     int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER);
@@ -1282,8 +1289,10 @@ connection_listener_new(const struct sockaddr *listensockaddr,
     if (bind(s,listensockaddr,socklen) < 0) {
       const char *helpfulhint = "";
       int e = tor_socket_errno(s);
-      if (ERRNO_IS_EADDRINUSE(e))
+      if (ERRNO_IS_EADDRINUSE(e)) {
         helpfulhint = ". Is Tor already running?";
+        if (addr_in_use) *addr_in_use = 1;
+      }
       log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort,
                tor_socket_strerror(e), helpfulhint);
       goto err;
@@ -1487,8 +1496,15 @@ connection_listener_new(const struct sockaddr *listensockaddr,
   return NULL;
 }
 
+/**
+ * Create a new listener connection for a given <b>port</b>. In case we
+ * for a reason that is not an error condition, set <b>defer</b>
+ * to true. If we cannot bind listening socket because address is already
+ * in use, set <b>addr_in_use</b> to true.
+ */
 static connection_t *
-connection_listener_new_for_port(const port_cfg_t *port, int *defer)
+connection_listener_new_for_port(const port_cfg_t *port,
+                                 int *defer, int *addr_in_use)
 {
   connection_t *conn;
   struct sockaddr *listensockaddr;
@@ -1531,7 +1547,8 @@ connection_listener_new_for_port(const port_cfg_t *port, int *defer)
 
   if (listensockaddr) {
     conn = connection_listener_new(listensockaddr, listensocklen,
-                                   port->type, address, port);
+                                   port->type, address, port,
+                                   addr_in_use);
     tor_free(listensockaddr);
     tor_free(address);
   } else {
@@ -2651,16 +2668,26 @@ connection_read_proxy_handshake(connection_t *conn)
   return ret;
 }
 
+struct replacement_s
+{
+  connection_t *old_conn;
+  port_cfg_t *new_port;
+};
+
 /** Given a list of listener connections in <b>old_conns</b>, and list of
  * port_cfg_t entries in <b>ports</b>, open a new listener for every port in
  * <b>ports</b> that does not already have a listener in <b>old_conns</b>.
  *
  * Remove from <b>old_conns</b> every connection that has a corresponding
  * entry in <b>ports</b>.  Add to <b>new_conns</b> new every connection we
- * launch.
+ * launch. If we may need to perform socket rebind when creating new
+ * listener that replaces old one, create a <b>replacement_s</b> struct
+ * for affected pair  and add it to <b>replacements</b>. For more
+ * information, see ticket #17873.
  *
  * If <b>control_listeners_only</b> is true, then we only open control
- * listeners, and we do not remove any noncontrol listeners from old_conns.
+ * listeners, and we do not remove any noncontrol listeners from
+ * old_conns.
  *
  * Return 0 on success, -1 on failure.
  **/
@@ -2668,8 +2695,10 @@ static int
 retry_listener_ports(smartlist_t *old_conns,
                      const smartlist_t *ports,
                      smartlist_t *new_conns,
+                     smartlist_t *replacements,
                      int control_listeners_only)
 {
+
   smartlist_t *launch = smartlist_new();
   int r = 0;
 
@@ -2705,16 +2734,31 @@ retry_listener_ports(smartlist_t *old_conns,
           break;
         }
       } else {
-        int port_matches;
-        if (wanted->port == CFG_AUTO_PORT) {
-          port_matches = 1;
-        } else {
-          port_matches = (wanted->port == conn->port);
-        }
+        /* Numeric values of old and new port match exactly. */
+        const int port_matches_exact = (wanted->port == conn->port);
+        /* Ports match semantically - either their specific values
+           match exactly, or new port is 'auto'.
+         */
+        const int port_matches = (wanted->port == CFG_AUTO_PORT ||
+                                  port_matches_exact);
+
         if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) {
           found_port = wanted;
           break;
         }
+        const int may_need_rebind =
+          port_matches_exact && bool_neq(tor_addr_is_null(&wanted->addr),
+                                         tor_addr_is_null(&conn->addr));
+        if (replacements && may_need_rebind) {
+          struct replacement_s *replacement =
+            tor_malloc(sizeof(struct replacement_s));
+
+          replacement->old_conn = conn;
+          replacement->new_port = (port_cfg_t *)wanted;
+          smartlist_add(replacements, replacement);
+
+          SMARTLIST_DEL_CURRENT(launch, wanted);
+        }
       }
     } SMARTLIST_FOREACH_END(wanted);
 
@@ -2731,7 +2775,7 @@ retry_listener_ports(smartlist_t *old_conns,
   /* Now open all the listeners that are configured but not opened. */
   SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) {
     int skip = 0;
-    connection_t *conn = connection_listener_new_for_port(port, &skip);
+    connection_t *conn = connection_listener_new_for_port(port, &skip, NULL);
 
     if (conn && new_conns)
       smartlist_add(new_conns, conn);
@@ -2759,6 +2803,7 @@ retry_all_listeners(smartlist_t *replaced_conns,
                     smartlist_t *new_conns, int close_all_noncontrol)
 {
   smartlist_t *listeners = smartlist_new();
+  smartlist_t *replacements = smartlist_new();
   const or_options_t *options = get_options();
   int retval = 0;
   const uint16_t old_or_port = router_get_advertised_or_port(options);
@@ -2774,9 +2819,42 @@ retry_all_listeners(smartlist_t *replaced_conns,
   if (retry_listener_ports(listeners,
                            get_configured_ports(),
                            new_conns,
+                           replacements,
                            close_all_noncontrol) < 0)
     retval = -1;
 
+  SMARTLIST_FOREACH_BEGIN(replacements, struct replacement_s *, r) {
+    int addr_in_use = 0;
+    int skip = 0;
+
+    tor_assert(r->new_port);
+    tor_assert(r->old_conn);
+
+    connection_t *new_conn =
+      connection_listener_new_for_port(r->new_port, &skip, &addr_in_use);
+    connection_t *old_conn = r->old_conn;
+
+
+    if (skip)
+      continue;
+
+    // XXX: replaced_conns
+    connection_close_immediate(r->old_conn);
+    connection_mark_for_close(r->old_conn);
+
+    if (addr_in_use) {
+      new_conn = connection_listener_new_for_port(r->new_port,
+                                                  &skip, &addr_in_use);
+    }
+
+    tor_assert(new_conn);
+
+    smartlist_add(new_conns, new_conn);
+
+    tor_free(r);
+    SMARTLIST_DEL_CURRENT(replacements, r);
+  } SMARTLIST_FOREACH_END(r);
+
   /* Any members that were still in 'listeners' don't correspond to
    * any configured port.  Kill 'em. */
   SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) {





More information about the tor-commits mailing list