diff --git a/README.rst b/README.rst
index 8bff55e78e..05a3bb3751 100644
--- a/README.rst
+++ b/README.rst
@@ -220,7 +220,7 @@ is configured to use TLS with a self-signed certificate. If you would like
to do initial test with a client without having to setup a reverse proxy,
you can temporarly use another certificate. (Note that a self-signed
certificate is fine for `Federation`_). You can do so by changing
-``tls_certificate_path``, ``tls_private_key_path`` and ``tls_dh_params_path``
+``tls_certificate_path`` and ``tls_private_key_path``
in ``homeserver.yaml``; alternatively, you can use a reverse-proxy, but be sure
to read `Using a reverse proxy with Synapse`_ when doing so.
diff --git a/changelog.d/4229.feature b/changelog.d/4229.feature
new file mode 100644
index 0000000000..0d1996c7e8
--- /dev/null
+++ b/changelog.d/4229.feature
@@ -0,0 +1 @@
+Synapse's cipher string has been updated to require ECDH key exchange. Configuring and generating dh_params is no longer required, and they will be ignored.
diff --git a/changelog.d/4423.feature b/changelog.d/4423.feature
new file mode 100644
index 0000000000..74aeab6d39
--- /dev/null
+++ b/changelog.d/4423.feature
@@ -0,0 +1 @@
+Config option to disable requesting MSISDN on registration.
diff --git a/changelog.d/4426.misc b/changelog.d/4426.misc
new file mode 100644
index 0000000000..cda50438e0
--- /dev/null
+++ b/changelog.d/4426.misc
@@ -0,0 +1 @@
+Remove redundant SynapseKeyClientProtocol magic
\ No newline at end of file
diff --git a/changelog.d/4427.misc b/changelog.d/4427.misc
new file mode 100644
index 0000000000..75500bdbc2
--- /dev/null
+++ b/changelog.d/4427.misc
@@ -0,0 +1 @@
+Refactor and cleanup for SRV record lookup
diff --git a/debian/homeserver.yaml b/debian/homeserver.yaml
index 188a2d5483..0bb2d22a95 100644
--- a/debian/homeserver.yaml
+++ b/debian/homeserver.yaml
@@ -9,9 +9,6 @@ tls_certificate_path: "/etc/matrix-synapse/homeserver.tls.crt"
# PEM encoded private key for TLS
tls_private_key_path: "/etc/matrix-synapse/homeserver.tls.key"
-# PEM dh parameters for ephemeral keys
-tls_dh_params_path: "/etc/matrix-synapse/homeserver.tls.dh"
-
# Don't bind to the https port
no_tls: False
diff --git a/demo/demo.tls.dh b/demo/demo.tls.dh
deleted file mode 100644
index cbc58272a0..0000000000
--- a/demo/demo.tls.dh
+++ /dev/null
@@ -1,9 +0,0 @@
-2048-bit DH parameters taken from rfc3526
------BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb
-IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft
-awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT
-mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh
-fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq
-5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==
------END DH PARAMETERS-----
diff --git a/docker/conf/homeserver.yaml b/docker/conf/homeserver.yaml
index c2b8576a32..529118d184 100644
--- a/docker/conf/homeserver.yaml
+++ b/docker/conf/homeserver.yaml
@@ -4,7 +4,6 @@
tls_certificate_path: "/data/{{ SYNAPSE_SERVER_NAME }}.tls.crt"
tls_private_key_path: "/data/{{ SYNAPSE_SERVER_NAME }}.tls.key"
-tls_dh_params_path: "/data/{{ SYNAPSE_SERVER_NAME }}.tls.dh"
no_tls: {{ "True" if SYNAPSE_NO_TLS else "False" }}
tls_fingerprints: []
diff --git a/synapse/config/registration.py b/synapse/config/registration.py
index 6c2b543b8c..fe520d6855 100644
--- a/synapse/config/registration.py
+++ b/synapse/config/registration.py
@@ -50,6 +50,10 @@ class RegistrationConfig(Config):
raise ConfigError('Invalid auto_join_rooms entry %s' % (room_alias,))
self.autocreate_auto_join_rooms = config.get("autocreate_auto_join_rooms", True)
+ self.disable_msisdn_registration = (
+ config.get("disable_msisdn_registration", False)
+ )
+
def default_config(self, generate_secrets=False, **kwargs):
if generate_secrets:
registration_shared_secret = 'registration_shared_secret: "%s"' % (
@@ -70,6 +74,11 @@ class RegistrationConfig(Config):
# - email
# - msisdn
+ # Explicitly disable asking for MSISDNs from the registration
+ # flow (overrides registrations_require_3pid if MSISDNs are set as required)
+ #
+ # disable_msisdn_registration = True
+
# Mandate that users are only allowed to associate certain formats of
# 3PIDs with accounts on this server.
#
diff --git a/synapse/config/tls.py b/synapse/config/tls.py
index fef1ea99cb..bb8952c672 100644
--- a/synapse/config/tls.py
+++ b/synapse/config/tls.py
@@ -14,7 +14,6 @@
# limitations under the License.
import os
-import subprocess
from hashlib import sha256
from unpaddedbase64 import encode_base64
@@ -23,8 +22,6 @@ from OpenSSL import crypto
from ._base import Config
-GENERATE_DH_PARAMS = False
-
class TlsConfig(Config):
def read_config(self, config):
@@ -42,10 +39,6 @@ class TlsConfig(Config):
config.get("tls_private_key_path")
)
- self.tls_dh_params_path = self.check_file(
- config.get("tls_dh_params_path"), "tls_dh_params"
- )
-
self.tls_fingerprints = config["tls_fingerprints"]
# Check that our own certificate is included in the list of fingerprints
@@ -72,7 +65,6 @@ class TlsConfig(Config):
tls_certificate_path = base_key_name + ".tls.crt"
tls_private_key_path = base_key_name + ".tls.key"
- tls_dh_params_path = base_key_name + ".tls.dh"
return """\
# PEM encoded X509 certificate for TLS.
@@ -85,9 +77,6 @@ class TlsConfig(Config):
# PEM encoded private key for TLS
tls_private_key_path: "%(tls_private_key_path)s"
- # PEM dh parameters for ephemeral keys
- tls_dh_params_path: "%(tls_dh_params_path)s"
-
# Don't bind to the https port
no_tls: False
@@ -131,7 +120,6 @@ class TlsConfig(Config):
def generate_files(self, config):
tls_certificate_path = config["tls_certificate_path"]
tls_private_key_path = config["tls_private_key_path"]
- tls_dh_params_path = config["tls_dh_params_path"]
if not self.path_exists(tls_private_key_path):
with open(tls_private_key_path, "wb") as private_key_file:
@@ -165,31 +153,3 @@ class TlsConfig(Config):
cert_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
certificate_file.write(cert_pem)
-
- if not self.path_exists(tls_dh_params_path):
- if GENERATE_DH_PARAMS:
- subprocess.check_call([
- "openssl", "dhparam",
- "-outform", "PEM",
- "-out", tls_dh_params_path,
- "2048"
- ])
- else:
- with open(tls_dh_params_path, "w") as dh_params_file:
- dh_params_file.write(
- "2048-bit DH parameters taken from rfc3526\n"
- "-----BEGIN DH PARAMETERS-----\n"
- "MIIBCAKCAQEA///////////JD9qiIWjC"
- "NMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n"
- "IlFKCHmONATd75UZs806QxswKwpt8l8U"
- "N0/hNW1tUcJF5IW1dmJefsb0TELppjft\n"
- "awv/XLb0Brft7jhr+1qJn6WunyQRfEsf"
- "5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n"
- "mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVS"
- "u57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n"
- "fDKQXkYuNs474553LBgOhgObJ4Oi7Aei"
- "j7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n"
- "5RXSJhiY+gUQFXKOWoqsqmj/////////"
- "/wIBAg==\n"
- "-----END DH PARAMETERS-----\n"
- )
diff --git a/synapse/crypto/context_factory.py b/synapse/crypto/context_factory.py
index 02b76dfcfb..6ba3eca7b2 100644
--- a/synapse/crypto/context_factory.py
+++ b/synapse/crypto/context_factory.py
@@ -46,8 +46,10 @@ class ServerContextFactory(ContextFactory):
if not config.no_tls:
context.use_privatekey(config.tls_private_key)
- context.load_tmp_dh(config.tls_dh_params_path)
- context.set_cipher_list("!ADH:HIGH+kEDH:!AECDH:HIGH+kEECDH")
+ # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
+ context.set_cipher_list(
+ "ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1"
+ )
def getContext(self):
return self._context
diff --git a/synapse/crypto/keyclient.py b/synapse/crypto/keyclient.py
deleted file mode 100644
index d40e4b8591..0000000000
--- a/synapse/crypto/keyclient.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import logging
-
-from six.moves import urllib
-
-from canonicaljson import json
-
-from twisted.internet import defer, reactor
-from twisted.internet.error import ConnectError
-from twisted.internet.protocol import Factory
-from twisted.names.error import DomainError
-from twisted.web.http import HTTPClient
-
-from synapse.http.endpoint import matrix_federation_endpoint
-from synapse.util import logcontext
-
-logger = logging.getLogger(__name__)
-
-KEY_API_V2 = "/_matrix/key/v2/server/%s"
-
-
-@defer.inlineCallbacks
-def fetch_server_key(server_name, tls_client_options_factory, key_id):
- """Fetch the keys for a remote server."""
-
- factory = SynapseKeyClientFactory()
- factory.path = KEY_API_V2 % (urllib.parse.quote(key_id), )
- factory.host = server_name
- endpoint = matrix_federation_endpoint(
- reactor, server_name, tls_client_options_factory, timeout=30
- )
-
- for i in range(5):
- try:
- with logcontext.PreserveLoggingContext():
- protocol = yield endpoint.connect(factory)
- server_response, server_certificate = yield protocol.remote_key
- defer.returnValue((server_response, server_certificate))
- except SynapseKeyClientError as e:
- logger.warn("Error getting key for %r: %s", server_name, e)
- if e.status.startswith(b"4"):
- # Don't retry for 4xx responses.
- raise IOError("Cannot get key for %r" % server_name)
- except (ConnectError, DomainError) as e:
- logger.warn("Error getting key for %r: %s", server_name, e)
- except Exception:
- logger.exception("Error getting key for %r", server_name)
- raise IOError("Cannot get key for %r" % server_name)
-
-
-class SynapseKeyClientError(Exception):
- """The key wasn't retrieved from the remote server."""
- status = None
- pass
-
-
-class SynapseKeyClientProtocol(HTTPClient):
- """Low level HTTPS client which retrieves an application/json response from
- the server and extracts the X.509 certificate for the remote peer from the
- SSL connection."""
-
- timeout = 30
-
- def __init__(self):
- self.remote_key = defer.Deferred()
- self.host = None
- self._peer = None
-
- def connectionMade(self):
- self._peer = self.transport.getPeer()
- logger.debug("Connected to %s", self._peer)
-
- if not isinstance(self.path, bytes):
- self.path = self.path.encode('ascii')
-
- if not isinstance(self.host, bytes):
- self.host = self.host.encode('ascii')
-
- self.sendCommand(b"GET", self.path)
- if self.host:
- self.sendHeader(b"Host", self.host)
- self.endHeaders()
- self.timer = reactor.callLater(
- self.timeout,
- self.on_timeout
- )
-
- def errback(self, error):
- if not self.remote_key.called:
- self.remote_key.errback(error)
-
- def callback(self, result):
- if not self.remote_key.called:
- self.remote_key.callback(result)
-
- def handleStatus(self, version, status, message):
- if status != b"200":
- # logger.info("Non-200 response from %s: %s %s",
- # self.transport.getHost(), status, message)
- error = SynapseKeyClientError(
- "Non-200 response %r from %r" % (status, self.host)
- )
- error.status = status
- self.errback(error)
- self.transport.abortConnection()
-
- def handleResponse(self, response_body_bytes):
- try:
- json_response = json.loads(response_body_bytes)
- except ValueError:
- # logger.info("Invalid JSON response from %s",
- # self.transport.getHost())
- self.transport.abortConnection()
- return
-
- certificate = self.transport.getPeerCertificate()
- self.callback((json_response, certificate))
- self.transport.abortConnection()
- self.timer.cancel()
-
- def on_timeout(self):
- logger.debug(
- "Timeout waiting for response from %s: %s",
- self.host, self._peer,
- )
- self.errback(IOError("Timeout waiting for response"))
- self.transport.abortConnection()
-
-
-class SynapseKeyClientFactory(Factory):
- def protocol(self):
- protocol = SynapseKeyClientProtocol()
- protocol.path = self.path
- protocol.host = self.host
- return protocol
diff --git a/synapse/crypto/keyring.py b/synapse/crypto/keyring.py
index 515ebbc148..3a96980bed 100644
--- a/synapse/crypto/keyring.py
+++ b/synapse/crypto/keyring.py
@@ -14,10 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import hashlib
import logging
from collections import namedtuple
+from six.moves import urllib
+
from signedjson.key import (
decode_verify_key_bytes,
encode_verify_key_base64,
@@ -30,13 +31,11 @@ from signedjson.sign import (
signature_ids,
verify_signed_json,
)
-from unpaddedbase64 import decode_base64, encode_base64
+from unpaddedbase64 import decode_base64
-from OpenSSL import crypto
from twisted.internet import defer
from synapse.api.errors import Codes, SynapseError
-from synapse.crypto.keyclient import fetch_server_key
from synapse.util import logcontext, unwrapFirstError
from synapse.util.logcontext import (
LoggingContext,
@@ -503,31 +502,16 @@ class Keyring(object):
if requested_key_id in keys:
continue
- (response, tls_certificate) = yield fetch_server_key(
- server_name, self.hs.tls_client_options_factory, requested_key_id
+ response = yield self.client.get_json(
+ destination=server_name,
+ path="/_matrix/key/v2/server/" + urllib.parse.quote(requested_key_id),
+ ignore_backoff=True,
)
if (u"signatures" not in response
or server_name not in response[u"signatures"]):
raise KeyLookupError("Key response not signed by remote server")
- if "tls_fingerprints" not in response:
- raise KeyLookupError("Key response missing TLS fingerprints")
-
- certificate_bytes = crypto.dump_certificate(
- crypto.FILETYPE_ASN1, tls_certificate
- )
- sha256_fingerprint = hashlib.sha256(certificate_bytes).digest()
- sha256_fingerprint_b64 = encode_base64(sha256_fingerprint)
-
- response_sha256_fingerprints = set()
- for fingerprint in response[u"tls_fingerprints"]:
- if u"sha256" in fingerprint:
- response_sha256_fingerprints.add(fingerprint[u"sha256"])
-
- if sha256_fingerprint_b64 not in response_sha256_fingerprints:
- raise KeyLookupError("TLS certificate not allowed by fingerprints")
-
response_keys = yield self.process_v2_response(
from_server=server_name,
requested_ids=[requested_key_id],
diff --git a/synapse/http/endpoint.py b/synapse/http/endpoint.py
index 1c3b7ea28a..815f8ff2f7 100644
--- a/synapse/http/endpoint.py
+++ b/synapse/http/endpoint.py
@@ -12,29 +12,17 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-import collections
import logging
import random
import re
-import time
from twisted.internet import defer
from twisted.internet.endpoints import HostnameEndpoint, wrapClientTLS
from twisted.internet.error import ConnectError
-from twisted.names import client, dns
-from twisted.names.error import DNSNameError, DomainError
-logger = logging.getLogger(__name__)
-
-SERVER_CACHE = {}
+from synapse.http.federation.srv_resolver import Server, resolve_service
-# our record of an individual server which can be tried to reach a destination.
-#
-# "host" is the hostname acquired from the SRV record. Except when there's
-# no SRV record, in which case it is the original hostname.
-_Server = collections.namedtuple(
- "_Server", "priority weight host port expires"
-)
+logger = logging.getLogger(__name__)
def parse_server_name(server_name):
@@ -165,12 +153,9 @@ class SRVClientEndpoint(object):
self.service_name = "_%s._%s.%s" % (service, protocol, domain)
if default_port is not None:
- self.default_server = _Server(
+ self.default_server = Server(
host=domain,
port=default_port,
- priority=0,
- weight=0,
- expires=0,
)
else:
self.default_server = None
@@ -240,57 +225,3 @@ class SRVClientEndpoint(object):
)
connection = yield endpoint.connect(protocolFactory)
defer.returnValue(connection)
-
-
-@defer.inlineCallbacks
-def resolve_service(service_name, dns_client=client, cache=SERVER_CACHE, clock=time):
- cache_entry = cache.get(service_name, None)
- if cache_entry:
- if all(s.expires > int(clock.time()) for s in cache_entry):
- servers = list(cache_entry)
- defer.returnValue(servers)
-
- servers = []
-
- try:
- try:
- answers, _, _ = yield dns_client.lookupService(service_name)
- except DNSNameError:
- defer.returnValue([])
-
- if (len(answers) == 1
- and answers[0].type == dns.SRV
- and answers[0].payload
- and answers[0].payload.target == dns.Name(b'.')):
- raise ConnectError("Service %s unavailable" % service_name)
-
- for answer in answers:
- if answer.type != dns.SRV or not answer.payload:
- continue
-
- payload = answer.payload
-
- servers.append(_Server(
- host=str(payload.target),
- port=int(payload.port),
- priority=int(payload.priority),
- weight=int(payload.weight),
- expires=int(clock.time()) + answer.ttl,
- ))
-
- servers.sort()
- cache[service_name] = list(servers)
- except DomainError as e:
- # We failed to resolve the name (other than a NameError)
- # Try something in the cache, else rereaise
- cache_entry = cache.get(service_name, None)
- if cache_entry:
- logger.warn(
- "Failed to resolve %r, falling back to cache. %r",
- service_name, e
- )
- servers = list(cache_entry)
- else:
- raise e
-
- defer.returnValue(servers)
diff --git a/synapse/http/federation/__init__.py b/synapse/http/federation/__init__.py
new file mode 100644
index 0000000000..1453d04571
--- /dev/null
+++ b/synapse/http/federation/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/synapse/http/federation/srv_resolver.py b/synapse/http/federation/srv_resolver.py
new file mode 100644
index 0000000000..c49b82c394
--- /dev/null
+++ b/synapse/http/federation/srv_resolver.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# Copyright 2014-2016 OpenMarket Ltd
+# Copyright 2019 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+
+import attr
+
+from twisted.internet import defer
+from twisted.internet.error import ConnectError
+from twisted.names import client, dns
+from twisted.names.error import DNSNameError, DomainError
+
+from synapse.util.logcontext import make_deferred_yieldable
+
+logger = logging.getLogger(__name__)
+
+SERVER_CACHE = {}
+
+
+@attr.s
+class Server(object):
+ """
+ Our record of an individual server which can be tried to reach a destination.
+
+ Attributes:
+ host (bytes): target hostname
+ port (int):
+ priority (int):
+ weight (int):
+ expires (int): when the cache should expire this record - in *seconds* since
+ the epoch
+ """
+ host = attr.ib()
+ port = attr.ib()
+ priority = attr.ib(default=0)
+ weight = attr.ib(default=0)
+ expires = attr.ib(default=0)
+
+
+@defer.inlineCallbacks
+def resolve_service(service_name, dns_client=client, cache=SERVER_CACHE, clock=time):
+ """Look up a SRV record, with caching
+
+ The default resolver in twisted.names doesn't do any caching (it has a CacheResolver,
+ but the cache never gets populated), so we add our own caching layer here.
+
+ Args:
+ service_name (unicode|bytes): record to look up
+ dns_client (twisted.internet.interfaces.IResolver): twisted resolver impl
+ cache (dict): cache object
+ clock (object): clock implementation. must provide a time() method.
+
+ Returns:
+ Deferred[list[Server]]: a list of the SRV records, or an empty list if none found
+ """
+ # TODO: the dns client handles both unicode names (encoding via idna) and pre-encoded
+ # byteses; however they will obviously end up as separate entries in the cache. We
+ # should pick one form and stick with it.
+ cache_entry = cache.get(service_name, None)
+ if cache_entry:
+ if all(s.expires > int(clock.time()) for s in cache_entry):
+ servers = list(cache_entry)
+ defer.returnValue(servers)
+
+ try:
+ answers, _, _ = yield make_deferred_yieldable(
+ dns_client.lookupService(service_name),
+ )
+ except DNSNameError:
+ # TODO: cache this. We can get the SOA out of the exception, and use
+ # the negative-TTL value.
+ defer.returnValue([])
+ except DomainError as e:
+ # We failed to resolve the name (other than a NameError)
+ # Try something in the cache, else rereaise
+ cache_entry = cache.get(service_name, None)
+ if cache_entry:
+ logger.warn(
+ "Failed to resolve %r, falling back to cache. %r",
+ service_name, e
+ )
+ defer.returnValue(list(cache_entry))
+ else:
+ raise e
+
+ if (len(answers) == 1
+ and answers[0].type == dns.SRV
+ and answers[0].payload
+ and answers[0].payload.target == dns.Name(b'.')):
+ raise ConnectError("Service %s unavailable" % service_name)
+
+ servers = []
+
+ for answer in answers:
+ if answer.type != dns.SRV or not answer.payload:
+ continue
+
+ payload = answer.payload
+
+ servers.append(Server(
+ host=payload.target.name,
+ port=payload.port,
+ priority=payload.priority,
+ weight=payload.weight,
+ expires=int(clock.time()) + answer.ttl,
+ ))
+
+ servers.sort() # FIXME: get rid of this (it's broken by the attrs change)
+ cache[service_name] = list(servers)
+ defer.returnValue(servers)
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index aec0c6b075..14025cd219 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -309,22 +309,16 @@ class RegisterRestServlet(RestServlet):
assigned_user_id=registered_user_id,
)
- # Only give msisdn flows if the x_show_msisdn flag is given:
- # this is a hack to work around the fact that clients were shipped
- # that use fallback registration if they see any flows that they don't
- # recognise, which means we break registration for these clients if we
- # advertise msisdn flows. Once usage of Riot iOS <=0.3.9 and Riot
- # Android <=0.6.9 have fallen below an acceptable threshold, this
- # parameter should go away and we should always advertise msisdn flows.
- show_msisdn = False
- if 'x_show_msisdn' in body and body['x_show_msisdn']:
- show_msisdn = True
-
# FIXME: need a better error than "no auth flow found" for scenarios
# where we required 3PID for registration but the user didn't give one
require_email = 'email' in self.hs.config.registrations_require_3pid
require_msisdn = 'msisdn' in self.hs.config.registrations_require_3pid
+ show_msisdn = True
+ if self.hs.config.disable_msisdn_registration:
+ show_msisdn = False
+ require_msisdn = False
+
flows = []
if self.hs.config.enable_registration_captcha:
# only support 3PIDless registration if no 3PIDs are required
diff --git a/tests/config/test_generate.py b/tests/config/test_generate.py
index 0c23068bcf..b5ad99348d 100644
--- a/tests/config/test_generate.py
+++ b/tests/config/test_generate.py
@@ -51,7 +51,6 @@ class ConfigGenerationTestCase(unittest.TestCase):
"lemurs.win.log.config",
"lemurs.win.signing.key",
"lemurs.win.tls.crt",
- "lemurs.win.tls.dh",
"lemurs.win.tls.key",
]
),
diff --git a/tests/http/federation/__init__.py b/tests/http/federation/__init__.py
new file mode 100644
index 0000000000..1453d04571
--- /dev/null
+++ b/tests/http/federation/__init__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tests/test_dns.py b/tests/http/federation/test_srv_resolver.py
index 90bd34be34..1271a495e1 100644
--- a/tests/test_dns.py
+++ b/tests/http/federation/test_srv_resolver.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2016 OpenMarket Ltd
+# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,40 +17,63 @@
from mock import Mock
from twisted.internet import defer
+from twisted.internet.defer import Deferred
+from twisted.internet.error import ConnectError
from twisted.names import dns, error
-from synapse.http.endpoint import resolve_service
+from synapse.http.federation.srv_resolver import resolve_service
+from synapse.util.logcontext import LoggingContext
+from tests import unittest
from tests.utils import MockClock
-from . import unittest
-
-@unittest.DEBUG
-class DnsTestCase(unittest.TestCase):
- @defer.inlineCallbacks
+class SrvResolverTestCase(unittest.TestCase):
def test_resolve(self):
dns_client_mock = Mock()
- service_name = "test_service.example.com"
- host_name = "example.com"
+ service_name = b"test_service.example.com"
+ host_name = b"example.com"
answer_srv = dns.RRHeader(
type=dns.SRV, payload=dns.Record_SRV(target=host_name)
)
- dns_client_mock.lookupService.return_value = defer.succeed(
- ([answer_srv], None, None)
- )
+ result_deferred = Deferred()
+ dns_client_mock.lookupService.return_value = result_deferred
cache = {}
- servers = yield resolve_service(
- service_name, dns_client=dns_client_mock, cache=cache
- )
+ @defer.inlineCallbacks
+ def do_lookup():
+ with LoggingContext("one") as ctx:
+ resolve_d = resolve_service(
+ service_name, dns_client=dns_client_mock, cache=cache
+ )
+
+ self.assertNoResult(resolve_d)
+
+ # should have reset to the sentinel context
+ self.assertIs(LoggingContext.current_context(), LoggingContext.sentinel)
+
+ result = yield resolve_d
+
+ # should have restored our context
+ self.assertIs(LoggingContext.current_context(), ctx)
+
+ defer.returnValue(result)
+
+ test_d = do_lookup()
+ self.assertNoResult(test_d)
dns_client_mock.lookupService.assert_called_once_with(service_name)
+ result_deferred.callback(
+ ([answer_srv], None, None)
+ )
+
+ servers = self.successResultOf(test_d)
+
self.assertEquals(len(servers), 1)
self.assertEquals(servers, cache[service_name])
self.assertEquals(servers[0].host, host_name)
@@ -127,3 +151,59 @@ class DnsTestCase(unittest.TestCase):
self.assertEquals(len(servers), 0)
self.assertEquals(len(cache), 0)
+
+ def test_disabled_service(self):
+ """
+ test the behaviour when there is a single record which is ".".
+ """
+ service_name = b"test_service.example.com"
+
+ lookup_deferred = Deferred()
+ dns_client_mock = Mock()
+ dns_client_mock.lookupService.return_value = lookup_deferred
+ cache = {}
+
+ resolve_d = resolve_service(
+ service_name, dns_client=dns_client_mock, cache=cache
+ )
+ self.assertNoResult(resolve_d)
+
+ # returning a single "." should make the lookup fail with a ConenctError
+ lookup_deferred.callback((
+ [dns.RRHeader(type=dns.SRV, payload=dns.Record_SRV(target=b"."))],
+ None,
+ None,
+ ))
+
+ self.failureResultOf(resolve_d, ConnectError)
+
+ def test_non_srv_answer(self):
+ """
+ test the behaviour when the dns server gives us a spurious non-SRV response
+ """
+ service_name = b"test_service.example.com"
+
+ lookup_deferred = Deferred()
+ dns_client_mock = Mock()
+ dns_client_mock.lookupService.return_value = lookup_deferred
+ cache = {}
+
+ resolve_d = resolve_service(
+ service_name, dns_client=dns_client_mock, cache=cache
+ )
+ self.assertNoResult(resolve_d)
+
+ lookup_deferred.callback((
+ [
+ dns.RRHeader(type=dns.A, payload=dns.Record_A()),
+ dns.RRHeader(type=dns.SRV, payload=dns.Record_SRV(target=b"host")),
+ ],
+ None,
+ None,
+ ))
+
+ servers = self.successResultOf(resolve_d)
+
+ self.assertEquals(len(servers), 1)
+ self.assertEquals(servers, cache[service_name])
+ self.assertEquals(servers[0].host, b"host")
|