summary refs log tree commit diff
diff options
context:
space:
mode:
authorSilke Hofstra <silke@slxh.eu>2017-09-06 17:48:49 +0200
committerSilke <silke@slxh.eu>2017-12-17 13:15:48 +0100
commited48ecc58cc1e26cacc5bf5a4b7d83c27ba27732 (patch)
treec9ad547a3bd213d8dd9d034ce9b3f2ca8b397cb4
parentAllow binds to both :: and 0.0.0.0 (diff)
downloadsynapse-ed48ecc58cc1e26cacc5bf5a4b7d83c27ba27732.tar.xz
Add methods for listening on multiple addresses
Add listen_tcp and listen_ssl which implement Twisted's reactor.listenTCP
and reactor.listenSSL for multiple addresses.

Signed-off-by: Silke Hofstra <silke@slxh.eu>
Diffstat (limited to '')
-rw-r--r--synapse/app/_base.py57
-rwxr-xr-xsynapse/app/homeserver.py84
2 files changed, 90 insertions, 51 deletions
diff --git a/synapse/app/_base.py b/synapse/app/_base.py
index 9477737759..0f9a57bd4c 100644
--- a/synapse/app/_base.py
+++ b/synapse/app/_base.py
@@ -25,7 +25,7 @@ except Exception:
 from daemonize import Daemonize
 from synapse.util import PreserveLoggingContext
 from synapse.util.rlimit import change_resource_limit
-from twisted.internet import reactor
+from twisted.internet import error, reactor
 
 
 def start_worker_reactor(appname, config):
@@ -120,3 +120,58 @@ def quit_with_error(error_string):
         sys.stderr.write(" %s\n" % (line.rstrip(),))
     sys.stderr.write("*" * line_length + '\n')
     sys.exit(1)
+
+
+def listen_tcp(logger, bind_addresses, port, factory, backlog=50):
+    """
+    Create a TCP socket for a port and several addresses
+    """
+    for address in bind_addresses:
+        try:
+            reactor.listenTCP(
+                port,
+                factory,
+                backlog,
+                address
+            )
+        except error.CannotListenError as e:
+            check_bind_error(logger, e, address, bind_addresses)
+
+
+def listen_ssl(logger, bind_addresses, port, factory, context_factory, backlog=50):
+    """
+    Create an SSL socket for a port and several addresses
+    """
+    for address in bind_addresses:
+        try:
+            reactor.listenSSL(
+                port,
+                factory,
+                context_factory,
+                backlog,
+                address
+            )
+        except error.CannotListenError as e:
+            check_bind_error(logger, e, address, bind_addresses)
+
+
+def check_bind_error(logger, e, address, bind_addresses):
+    """
+    This method checks an exception occurred while binding on 0.0.0.0.
+    If :: is specified in the bind addresses a warning is shown.
+    The exception is still raised otherwise.
+
+    Binding on both 0.0.0.0 and :: causes an exception on Linux and macOS
+    because :: binds on both IPv4 and IPv6 (as per RFC 3493).
+    When binding on 0.0.0.0 after :: this can safely be ignored.
+
+    Args:
+        logger (Logger): Logger used to log the warning.
+        e (Exception): Exception that was caught.
+        address (str): Address on which binding was attempted.
+        bind_addresses (list): Addresses on which the service listens.
+    """
+    if address == '0.0.0.0' and '::' in bind_addresses:
+        logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]')
+    else:
+        raise e
diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py
index 82a4e18c67..66f206a89d 100755
--- a/synapse/app/homeserver.py
+++ b/synapse/app/homeserver.py
@@ -131,37 +131,31 @@ class SynapseHomeServer(HomeServer):
         root_resource = create_resource_tree(resources, root_resource)
 
         if tls:
-            for address in bind_addresses:
-                try:
-                    reactor.listenSSL(
-                        port,
-                        SynapseSite(
-                            "synapse.access.https.%s" % (site_tag,),
-                            site_tag,
-                            listener_config,
-                            root_resource,
-                        ),
-                        self.tls_server_context_factory,
-                        interface=address
-                    )
-                except error.CannotListenError as e:
-                    check_bind_error(e, address, bind_addresses)
+            _base.listen_ssl(
+                logger,
+                bind_addresses,
+                port,
+                SynapseSite(
+                    "synapse.access.https.%s" % (site_tag,),
+                    site_tag,
+                    listener_config,
+                    root_resource,
+                ),
+                self.tls_server_context_factory,
+            )
 
         else:
-            for address in bind_addresses:
-                try:
-                    reactor.listenTCP(
-                        port,
-                        SynapseSite(
-                            "synapse.access.http.%s" % (site_tag,),
-                            site_tag,
-                            listener_config,
-                            root_resource,
-                        ),
-                        interface=address
-                    )
-                except error.CannotListenError as e:
-                    check_bind_error(e, address, bind_addresses)
+            _base.listen_tcp(
+                logger,
+                bind_addresses,
+                port,
+                SynapseSite(
+                    "synapse.access.http.%s" % (site_tag,),
+                    site_tag,
+                    listener_config,
+                    root_resource,
+                )
+            )
         logger.info("Synapse now listening on port %d", port)
 
     def _configure_named_resource(self, name, compress=False):
@@ -239,19 +233,16 @@ class SynapseHomeServer(HomeServer):
             elif listener["type"] == "manhole":
                 bind_addresses = listener["bind_addresses"]
 
-                for address in bind_addresses:
-                    try:
-                        reactor.listenTCP(
-                            listener["port"],
-                            manhole(
-                                username="matrix",
-                                password="rabbithole",
-                                globals={"hs": self},
-                            ),
-                            interface=address
-                        )
-                    except error.CannotListenError as e:
-                        check_bind_error(e, address, bind_addresses)
+                _base.listen_tcp(
+                    logger,
+                    bind_addresses,
+                    listener["port"],
+                    manhole(
+                        username="matrix",
+                        password="rabbithole",
+                        globals={"hs": self},
+                    )
+                )
             elif listener["type"] == "replication":
                 bind_addresses = listener["bind_addresses"]
                 for address in bind_addresses:
@@ -264,7 +255,7 @@ class SynapseHomeServer(HomeServer):
                             "before", "shutdown", server_listener.stopListening,
                         )
                     except error.CannotListenError as e:
-                        check_bind_error(e, address, bind_addresses)
+                        _base.check_bind_error(logger, e, address, bind_addresses)
             else:
                 logger.warn("Unrecognized listener type: %s", listener["type"])
 
@@ -298,13 +289,6 @@ class SynapseHomeServer(HomeServer):
         return db_conn
 
 
-def check_bind_error(e, address, bind_addresses):
-    if address == '0.0.0.0' and '::' in bind_addresses:
-        logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]')
-    else:
-        raise e
-
-
 def setup(config_options):
     """
     Args: