1313# limitations under the License.
1414
1515
16- from typing import Callable , List , Tuple
16+ from typing import Callable , List , Tuple , Type , Union
17+ from unittest .mock import patch
1718
1819from zope .interface import implementer
1920
2021from twisted .internet import defer
21- from twisted .internet .address import IPv4Address
22+ from twisted .internet ._sslverify import ClientTLSOptions
23+ from twisted .internet .address import IPv4Address , IPv6Address
2224from twisted .internet .defer import ensureDeferred
25+ from twisted .internet .interfaces import IProtocolFactory
26+ from twisted .internet .ssl import ContextFactory
2327from twisted .mail import interfaces , smtp
2428
2529from tests .server import FakeTransport
2630from tests .unittest import HomeserverTestCase , override_config
2731
2832
33+ def TestingESMTPTLSClientFactory (
34+ contextFactory : ContextFactory ,
35+ _connectWrapped : bool ,
36+ wrappedProtocol : IProtocolFactory ,
37+ ) -> IProtocolFactory :
38+ """We use this to pass through in testing without using TLS, but
39+ saving the context information to check that it would have happened.
40+
41+ Note that this is what the MemoryReactor does on connectSSL.
42+ It only saves the contextFactory, but starts the connection with the
43+ underlying Factory.
44+ See: L{twisted.internet.testing.MemoryReactor.connectSSL}"""
45+
46+ wrappedProtocol ._testingContextFactory = contextFactory # type: ignore[attr-defined]
47+ return wrappedProtocol
48+
49+
2950@implementer (interfaces .IMessageDelivery )
3051class _DummyMessageDelivery :
3152 def __init__ (self ) -> None :
@@ -75,7 +96,13 @@ def connectionLost(self) -> None:
7596 pass
7697
7798
78- class SendEmailHandlerTestCase (HomeserverTestCase ):
99+ class SendEmailHandlerTestCaseIPv4 (HomeserverTestCase ):
100+ ip_class : Union [Type [IPv4Address ], Type [IPv6Address ]] = IPv4Address
101+
102+ def setUp (self ) -> None :
103+ super ().setUp ()
104+ self .reactor .lookups ["localhost" ] = "127.0.0.1"
105+
79106 def test_send_email (self ) -> None :
80107 """Happy-path test that we can send email to a non-TLS server."""
81108 h = self .hs .get_send_email_handler ()
@@ -89,7 +116,7 @@ def test_send_email(self) -> None:
89116 (host , port , client_factory , _timeout , _bindAddress ) = self .reactor .tcpClients [
90117 0
91118 ]
92- self .assertEqual (host , "localhost" )
119+ self .assertEqual (host , self . reactor . lookups [ "localhost" ] )
93120 self .assertEqual (port , 25 )
94121
95122 # wire it up to an SMTP server
@@ -105,7 +132,9 @@ def test_send_email(self) -> None:
105132 FakeTransport (
106133 client_protocol ,
107134 self .reactor ,
108- peer_address = IPv4Address ("TCP" , "127.0.0.1" , 1234 ),
135+ peer_address = self .ip_class (
136+ "TCP" , self .reactor .lookups ["localhost" ], 1234
137+ ),
109138 )
110139 )
111140
@@ -118,6 +147,10 @@ def test_send_email(self) -> None:
118147 self .
assertEqual (
str (
user ),
"[email protected] " )
119148 self .assertIn (b"Subject: test subject" , msg )
120149
150+ @patch (
151+ "synapse.handlers.send_email.TLSMemoryBIOFactory" ,
152+ TestingESMTPTLSClientFactory ,
153+ )
121154 @override_config (
122155 {
123156 "email" : {
@@ -135,17 +168,23 @@ def test_send_email_force_tls(self) -> None:
135168 )
136169 )
137170 # there should be an attempt to connect to localhost:465
138- self .assertEqual (len (self .reactor .sslClients ), 1 )
171+ self .assertEqual (len (self .reactor .tcpClients ), 1 )
139172 (
140173 host ,
141174 port ,
142175 client_factory ,
143- contextFactory ,
144176 _timeout ,
145177 _bindAddress ,
146- ) = self .reactor .sslClients [0 ]
147- self .assertEqual (host , "localhost" )
178+ ) = self .reactor .tcpClients [0 ]
179+ self .assertEqual (host , self . reactor . lookups [ "localhost" ] )
148180 self .assertEqual (port , 465 )
181+ # We need to make sure that TLS is happenning
182+ self .assertIsInstance (
183+ client_factory ._wrappedFactory ._testingContextFactory ,
184+ ClientTLSOptions ,
185+ )
186+ # And since we use endpoints, they go through reactor.connectTCP
187+ # which works differently to connectSSL on the testing reactor
149188
150189 # wire it up to an SMTP server
151190 message_delivery = _DummyMessageDelivery ()
@@ -160,7 +199,9 @@ def test_send_email_force_tls(self) -> None:
160199 FakeTransport (
161200 client_protocol ,
162201 self .reactor ,
163- peer_address = IPv4Address ("TCP" , "127.0.0.1" , 1234 ),
202+ peer_address = self .ip_class (
203+ "TCP" , self .reactor .lookups ["localhost" ], 1234
204+ ),
164205 )
165206 )
166207
@@ -172,3 +213,11 @@ def test_send_email_force_tls(self) -> None:
172213 user , msg = message_delivery .messages .pop ()
173214 self .
assertEqual (
str (
user ),
"[email protected] " )
174215 self .assertIn (b"Subject: test subject" , msg )
216+
217+
218+ class SendEmailHandlerTestCaseIPv6 (SendEmailHandlerTestCaseIPv4 ):
219+ ip_class = IPv6Address
220+
221+ def setUp (self ) -> None :
222+ super ().setUp ()
223+ self .reactor .lookups ["localhost" ] = "::1"
0 commit comments