You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

182 lines
6.0 KiB

  1. # Copyright 2020 The Matrix.org Foundation C.I.C.
  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. from unittest.mock import Mock
  15. from netaddr import IPSet
  16. from twisted.internet import defer
  17. from twisted.internet.error import DNSLookupError
  18. from twisted.test.proto_helpers import MemoryReactor
  19. from synapse.http import RequestTimedOutError
  20. from synapse.http.client import SimpleHttpClient
  21. from synapse.server import HomeServer
  22. from synapse.util import Clock
  23. from tests.unittest import HomeserverTestCase
  24. class SimpleHttpClientTests(HomeserverTestCase):
  25. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: "HomeServer") -> None:
  26. # Add a DNS entry for a test server
  27. self.reactor.lookups["testserv"] = "1.2.3.4"
  28. self.cl = hs.get_simple_http_client()
  29. def test_dns_error(self) -> None:
  30. """
  31. If the DNS lookup returns an error, it will bubble up.
  32. """
  33. d = defer.ensureDeferred(self.cl.get_json("http://testserv2:8008/foo/bar"))
  34. self.pump()
  35. f = self.failureResultOf(d)
  36. self.assertIsInstance(f.value, DNSLookupError)
  37. def test_client_connection_refused(self) -> None:
  38. d = defer.ensureDeferred(self.cl.get_json("http://testserv:8008/foo/bar"))
  39. self.pump()
  40. # Nothing happened yet
  41. self.assertNoResult(d)
  42. clients = self.reactor.tcpClients
  43. self.assertEqual(len(clients), 1)
  44. (host, port, factory, _timeout, _bindAddress) = clients[0]
  45. self.assertEqual(host, "1.2.3.4")
  46. self.assertEqual(port, 8008)
  47. e = Exception("go away")
  48. factory.clientConnectionFailed(None, e)
  49. self.pump(0.5)
  50. f = self.failureResultOf(d)
  51. self.assertIs(f.value, e)
  52. def test_client_never_connect(self) -> None:
  53. """
  54. If the HTTP request is not connected and is timed out, it'll give a
  55. ConnectingCancelledError or TimeoutError.
  56. """
  57. d = defer.ensureDeferred(self.cl.get_json("http://testserv:8008/foo/bar"))
  58. self.pump()
  59. # Nothing happened yet
  60. self.assertNoResult(d)
  61. # Make sure treq is trying to connect
  62. clients = self.reactor.tcpClients
  63. self.assertEqual(len(clients), 1)
  64. self.assertEqual(clients[0][0], "1.2.3.4")
  65. self.assertEqual(clients[0][1], 8008)
  66. # Deferred is still without a result
  67. self.assertNoResult(d)
  68. # Push by enough to time it out
  69. self.reactor.advance(120)
  70. f = self.failureResultOf(d)
  71. self.assertIsInstance(f.value, RequestTimedOutError)
  72. def test_client_connect_no_response(self) -> None:
  73. """
  74. If the HTTP request is connected, but gets no response before being
  75. timed out, it'll give a ResponseNeverReceived.
  76. """
  77. d = defer.ensureDeferred(self.cl.get_json("http://testserv:8008/foo/bar"))
  78. self.pump()
  79. # Nothing happened yet
  80. self.assertNoResult(d)
  81. # Make sure treq is trying to connect
  82. clients = self.reactor.tcpClients
  83. self.assertEqual(len(clients), 1)
  84. self.assertEqual(clients[0][0], "1.2.3.4")
  85. self.assertEqual(clients[0][1], 8008)
  86. conn = Mock()
  87. client = clients[0][2].buildProtocol(None)
  88. client.makeConnection(conn)
  89. # Deferred is still without a result
  90. self.assertNoResult(d)
  91. # Push by enough to time it out
  92. self.reactor.advance(120)
  93. f = self.failureResultOf(d)
  94. self.assertIsInstance(f.value, RequestTimedOutError)
  95. def test_client_ip_range_blocklist(self) -> None:
  96. """Ensure that Synapse does not try to connect to blocked IPs"""
  97. # Add some DNS entries we'll block
  98. self.reactor.lookups["internal"] = "127.0.0.1"
  99. self.reactor.lookups["internalv6"] = "fe80:0:0:0:0:8a2e:370:7337"
  100. ip_blocklist = IPSet(["127.0.0.0/8", "fe80::/64"])
  101. cl = SimpleHttpClient(self.hs, ip_blocklist=ip_blocklist)
  102. # Try making a GET request to a blocked IPv4 address
  103. # ------------------------------------------------------
  104. # Make the request
  105. d = defer.ensureDeferred(cl.get_json("http://internal:8008/foo/bar"))
  106. self.pump(1)
  107. # Check that it was unable to resolve the address
  108. clients = self.reactor.tcpClients
  109. self.assertEqual(len(clients), 0)
  110. self.failureResultOf(d, DNSLookupError)
  111. # Try making a POST request to a blocked IPv6 address
  112. # -------------------------------------------------------
  113. # Make the request
  114. d = defer.ensureDeferred(
  115. cl.post_json_get_json("http://internalv6:8008/foo/bar", {})
  116. )
  117. # Move the reactor forwards
  118. self.pump(1)
  119. # Check that it was unable to resolve the address
  120. clients = self.reactor.tcpClients
  121. self.assertEqual(len(clients), 0)
  122. # Check that it was due to a blocked DNS lookup
  123. self.failureResultOf(d, DNSLookupError)
  124. # Try making a GET request to a non-blocked IPv4 address
  125. # ----------------------------------------------------------
  126. # Make the request
  127. d = defer.ensureDeferred(cl.get_json("http://testserv:8008/foo/bar"))
  128. # Nothing has happened yet
  129. self.assertNoResult(d)
  130. # Move the reactor forwards
  131. self.pump(1)
  132. # Check that it was able to resolve the address
  133. clients = self.reactor.tcpClients
  134. self.assertNotEqual(len(clients), 0)
  135. # Connection will still fail as this IP address does not resolve to anything
  136. self.failureResultOf(d, RequestTimedOutError)