test_matrix_federation_agent.py 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552
  1. # Copyright 2019 New Vector Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import base64
  15. import logging
  16. import os
  17. from typing import Iterable, Optional
  18. from unittest.mock import Mock, patch
  19. import treq
  20. from netaddr import IPSet
  21. from service_identity import VerificationError
  22. from zope.interface import implementer
  23. from twisted.internet import defer
  24. from twisted.internet._sslverify import ClientTLSOptions, OpenSSLCertificateOptions
  25. from twisted.internet.interfaces import IProtocolFactory
  26. from twisted.internet.protocol import Factory
  27. from twisted.protocols.tls import TLSMemoryBIOFactory, TLSMemoryBIOProtocol
  28. from twisted.web._newclient import ResponseNeverReceived
  29. from twisted.web.client import Agent
  30. from twisted.web.http import HTTPChannel, Request
  31. from twisted.web.http_headers import Headers
  32. from twisted.web.iweb import IPolicyForHTTPS
  33. from synapse.config.homeserver import HomeServerConfig
  34. from synapse.crypto.context_factory import FederationPolicyForHTTPS
  35. from synapse.http.federation.matrix_federation_agent import MatrixFederationAgent
  36. from synapse.http.federation.srv_resolver import Server
  37. from synapse.http.federation.well_known_resolver import (
  38. WELL_KNOWN_MAX_SIZE,
  39. WellKnownResolver,
  40. _cache_period_from_headers,
  41. )
  42. from synapse.logging.context import SENTINEL_CONTEXT, LoggingContext, current_context
  43. from synapse.util.caches.ttlcache import TTLCache
  44. from tests import unittest
  45. from tests.http import TestServerTLSConnectionFactory, get_test_ca_cert_file
  46. from tests.server import FakeTransport, ThreadedMemoryReactorClock
  47. from tests.utils import default_config
  48. logger = logging.getLogger(__name__)
  49. # Once Async Mocks or lambdas are supported this can go away.
  50. def generate_resolve_service(result):
  51. async def resolve_service(_):
  52. return result
  53. return resolve_service
  54. class MatrixFederationAgentTests(unittest.TestCase):
  55. def setUp(self):
  56. self.reactor = ThreadedMemoryReactorClock()
  57. self.mock_resolver = Mock()
  58. config_dict = default_config("test", parse=False)
  59. config_dict["federation_custom_ca_list"] = [get_test_ca_cert_file()]
  60. self._config = config = HomeServerConfig()
  61. config.parse_config_dict(config_dict, "", "")
  62. self.tls_factory = FederationPolicyForHTTPS(config)
  63. self.well_known_cache = TTLCache("test_cache", timer=self.reactor.seconds)
  64. self.had_well_known_cache = TTLCache("test_cache", timer=self.reactor.seconds)
  65. self.well_known_resolver = WellKnownResolver(
  66. self.reactor,
  67. Agent(self.reactor, contextFactory=self.tls_factory),
  68. b"test-agent",
  69. well_known_cache=self.well_known_cache,
  70. had_well_known_cache=self.had_well_known_cache,
  71. )
  72. def _make_connection(
  73. self,
  74. client_factory: IProtocolFactory,
  75. ssl: bool = True,
  76. expected_sni: bytes = None,
  77. tls_sanlist: Optional[Iterable[bytes]] = None,
  78. ) -> HTTPChannel:
  79. """Builds a test server, and completes the outgoing client connection
  80. Args:
  81. client_factory: the the factory that the
  82. application is trying to use to make the outbound connection. We will
  83. invoke it to build the client Protocol
  84. ssl: If true, we will expect an ssl connection and wrap
  85. server_factory with a TLSMemoryBIOFactory
  86. False is set only for when proxy expect http connection.
  87. Otherwise federation requests use always https.
  88. expected_sni: the expected SNI value
  89. tls_sanlist: list of SAN entries for the TLS cert presented by the server.
  90. Returns:
  91. the server Protocol returned by server_factory
  92. """
  93. # build the test server
  94. server_factory = _get_test_protocol_factory()
  95. if ssl:
  96. server_factory = _wrap_server_factory_for_tls(server_factory, tls_sanlist)
  97. server_protocol = server_factory.buildProtocol(None)
  98. # now, tell the client protocol factory to build the client protocol (it will be a
  99. # _WrappingProtocol, around a TLSMemoryBIOProtocol, around an
  100. # HTTP11ClientProtocol) and wire the output of said protocol up to the server via
  101. # a FakeTransport.
  102. #
  103. # Normally this would be done by the TCP socket code in Twisted, but we are
  104. # stubbing that out here.
  105. client_protocol = client_factory.buildProtocol(None)
  106. client_protocol.makeConnection(
  107. FakeTransport(server_protocol, self.reactor, client_protocol)
  108. )
  109. # tell the server protocol to send its stuff back to the client, too
  110. server_protocol.makeConnection(
  111. FakeTransport(client_protocol, self.reactor, server_protocol)
  112. )
  113. if ssl:
  114. # fish the test server back out of the server-side TLS protocol.
  115. http_protocol = server_protocol.wrappedProtocol
  116. # grab a hold of the TLS connection, in case it gets torn down
  117. tls_connection = server_protocol._tlsConnection
  118. else:
  119. http_protocol = server_protocol
  120. tls_connection = None
  121. # give the reactor a pump to get the TLS juices flowing (if needed)
  122. self.reactor.advance(0)
  123. # check the SNI
  124. if expected_sni is not None:
  125. server_name = tls_connection.get_servername()
  126. self.assertEqual(
  127. server_name,
  128. expected_sni,
  129. f"Expected SNI {expected_sni!s} but got {server_name!s}",
  130. )
  131. return http_protocol
  132. @defer.inlineCallbacks
  133. def _make_get_request(self, uri: bytes):
  134. """
  135. Sends a simple GET request via the agent, and checks its logcontext management
  136. """
  137. with LoggingContext("one") as context:
  138. fetch_d = self.agent.request(b"GET", uri)
  139. # Nothing happened yet
  140. self.assertNoResult(fetch_d)
  141. # should have reset logcontext to the sentinel
  142. _check_logcontext(SENTINEL_CONTEXT)
  143. try:
  144. fetch_res = yield fetch_d
  145. return fetch_res
  146. except Exception as e:
  147. logger.info("Fetch of %s failed: %s", uri.decode("ascii"), e)
  148. raise
  149. finally:
  150. _check_logcontext(context)
  151. def _handle_well_known_connection(
  152. self,
  153. client_factory: IProtocolFactory,
  154. expected_sni: bytes,
  155. content: bytes,
  156. response_headers: Optional[dict] = None,
  157. ) -> HTTPChannel:
  158. """Handle an outgoing HTTPs connection: wire it up to a server, check that the
  159. request is for a .well-known, and send the response.
  160. Args:
  161. client_factory: outgoing connection
  162. expected_sni: SNI that we expect the outgoing connection to send
  163. content: content to send back as the .well-known
  164. Returns:
  165. server impl
  166. """
  167. # make the connection for .well-known
  168. well_known_server = self._make_connection(
  169. client_factory, expected_sni=expected_sni
  170. )
  171. # check the .well-known request and send a response
  172. self.assertEqual(len(well_known_server.requests), 1)
  173. request = well_known_server.requests[0]
  174. self.assertEqual(
  175. request.requestHeaders.getRawHeaders(b"user-agent"), [b"test-agent"]
  176. )
  177. self._send_well_known_response(request, content, headers=response_headers or {})
  178. return well_known_server
  179. def _send_well_known_response(
  180. self,
  181. request: Request,
  182. content: bytes,
  183. headers: Optional[dict] = None,
  184. ):
  185. """Check that an incoming request looks like a valid .well-known request, and
  186. send back the response.
  187. """
  188. self.assertEqual(request.method, b"GET")
  189. self.assertEqual(request.path, b"/.well-known/matrix/server")
  190. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
  191. # send back a response
  192. for k, v in (headers or {}).items():
  193. request.setHeader(k, v)
  194. request.write(content)
  195. request.finish()
  196. self.reactor.pump((0.1,))
  197. def _make_agent(self) -> MatrixFederationAgent:
  198. """
  199. If a proxy server is set, the MatrixFederationAgent must be created again
  200. because it is created too early during setUp
  201. """
  202. return MatrixFederationAgent(
  203. reactor=self.reactor,
  204. tls_client_options_factory=self.tls_factory,
  205. user_agent="test-agent", # Note that this is unused since _well_known_resolver is provided.
  206. ip_whitelist=IPSet(),
  207. ip_blacklist=IPSet(),
  208. _srv_resolver=self.mock_resolver,
  209. _well_known_resolver=self.well_known_resolver,
  210. )
  211. def test_get(self):
  212. """happy-path test of a GET request with an explicit port"""
  213. self._do_get()
  214. @patch.dict(
  215. os.environ,
  216. {"https_proxy": "proxy.com", "no_proxy": "testserv"},
  217. )
  218. def test_get_bypass_proxy(self):
  219. """test of a GET request with an explicit port and bypass proxy"""
  220. self._do_get()
  221. def _do_get(self):
  222. """test of a GET request with an explicit port"""
  223. self.agent = self._make_agent()
  224. self.reactor.lookups["testserv"] = "1.2.3.4"
  225. test_d = self._make_get_request(b"matrix://testserv:8448/foo/bar")
  226. # Nothing happened yet
  227. self.assertNoResult(test_d)
  228. # Make sure treq is trying to connect
  229. clients = self.reactor.tcpClients
  230. self.assertEqual(len(clients), 1)
  231. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  232. self.assertEqual(host, "1.2.3.4")
  233. self.assertEqual(port, 8448)
  234. # make a test server, and wire up the client
  235. http_server = self._make_connection(client_factory, expected_sni=b"testserv")
  236. self.assertEqual(len(http_server.requests), 1)
  237. request = http_server.requests[0]
  238. self.assertEqual(request.method, b"GET")
  239. self.assertEqual(request.path, b"/foo/bar")
  240. self.assertEqual(
  241. request.requestHeaders.getRawHeaders(b"host"), [b"testserv:8448"]
  242. )
  243. self.assertEqual(
  244. request.requestHeaders.getRawHeaders(b"user-agent"), [b"test-agent"]
  245. )
  246. content = request.content.read()
  247. self.assertEqual(content, b"")
  248. # Deferred is still without a result
  249. self.assertNoResult(test_d)
  250. # send the headers
  251. request.responseHeaders.setRawHeaders(b"Content-Type", [b"application/json"])
  252. request.write("")
  253. self.reactor.pump((0.1,))
  254. response = self.successResultOf(test_d)
  255. # that should give us a Response object
  256. self.assertEqual(response.code, 200)
  257. # Send the body
  258. request.write(b'{ "a": 1 }')
  259. request.finish()
  260. self.reactor.pump((0.1,))
  261. # check it can be read
  262. json = self.successResultOf(treq.json_content(response))
  263. self.assertEqual(json, {"a": 1})
  264. @patch.dict(
  265. os.environ, {"https_proxy": "http://proxy.com", "no_proxy": "unused.com"}
  266. )
  267. def test_get_via_http_proxy(self):
  268. """test for federation request through a http proxy"""
  269. self._do_get_via_proxy(expect_proxy_ssl=False, expected_auth_credentials=None)
  270. @patch.dict(
  271. os.environ,
  272. {"https_proxy": "http://user:pass@proxy.com", "no_proxy": "unused.com"},
  273. )
  274. def test_get_via_http_proxy_with_auth(self):
  275. """test for federation request through a http proxy with authentication"""
  276. self._do_get_via_proxy(
  277. expect_proxy_ssl=False, expected_auth_credentials=b"user:pass"
  278. )
  279. @patch.dict(
  280. os.environ, {"https_proxy": "https://proxy.com", "no_proxy": "unused.com"}
  281. )
  282. def test_get_via_https_proxy(self):
  283. """test for federation request through a https proxy"""
  284. self._do_get_via_proxy(expect_proxy_ssl=True, expected_auth_credentials=None)
  285. @patch.dict(
  286. os.environ,
  287. {"https_proxy": "https://user:pass@proxy.com", "no_proxy": "unused.com"},
  288. )
  289. def test_get_via_https_proxy_with_auth(self):
  290. """test for federation request through a https proxy with authentication"""
  291. self._do_get_via_proxy(
  292. expect_proxy_ssl=True, expected_auth_credentials=b"user:pass"
  293. )
  294. def _do_get_via_proxy(
  295. self,
  296. expect_proxy_ssl: bool = False,
  297. expected_auth_credentials: Optional[bytes] = None,
  298. ):
  299. """Send a https federation request via an agent and check that it is correctly
  300. received at the proxy and client. The proxy can use either http or https.
  301. Args:
  302. expect_proxy_ssl: True if we expect the request to connect to the proxy via https.
  303. expected_auth_credentials: credentials we expect to be presented to authenticate at the proxy
  304. """
  305. self.agent = self._make_agent()
  306. self.reactor.lookups["testserv"] = "1.2.3.4"
  307. self.reactor.lookups["proxy.com"] = "9.9.9.9"
  308. test_d = self._make_get_request(b"matrix://testserv:8448/foo/bar")
  309. # Nothing happened yet
  310. self.assertNoResult(test_d)
  311. # Make sure treq is trying to connect
  312. clients = self.reactor.tcpClients
  313. self.assertEqual(len(clients), 1)
  314. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  315. # make sure we are connecting to the proxy
  316. self.assertEqual(host, "9.9.9.9")
  317. self.assertEqual(port, 1080)
  318. # make a test server to act as the proxy, and wire up the client
  319. proxy_server = self._make_connection(
  320. client_factory,
  321. ssl=expect_proxy_ssl,
  322. tls_sanlist=[b"DNS:proxy.com"] if expect_proxy_ssl else None,
  323. expected_sni=b"proxy.com" if expect_proxy_ssl else None,
  324. )
  325. assert isinstance(proxy_server, HTTPChannel)
  326. # now there should be a pending CONNECT request
  327. self.assertEqual(len(proxy_server.requests), 1)
  328. request = proxy_server.requests[0]
  329. self.assertEqual(request.method, b"CONNECT")
  330. self.assertEqual(request.path, b"testserv:8448")
  331. # Check whether auth credentials have been supplied to the proxy
  332. proxy_auth_header_values = request.requestHeaders.getRawHeaders(
  333. b"Proxy-Authorization"
  334. )
  335. if expected_auth_credentials is not None:
  336. # Compute the correct header value for Proxy-Authorization
  337. encoded_credentials = base64.b64encode(expected_auth_credentials)
  338. expected_header_value = b"Basic " + encoded_credentials
  339. # Validate the header's value
  340. self.assertIn(expected_header_value, proxy_auth_header_values)
  341. else:
  342. # Check that the Proxy-Authorization header has not been supplied to the proxy
  343. self.assertIsNone(proxy_auth_header_values)
  344. # tell the proxy server not to close the connection
  345. proxy_server.persistent = True
  346. request.finish()
  347. # now we make another test server to act as the upstream HTTP server.
  348. server_ssl_protocol = _wrap_server_factory_for_tls(
  349. _get_test_protocol_factory()
  350. ).buildProtocol(None)
  351. # Tell the HTTP server to send outgoing traffic back via the proxy's transport.
  352. proxy_server_transport = proxy_server.transport
  353. server_ssl_protocol.makeConnection(proxy_server_transport)
  354. # ... and replace the protocol on the proxy's transport with the
  355. # TLSMemoryBIOProtocol for the test server, so that incoming traffic
  356. # to the proxy gets sent over to the HTTP(s) server.
  357. # See also comment at `_do_https_request_via_proxy`
  358. # in ../test_proxyagent.py for more details
  359. if expect_proxy_ssl:
  360. assert isinstance(proxy_server_transport, TLSMemoryBIOProtocol)
  361. proxy_server_transport.wrappedProtocol = server_ssl_protocol
  362. else:
  363. assert isinstance(proxy_server_transport, FakeTransport)
  364. client_protocol = proxy_server_transport.other
  365. c2s_transport = client_protocol.transport
  366. c2s_transport.other = server_ssl_protocol
  367. self.reactor.advance(0)
  368. server_name = server_ssl_protocol._tlsConnection.get_servername()
  369. expected_sni = b"testserv"
  370. self.assertEqual(
  371. server_name,
  372. expected_sni,
  373. f"Expected SNI {expected_sni!s} but got {server_name!s}",
  374. )
  375. # now there should be a pending request
  376. http_server = server_ssl_protocol.wrappedProtocol
  377. self.assertEqual(len(http_server.requests), 1)
  378. request = http_server.requests[0]
  379. self.assertEqual(request.method, b"GET")
  380. self.assertEqual(request.path, b"/foo/bar")
  381. self.assertEqual(
  382. request.requestHeaders.getRawHeaders(b"host"), [b"testserv:8448"]
  383. )
  384. self.assertEqual(
  385. request.requestHeaders.getRawHeaders(b"user-agent"), [b"test-agent"]
  386. )
  387. # Check that the destination server DID NOT receive proxy credentials
  388. self.assertIsNone(request.requestHeaders.getRawHeaders(b"Proxy-Authorization"))
  389. content = request.content.read()
  390. self.assertEqual(content, b"")
  391. # Deferred is still without a result
  392. self.assertNoResult(test_d)
  393. # send the headers
  394. request.responseHeaders.setRawHeaders(b"Content-Type", [b"application/json"])
  395. request.write("")
  396. self.reactor.pump((0.1,))
  397. response = self.successResultOf(test_d)
  398. # that should give us a Response object
  399. self.assertEqual(response.code, 200)
  400. # Send the body
  401. request.write('{ "a": 1 }'.encode("ascii"))
  402. request.finish()
  403. self.reactor.pump((0.1,))
  404. # check it can be read
  405. json = self.successResultOf(treq.json_content(response))
  406. self.assertEqual(json, {"a": 1})
  407. def test_get_ip_address(self):
  408. """
  409. Test the behaviour when the server name contains an explicit IP (with no port)
  410. """
  411. self.agent = self._make_agent()
  412. # there will be a getaddrinfo on the IP
  413. self.reactor.lookups["1.2.3.4"] = "1.2.3.4"
  414. test_d = self._make_get_request(b"matrix://1.2.3.4/foo/bar")
  415. # Nothing happened yet
  416. self.assertNoResult(test_d)
  417. # Make sure treq is trying to connect
  418. clients = self.reactor.tcpClients
  419. self.assertEqual(len(clients), 1)
  420. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  421. self.assertEqual(host, "1.2.3.4")
  422. self.assertEqual(port, 8448)
  423. # make a test server, and wire up the client
  424. http_server = self._make_connection(client_factory, expected_sni=None)
  425. self.assertEqual(len(http_server.requests), 1)
  426. request = http_server.requests[0]
  427. self.assertEqual(request.method, b"GET")
  428. self.assertEqual(request.path, b"/foo/bar")
  429. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"1.2.3.4"])
  430. # finish the request
  431. request.finish()
  432. self.reactor.pump((0.1,))
  433. self.successResultOf(test_d)
  434. def test_get_ipv6_address(self):
  435. """
  436. Test the behaviour when the server name contains an explicit IPv6 address
  437. (with no port)
  438. """
  439. self.agent = self._make_agent()
  440. # there will be a getaddrinfo on the IP
  441. self.reactor.lookups["::1"] = "::1"
  442. test_d = self._make_get_request(b"matrix://[::1]/foo/bar")
  443. # Nothing happened yet
  444. self.assertNoResult(test_d)
  445. # Make sure treq is trying to connect
  446. clients = self.reactor.tcpClients
  447. self.assertEqual(len(clients), 1)
  448. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  449. self.assertEqual(host, "::1")
  450. self.assertEqual(port, 8448)
  451. # make a test server, and wire up the client
  452. http_server = self._make_connection(client_factory, expected_sni=None)
  453. self.assertEqual(len(http_server.requests), 1)
  454. request = http_server.requests[0]
  455. self.assertEqual(request.method, b"GET")
  456. self.assertEqual(request.path, b"/foo/bar")
  457. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"[::1]"])
  458. # finish the request
  459. request.finish()
  460. self.reactor.pump((0.1,))
  461. self.successResultOf(test_d)
  462. def test_get_ipv6_address_with_port(self):
  463. """
  464. Test the behaviour when the server name contains an explicit IPv6 address
  465. (with explicit port)
  466. """
  467. self.agent = self._make_agent()
  468. # there will be a getaddrinfo on the IP
  469. self.reactor.lookups["::1"] = "::1"
  470. test_d = self._make_get_request(b"matrix://[::1]:80/foo/bar")
  471. # Nothing happened yet
  472. self.assertNoResult(test_d)
  473. # Make sure treq is trying to connect
  474. clients = self.reactor.tcpClients
  475. self.assertEqual(len(clients), 1)
  476. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  477. self.assertEqual(host, "::1")
  478. self.assertEqual(port, 80)
  479. # make a test server, and wire up the client
  480. http_server = self._make_connection(client_factory, expected_sni=None)
  481. self.assertEqual(len(http_server.requests), 1)
  482. request = http_server.requests[0]
  483. self.assertEqual(request.method, b"GET")
  484. self.assertEqual(request.path, b"/foo/bar")
  485. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"[::1]:80"])
  486. # finish the request
  487. request.finish()
  488. self.reactor.pump((0.1,))
  489. self.successResultOf(test_d)
  490. def test_get_hostname_bad_cert(self):
  491. """
  492. Test the behaviour when the certificate on the server doesn't match the hostname
  493. """
  494. self.agent = self._make_agent()
  495. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  496. self.reactor.lookups["testserv1"] = "1.2.3.4"
  497. test_d = self._make_get_request(b"matrix://testserv1/foo/bar")
  498. # Nothing happened yet
  499. self.assertNoResult(test_d)
  500. # No SRV record lookup yet
  501. self.mock_resolver.resolve_service.assert_not_called()
  502. # there should be an attempt to connect on port 443 for the .well-known
  503. clients = self.reactor.tcpClients
  504. self.assertEqual(len(clients), 1)
  505. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  506. self.assertEqual(host, "1.2.3.4")
  507. self.assertEqual(port, 443)
  508. # fonx the connection
  509. client_factory.clientConnectionFailed(None, Exception("nope"))
  510. # attemptdelay on the hostnameendpoint is 0.3, so takes that long before the
  511. # .well-known request fails.
  512. self.reactor.pump((0.4,))
  513. # now there should be a SRV lookup
  514. self.mock_resolver.resolve_service.assert_called_once_with(
  515. b"_matrix._tcp.testserv1"
  516. )
  517. # we should fall back to a direct connection
  518. self.assertEqual(len(clients), 2)
  519. (host, port, client_factory, _timeout, _bindAddress) = clients[1]
  520. self.assertEqual(host, "1.2.3.4")
  521. self.assertEqual(port, 8448)
  522. # make a test server, and wire up the client
  523. http_server = self._make_connection(client_factory, expected_sni=b"testserv1")
  524. # there should be no requests
  525. self.assertEqual(len(http_server.requests), 0)
  526. # ... and the request should have failed
  527. e = self.failureResultOf(test_d, ResponseNeverReceived)
  528. failure_reason = e.value.reasons[0]
  529. self.assertIsInstance(failure_reason.value, VerificationError)
  530. def test_get_ip_address_bad_cert(self):
  531. """
  532. Test the behaviour when the server name contains an explicit IP, but
  533. the server cert doesn't cover it
  534. """
  535. self.agent = self._make_agent()
  536. # there will be a getaddrinfo on the IP
  537. self.reactor.lookups["1.2.3.5"] = "1.2.3.5"
  538. test_d = self._make_get_request(b"matrix://1.2.3.5/foo/bar")
  539. # Nothing happened yet
  540. self.assertNoResult(test_d)
  541. # Make sure treq is trying to connect
  542. clients = self.reactor.tcpClients
  543. self.assertEqual(len(clients), 1)
  544. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  545. self.assertEqual(host, "1.2.3.5")
  546. self.assertEqual(port, 8448)
  547. # make a test server, and wire up the client
  548. http_server = self._make_connection(client_factory, expected_sni=None)
  549. # there should be no requests
  550. self.assertEqual(len(http_server.requests), 0)
  551. # ... and the request should have failed
  552. e = self.failureResultOf(test_d, ResponseNeverReceived)
  553. failure_reason = e.value.reasons[0]
  554. self.assertIsInstance(failure_reason.value, VerificationError)
  555. def test_get_no_srv_no_well_known(self):
  556. """
  557. Test the behaviour when the server name has no port, no SRV, and no well-known
  558. """
  559. self.agent = self._make_agent()
  560. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  561. self.reactor.lookups["testserv"] = "1.2.3.4"
  562. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  563. # Nothing happened yet
  564. self.assertNoResult(test_d)
  565. # No SRV record lookup yet
  566. self.mock_resolver.resolve_service.assert_not_called()
  567. # there should be an attempt to connect on port 443 for the .well-known
  568. clients = self.reactor.tcpClients
  569. self.assertEqual(len(clients), 1)
  570. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  571. self.assertEqual(host, "1.2.3.4")
  572. self.assertEqual(port, 443)
  573. # fonx the connection
  574. client_factory.clientConnectionFailed(None, Exception("nope"))
  575. # attemptdelay on the hostnameendpoint is 0.3, so takes that long before the
  576. # .well-known request fails.
  577. self.reactor.pump((0.4,))
  578. # now there should be a SRV lookup
  579. self.mock_resolver.resolve_service.assert_called_once_with(
  580. b"_matrix._tcp.testserv"
  581. )
  582. # we should fall back to a direct connection
  583. self.assertEqual(len(clients), 2)
  584. (host, port, client_factory, _timeout, _bindAddress) = clients[1]
  585. self.assertEqual(host, "1.2.3.4")
  586. self.assertEqual(port, 8448)
  587. # make a test server, and wire up the client
  588. http_server = self._make_connection(client_factory, expected_sni=b"testserv")
  589. self.assertEqual(len(http_server.requests), 1)
  590. request = http_server.requests[0]
  591. self.assertEqual(request.method, b"GET")
  592. self.assertEqual(request.path, b"/foo/bar")
  593. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
  594. # finish the request
  595. request.finish()
  596. self.reactor.pump((0.1,))
  597. self.successResultOf(test_d)
  598. def test_get_well_known(self):
  599. """Test the behaviour when the .well-known delegates elsewhere"""
  600. self.agent = self._make_agent()
  601. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  602. self.reactor.lookups["testserv"] = "1.2.3.4"
  603. self.reactor.lookups["target-server"] = "1::f"
  604. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  605. # Nothing happened yet
  606. self.assertNoResult(test_d)
  607. # there should be an attempt to connect on port 443 for the .well-known
  608. clients = self.reactor.tcpClients
  609. self.assertEqual(len(clients), 1)
  610. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  611. self.assertEqual(host, "1.2.3.4")
  612. self.assertEqual(port, 443)
  613. self._handle_well_known_connection(
  614. client_factory,
  615. expected_sni=b"testserv",
  616. content=b'{ "m.server": "target-server" }',
  617. )
  618. # there should be a SRV lookup
  619. self.mock_resolver.resolve_service.assert_called_once_with(
  620. b"_matrix._tcp.target-server"
  621. )
  622. # now we should get a connection to the target server
  623. self.assertEqual(len(clients), 2)
  624. (host, port, client_factory, _timeout, _bindAddress) = clients[1]
  625. self.assertEqual(host, "1::f")
  626. self.assertEqual(port, 8448)
  627. # make a test server, and wire up the client
  628. http_server = self._make_connection(
  629. client_factory, expected_sni=b"target-server"
  630. )
  631. self.assertEqual(len(http_server.requests), 1)
  632. request = http_server.requests[0]
  633. self.assertEqual(request.method, b"GET")
  634. self.assertEqual(request.path, b"/foo/bar")
  635. self.assertEqual(
  636. request.requestHeaders.getRawHeaders(b"host"), [b"target-server"]
  637. )
  638. # finish the request
  639. request.finish()
  640. self.reactor.pump((0.1,))
  641. self.successResultOf(test_d)
  642. self.assertEqual(self.well_known_cache[b"testserv"], b"target-server")
  643. # check the cache expires
  644. self.reactor.pump((48 * 3600,))
  645. self.well_known_cache.expire()
  646. self.assertNotIn(b"testserv", self.well_known_cache)
  647. def test_get_well_known_redirect(self):
  648. """Test the behaviour when the server name has no port and no SRV record, but
  649. the .well-known has a 300 redirect
  650. """
  651. self.agent = self._make_agent()
  652. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  653. self.reactor.lookups["testserv"] = "1.2.3.4"
  654. self.reactor.lookups["target-server"] = "1::f"
  655. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  656. # Nothing happened yet
  657. self.assertNoResult(test_d)
  658. # there should be an attempt to connect on port 443 for the .well-known
  659. clients = self.reactor.tcpClients
  660. self.assertEqual(len(clients), 1)
  661. (host, port, client_factory, _timeout, _bindAddress) = clients.pop()
  662. self.assertEqual(host, "1.2.3.4")
  663. self.assertEqual(port, 443)
  664. redirect_server = self._make_connection(
  665. client_factory, expected_sni=b"testserv"
  666. )
  667. # send a 302 redirect
  668. self.assertEqual(len(redirect_server.requests), 1)
  669. request = redirect_server.requests[0]
  670. request.redirect(b"https://testserv/even_better_known")
  671. request.finish()
  672. self.reactor.pump((0.1,))
  673. # now there should be another connection
  674. clients = self.reactor.tcpClients
  675. self.assertEqual(len(clients), 1)
  676. (host, port, client_factory, _timeout, _bindAddress) = clients.pop()
  677. self.assertEqual(host, "1.2.3.4")
  678. self.assertEqual(port, 443)
  679. well_known_server = self._make_connection(
  680. client_factory, expected_sni=b"testserv"
  681. )
  682. self.assertEqual(len(well_known_server.requests), 1, "No request after 302")
  683. request = well_known_server.requests[0]
  684. self.assertEqual(request.method, b"GET")
  685. self.assertEqual(request.path, b"/even_better_known")
  686. request.write(b'{ "m.server": "target-server" }')
  687. request.finish()
  688. self.reactor.pump((0.1,))
  689. # there should be a SRV lookup
  690. self.mock_resolver.resolve_service.assert_called_once_with(
  691. b"_matrix._tcp.target-server"
  692. )
  693. # now we should get a connection to the target server
  694. self.assertEqual(len(clients), 1)
  695. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  696. self.assertEqual(host, "1::f")
  697. self.assertEqual(port, 8448)
  698. # make a test server, and wire up the client
  699. http_server = self._make_connection(
  700. client_factory, expected_sni=b"target-server"
  701. )
  702. self.assertEqual(len(http_server.requests), 1)
  703. request = http_server.requests[0]
  704. self.assertEqual(request.method, b"GET")
  705. self.assertEqual(request.path, b"/foo/bar")
  706. self.assertEqual(
  707. request.requestHeaders.getRawHeaders(b"host"), [b"target-server"]
  708. )
  709. # finish the request
  710. request.finish()
  711. self.reactor.pump((0.1,))
  712. self.successResultOf(test_d)
  713. self.assertEqual(self.well_known_cache[b"testserv"], b"target-server")
  714. # check the cache expires
  715. self.reactor.pump((48 * 3600,))
  716. self.well_known_cache.expire()
  717. self.assertNotIn(b"testserv", self.well_known_cache)
  718. def test_get_invalid_well_known(self):
  719. """
  720. Test the behaviour when the server name has an *invalid* well-known (and no SRV)
  721. """
  722. self.agent = self._make_agent()
  723. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  724. self.reactor.lookups["testserv"] = "1.2.3.4"
  725. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  726. # Nothing happened yet
  727. self.assertNoResult(test_d)
  728. # No SRV record lookup yet
  729. self.mock_resolver.resolve_service.assert_not_called()
  730. # there should be an attempt to connect on port 443 for the .well-known
  731. clients = self.reactor.tcpClients
  732. self.assertEqual(len(clients), 1)
  733. (host, port, client_factory, _timeout, _bindAddress) = clients.pop()
  734. self.assertEqual(host, "1.2.3.4")
  735. self.assertEqual(port, 443)
  736. self._handle_well_known_connection(
  737. client_factory, expected_sni=b"testserv", content=b"NOT JSON"
  738. )
  739. # now there should be a SRV lookup
  740. self.mock_resolver.resolve_service.assert_called_once_with(
  741. b"_matrix._tcp.testserv"
  742. )
  743. # we should fall back to a direct connection
  744. self.assertEqual(len(clients), 1)
  745. (host, port, client_factory, _timeout, _bindAddress) = clients.pop()
  746. self.assertEqual(host, "1.2.3.4")
  747. self.assertEqual(port, 8448)
  748. # make a test server, and wire up the client
  749. http_server = self._make_connection(client_factory, expected_sni=b"testserv")
  750. self.assertEqual(len(http_server.requests), 1)
  751. request = http_server.requests[0]
  752. self.assertEqual(request.method, b"GET")
  753. self.assertEqual(request.path, b"/foo/bar")
  754. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
  755. # finish the request
  756. request.finish()
  757. self.reactor.pump((0.1,))
  758. self.successResultOf(test_d)
  759. def test_get_well_known_unsigned_cert(self):
  760. """Test the behaviour when the .well-known server presents a cert
  761. not signed by a CA
  762. """
  763. # we use the same test server as the other tests, but use an agent with
  764. # the config left to the default, which will not trust it (since the
  765. # presented cert is signed by a test CA)
  766. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  767. self.reactor.lookups["testserv"] = "1.2.3.4"
  768. config = default_config("test", parse=True)
  769. # Build a new agent and WellKnownResolver with a different tls factory
  770. tls_factory = FederationPolicyForHTTPS(config)
  771. agent = MatrixFederationAgent(
  772. reactor=self.reactor,
  773. tls_client_options_factory=tls_factory,
  774. user_agent=b"test-agent", # This is unused since _well_known_resolver is passed below.
  775. ip_whitelist=IPSet(),
  776. ip_blacklist=IPSet(),
  777. _srv_resolver=self.mock_resolver,
  778. _well_known_resolver=WellKnownResolver(
  779. self.reactor,
  780. Agent(self.reactor, contextFactory=tls_factory),
  781. b"test-agent",
  782. well_known_cache=self.well_known_cache,
  783. had_well_known_cache=self.had_well_known_cache,
  784. ),
  785. )
  786. test_d = agent.request(b"GET", b"matrix://testserv/foo/bar")
  787. # Nothing happened yet
  788. self.assertNoResult(test_d)
  789. # there should be an attempt to connect on port 443 for the .well-known
  790. clients = self.reactor.tcpClients
  791. self.assertEqual(len(clients), 1)
  792. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  793. self.assertEqual(host, "1.2.3.4")
  794. self.assertEqual(port, 443)
  795. http_proto = self._make_connection(client_factory, expected_sni=b"testserv")
  796. # there should be no requests
  797. self.assertEqual(len(http_proto.requests), 0)
  798. # and there should be a SRV lookup instead
  799. self.mock_resolver.resolve_service.assert_called_once_with(
  800. b"_matrix._tcp.testserv"
  801. )
  802. def test_get_hostname_srv(self):
  803. """
  804. Test the behaviour when there is a single SRV record
  805. """
  806. self.agent = self._make_agent()
  807. self.mock_resolver.resolve_service.side_effect = generate_resolve_service(
  808. [Server(host=b"srvtarget", port=8443)]
  809. )
  810. self.reactor.lookups["srvtarget"] = "1.2.3.4"
  811. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  812. # Nothing happened yet
  813. self.assertNoResult(test_d)
  814. # the request for a .well-known will have failed with a DNS lookup error.
  815. self.mock_resolver.resolve_service.assert_called_once_with(
  816. b"_matrix._tcp.testserv"
  817. )
  818. # Make sure treq is trying to connect
  819. clients = self.reactor.tcpClients
  820. self.assertEqual(len(clients), 1)
  821. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  822. self.assertEqual(host, "1.2.3.4")
  823. self.assertEqual(port, 8443)
  824. # make a test server, and wire up the client
  825. http_server = self._make_connection(client_factory, expected_sni=b"testserv")
  826. self.assertEqual(len(http_server.requests), 1)
  827. request = http_server.requests[0]
  828. self.assertEqual(request.method, b"GET")
  829. self.assertEqual(request.path, b"/foo/bar")
  830. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
  831. # finish the request
  832. request.finish()
  833. self.reactor.pump((0.1,))
  834. self.successResultOf(test_d)
  835. def test_get_well_known_srv(self):
  836. """Test the behaviour when the .well-known redirects to a place where there
  837. is a SRV.
  838. """
  839. self.agent = self._make_agent()
  840. self.reactor.lookups["testserv"] = "1.2.3.4"
  841. self.reactor.lookups["srvtarget"] = "5.6.7.8"
  842. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  843. # Nothing happened yet
  844. self.assertNoResult(test_d)
  845. # there should be an attempt to connect on port 443 for the .well-known
  846. clients = self.reactor.tcpClients
  847. self.assertEqual(len(clients), 1)
  848. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  849. self.assertEqual(host, "1.2.3.4")
  850. self.assertEqual(port, 443)
  851. self.mock_resolver.resolve_service.side_effect = generate_resolve_service(
  852. [Server(host=b"srvtarget", port=8443)]
  853. )
  854. self._handle_well_known_connection(
  855. client_factory,
  856. expected_sni=b"testserv",
  857. content=b'{ "m.server": "target-server" }',
  858. )
  859. # there should be a SRV lookup
  860. self.mock_resolver.resolve_service.assert_called_once_with(
  861. b"_matrix._tcp.target-server"
  862. )
  863. # now we should get a connection to the target of the SRV record
  864. self.assertEqual(len(clients), 2)
  865. (host, port, client_factory, _timeout, _bindAddress) = clients[1]
  866. self.assertEqual(host, "5.6.7.8")
  867. self.assertEqual(port, 8443)
  868. # make a test server, and wire up the client
  869. http_server = self._make_connection(
  870. client_factory, expected_sni=b"target-server"
  871. )
  872. self.assertEqual(len(http_server.requests), 1)
  873. request = http_server.requests[0]
  874. self.assertEqual(request.method, b"GET")
  875. self.assertEqual(request.path, b"/foo/bar")
  876. self.assertEqual(
  877. request.requestHeaders.getRawHeaders(b"host"), [b"target-server"]
  878. )
  879. # finish the request
  880. request.finish()
  881. self.reactor.pump((0.1,))
  882. self.successResultOf(test_d)
  883. def test_idna_servername(self):
  884. """test the behaviour when the server name has idna chars in"""
  885. self.agent = self._make_agent()
  886. self.mock_resolver.resolve_service.side_effect = generate_resolve_service([])
  887. # the resolver is always called with the IDNA hostname as a native string.
  888. self.reactor.lookups["xn--bcher-kva.com"] = "1.2.3.4"
  889. # this is idna for bücher.com
  890. test_d = self._make_get_request(b"matrix://xn--bcher-kva.com/foo/bar")
  891. # Nothing happened yet
  892. self.assertNoResult(test_d)
  893. # No SRV record lookup yet
  894. self.mock_resolver.resolve_service.assert_not_called()
  895. # there should be an attempt to connect on port 443 for the .well-known
  896. clients = self.reactor.tcpClients
  897. self.assertEqual(len(clients), 1)
  898. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  899. self.assertEqual(host, "1.2.3.4")
  900. self.assertEqual(port, 443)
  901. # fonx the connection
  902. client_factory.clientConnectionFailed(None, Exception("nope"))
  903. # attemptdelay on the hostnameendpoint is 0.3, so takes that long before the
  904. # .well-known request fails.
  905. self.reactor.pump((0.4,))
  906. # now there should have been a SRV lookup
  907. self.mock_resolver.resolve_service.assert_called_once_with(
  908. b"_matrix._tcp.xn--bcher-kva.com"
  909. )
  910. # We should fall back to port 8448
  911. clients = self.reactor.tcpClients
  912. self.assertEqual(len(clients), 2)
  913. (host, port, client_factory, _timeout, _bindAddress) = clients[1]
  914. self.assertEqual(host, "1.2.3.4")
  915. self.assertEqual(port, 8448)
  916. # make a test server, and wire up the client
  917. http_server = self._make_connection(
  918. client_factory, expected_sni=b"xn--bcher-kva.com"
  919. )
  920. self.assertEqual(len(http_server.requests), 1)
  921. request = http_server.requests[0]
  922. self.assertEqual(request.method, b"GET")
  923. self.assertEqual(request.path, b"/foo/bar")
  924. self.assertEqual(
  925. request.requestHeaders.getRawHeaders(b"host"), [b"xn--bcher-kva.com"]
  926. )
  927. # finish the request
  928. request.finish()
  929. self.reactor.pump((0.1,))
  930. self.successResultOf(test_d)
  931. def test_idna_srv_target(self):
  932. """test the behaviour when the target of a SRV record has idna chars"""
  933. self.agent = self._make_agent()
  934. self.mock_resolver.resolve_service.side_effect = generate_resolve_service(
  935. [Server(host=b"xn--trget-3qa.com", port=8443)] # târget.com
  936. )
  937. self.reactor.lookups["xn--trget-3qa.com"] = "1.2.3.4"
  938. test_d = self._make_get_request(b"matrix://xn--bcher-kva.com/foo/bar")
  939. # Nothing happened yet
  940. self.assertNoResult(test_d)
  941. self.mock_resolver.resolve_service.assert_called_once_with(
  942. b"_matrix._tcp.xn--bcher-kva.com"
  943. )
  944. # Make sure treq is trying to connect
  945. clients = self.reactor.tcpClients
  946. self.assertEqual(len(clients), 1)
  947. (host, port, client_factory, _timeout, _bindAddress) = clients[0]
  948. self.assertEqual(host, "1.2.3.4")
  949. self.assertEqual(port, 8443)
  950. # make a test server, and wire up the client
  951. http_server = self._make_connection(
  952. client_factory, expected_sni=b"xn--bcher-kva.com"
  953. )
  954. self.assertEqual(len(http_server.requests), 1)
  955. request = http_server.requests[0]
  956. self.assertEqual(request.method, b"GET")
  957. self.assertEqual(request.path, b"/foo/bar")
  958. self.assertEqual(
  959. request.requestHeaders.getRawHeaders(b"host"), [b"xn--bcher-kva.com"]
  960. )
  961. # finish the request
  962. request.finish()
  963. self.reactor.pump((0.1,))
  964. self.successResultOf(test_d)
  965. def test_well_known_cache(self):
  966. self.reactor.lookups["testserv"] = "1.2.3.4"
  967. fetch_d = defer.ensureDeferred(
  968. self.well_known_resolver.get_well_known(b"testserv")
  969. )
  970. # there should be an attempt to connect on port 443 for the .well-known
  971. clients = self.reactor.tcpClients
  972. self.assertEqual(len(clients), 1)
  973. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  974. self.assertEqual(host, "1.2.3.4")
  975. self.assertEqual(port, 443)
  976. well_known_server = self._handle_well_known_connection(
  977. client_factory,
  978. expected_sni=b"testserv",
  979. response_headers={b"Cache-Control": b"max-age=1000"},
  980. content=b'{ "m.server": "target-server" }',
  981. )
  982. r = self.successResultOf(fetch_d)
  983. self.assertEqual(r.delegated_server, b"target-server")
  984. # close the tcp connection
  985. well_known_server.loseConnection()
  986. # repeat the request: it should hit the cache
  987. fetch_d = defer.ensureDeferred(
  988. self.well_known_resolver.get_well_known(b"testserv")
  989. )
  990. r = self.successResultOf(fetch_d)
  991. self.assertEqual(r.delegated_server, b"target-server")
  992. # expire the cache
  993. self.reactor.pump((1000.0,))
  994. # now it should connect again
  995. fetch_d = defer.ensureDeferred(
  996. self.well_known_resolver.get_well_known(b"testserv")
  997. )
  998. self.assertEqual(len(clients), 1)
  999. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1000. self.assertEqual(host, "1.2.3.4")
  1001. self.assertEqual(port, 443)
  1002. self._handle_well_known_connection(
  1003. client_factory,
  1004. expected_sni=b"testserv",
  1005. content=b'{ "m.server": "other-server" }',
  1006. )
  1007. r = self.successResultOf(fetch_d)
  1008. self.assertEqual(r.delegated_server, b"other-server")
  1009. def test_well_known_cache_with_temp_failure(self):
  1010. """Test that we refetch well-known before the cache expires, and that
  1011. it ignores transient errors.
  1012. """
  1013. self.reactor.lookups["testserv"] = "1.2.3.4"
  1014. fetch_d = defer.ensureDeferred(
  1015. self.well_known_resolver.get_well_known(b"testserv")
  1016. )
  1017. # there should be an attempt to connect on port 443 for the .well-known
  1018. clients = self.reactor.tcpClients
  1019. self.assertEqual(len(clients), 1)
  1020. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1021. self.assertEqual(host, "1.2.3.4")
  1022. self.assertEqual(port, 443)
  1023. well_known_server = self._handle_well_known_connection(
  1024. client_factory,
  1025. expected_sni=b"testserv",
  1026. response_headers={b"Cache-Control": b"max-age=1000"},
  1027. content=b'{ "m.server": "target-server" }',
  1028. )
  1029. r = self.successResultOf(fetch_d)
  1030. self.assertEqual(r.delegated_server, b"target-server")
  1031. # close the tcp connection
  1032. well_known_server.loseConnection()
  1033. # Get close to the cache expiry, this will cause the resolver to do
  1034. # another lookup.
  1035. self.reactor.pump((900.0,))
  1036. fetch_d = defer.ensureDeferred(
  1037. self.well_known_resolver.get_well_known(b"testserv")
  1038. )
  1039. # The resolver may retry a few times, so fonx all requests that come along
  1040. attempts = 0
  1041. while self.reactor.tcpClients:
  1042. clients = self.reactor.tcpClients
  1043. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1044. attempts += 1
  1045. # fonx the connection attempt, this will be treated as a temporary
  1046. # failure.
  1047. client_factory.clientConnectionFailed(None, Exception("nope"))
  1048. # There's a few sleeps involved, so we have to pump the reactor a
  1049. # bit.
  1050. self.reactor.pump((1.0, 1.0))
  1051. # We expect to see more than one attempt as there was previously a valid
  1052. # well known.
  1053. self.assertGreater(attempts, 1)
  1054. # Resolver should return cached value, despite the lookup failing.
  1055. r = self.successResultOf(fetch_d)
  1056. self.assertEqual(r.delegated_server, b"target-server")
  1057. # Expire both caches and repeat the request
  1058. self.reactor.pump((10000.0,))
  1059. # Repeat the request, this time it should fail if the lookup fails.
  1060. fetch_d = defer.ensureDeferred(
  1061. self.well_known_resolver.get_well_known(b"testserv")
  1062. )
  1063. clients = self.reactor.tcpClients
  1064. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1065. client_factory.clientConnectionFailed(None, Exception("nope"))
  1066. self.reactor.pump((0.4,))
  1067. r = self.successResultOf(fetch_d)
  1068. self.assertEqual(r.delegated_server, None)
  1069. def test_well_known_too_large(self):
  1070. """A well-known query that returns a result which is too large should be rejected."""
  1071. self.reactor.lookups["testserv"] = "1.2.3.4"
  1072. fetch_d = defer.ensureDeferred(
  1073. self.well_known_resolver.get_well_known(b"testserv")
  1074. )
  1075. # there should be an attempt to connect on port 443 for the .well-known
  1076. clients = self.reactor.tcpClients
  1077. self.assertEqual(len(clients), 1)
  1078. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1079. self.assertEqual(host, "1.2.3.4")
  1080. self.assertEqual(port, 443)
  1081. self._handle_well_known_connection(
  1082. client_factory,
  1083. expected_sni=b"testserv",
  1084. response_headers={b"Cache-Control": b"max-age=1000"},
  1085. content=b'{ "m.server": "' + (b"a" * WELL_KNOWN_MAX_SIZE) + b'" }',
  1086. )
  1087. # The result is successful, but disabled delegation.
  1088. r = self.successResultOf(fetch_d)
  1089. self.assertIsNone(r.delegated_server)
  1090. def test_srv_fallbacks(self):
  1091. """Test that other SRV results are tried if the first one fails."""
  1092. self.agent = self._make_agent()
  1093. self.mock_resolver.resolve_service.side_effect = generate_resolve_service(
  1094. [
  1095. Server(host=b"target.com", port=8443),
  1096. Server(host=b"target.com", port=8444),
  1097. ]
  1098. )
  1099. self.reactor.lookups["target.com"] = "1.2.3.4"
  1100. test_d = self._make_get_request(b"matrix://testserv/foo/bar")
  1101. # Nothing happened yet
  1102. self.assertNoResult(test_d)
  1103. self.mock_resolver.resolve_service.assert_called_once_with(
  1104. b"_matrix._tcp.testserv"
  1105. )
  1106. # We should see an attempt to connect to the first server
  1107. clients = self.reactor.tcpClients
  1108. self.assertEqual(len(clients), 1)
  1109. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1110. self.assertEqual(host, "1.2.3.4")
  1111. self.assertEqual(port, 8443)
  1112. # Fonx the connection
  1113. client_factory.clientConnectionFailed(None, Exception("nope"))
  1114. # There's a 300ms delay in HostnameEndpoint
  1115. self.reactor.pump((0.4,))
  1116. # Hasn't failed yet
  1117. self.assertNoResult(test_d)
  1118. # We shouldnow see an attempt to connect to the second server
  1119. clients = self.reactor.tcpClients
  1120. self.assertEqual(len(clients), 1)
  1121. (host, port, client_factory, _timeout, _bindAddress) = clients.pop(0)
  1122. self.assertEqual(host, "1.2.3.4")
  1123. self.assertEqual(port, 8444)
  1124. # make a test server, and wire up the client
  1125. http_server = self._make_connection(client_factory, expected_sni=b"testserv")
  1126. self.assertEqual(len(http_server.requests), 1)
  1127. request = http_server.requests[0]
  1128. self.assertEqual(request.method, b"GET")
  1129. self.assertEqual(request.path, b"/foo/bar")
  1130. self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"testserv"])
  1131. # finish the request
  1132. request.finish()
  1133. self.reactor.pump((0.1,))
  1134. self.successResultOf(test_d)
  1135. class TestCachePeriodFromHeaders(unittest.TestCase):
  1136. def test_cache_control(self):
  1137. # uppercase
  1138. self.assertEqual(
  1139. _cache_period_from_headers(
  1140. Headers({b"Cache-Control": [b"foo, Max-Age = 100, bar"]})
  1141. ),
  1142. 100,
  1143. )
  1144. # missing value
  1145. self.assertIsNone(
  1146. _cache_period_from_headers(Headers({b"Cache-Control": [b"max-age=, bar"]}))
  1147. )
  1148. # hackernews: bogus due to semicolon
  1149. self.assertIsNone(
  1150. _cache_period_from_headers(
  1151. Headers({b"Cache-Control": [b"private; max-age=0"]})
  1152. )
  1153. )
  1154. # github
  1155. self.assertEqual(
  1156. _cache_period_from_headers(
  1157. Headers({b"Cache-Control": [b"max-age=0, private, must-revalidate"]})
  1158. ),
  1159. 0,
  1160. )
  1161. # google
  1162. self.assertEqual(
  1163. _cache_period_from_headers(
  1164. Headers({b"cache-control": [b"private, max-age=0"]})
  1165. ),
  1166. 0,
  1167. )
  1168. def test_expires(self):
  1169. self.assertEqual(
  1170. _cache_period_from_headers(
  1171. Headers({b"Expires": [b"Wed, 30 Jan 2019 07:35:33 GMT"]}),
  1172. time_now=lambda: 1548833700,
  1173. ),
  1174. 33,
  1175. )
  1176. # cache-control overrides expires
  1177. self.assertEqual(
  1178. _cache_period_from_headers(
  1179. Headers(
  1180. {
  1181. b"cache-control": [b"max-age=10"],
  1182. b"Expires": [b"Wed, 30 Jan 2019 07:35:33 GMT"],
  1183. }
  1184. ),
  1185. time_now=lambda: 1548833700,
  1186. ),
  1187. 10,
  1188. )
  1189. # invalid expires means immediate expiry
  1190. self.assertEqual(_cache_period_from_headers(Headers({b"Expires": [b"0"]})), 0)
  1191. def _check_logcontext(context):
  1192. current = current_context()
  1193. if current is not context:
  1194. raise AssertionError("Expected logcontext %s but was %s" % (context, current))
  1195. def _wrap_server_factory_for_tls(
  1196. factory: IProtocolFactory, sanlist: Iterable[bytes] = None
  1197. ) -> IProtocolFactory:
  1198. """Wrap an existing Protocol Factory with a test TLSMemoryBIOFactory
  1199. The resultant factory will create a TLS server which presents a certificate
  1200. signed by our test CA, valid for the domains in `sanlist`
  1201. Args:
  1202. factory: protocol factory to wrap
  1203. sanlist: list of domains the cert should be valid for
  1204. Returns:
  1205. interfaces.IProtocolFactory
  1206. """
  1207. if sanlist is None:
  1208. sanlist = [
  1209. b"DNS:testserv",
  1210. b"DNS:target-server",
  1211. b"DNS:xn--bcher-kva.com",
  1212. b"IP:1.2.3.4",
  1213. b"IP:::1",
  1214. ]
  1215. connection_creator = TestServerTLSConnectionFactory(sanlist=sanlist)
  1216. return TLSMemoryBIOFactory(
  1217. connection_creator, isClient=False, wrappedFactory=factory
  1218. )
  1219. def _get_test_protocol_factory() -> IProtocolFactory:
  1220. """Get a protocol Factory which will build an HTTPChannel
  1221. Returns:
  1222. interfaces.IProtocolFactory
  1223. """
  1224. server_factory = Factory.forProtocol(HTTPChannel)
  1225. # Request.finish expects the factory to have a 'log' method.
  1226. server_factory.log = _log_request
  1227. return server_factory
  1228. def _log_request(request: str):
  1229. """Implements Factory.log, which is expected by Request.finish"""
  1230. logger.info(f"Completed request {request}")
  1231. @implementer(IPolicyForHTTPS)
  1232. class TrustingTLSPolicyForHTTPS:
  1233. """An IPolicyForHTTPS which checks that the certificate belongs to the
  1234. right server, but doesn't check the certificate chain."""
  1235. def creatorForNetloc(self, hostname, port):
  1236. certificateOptions = OpenSSLCertificateOptions()
  1237. return ClientTLSOptions(hostname, certificateOptions.getContext())