Skip to content

Commit 4744fc0

Browse files
committed
Fix bind unspecified address controller will raise TimeoutError
The controller throws a TimeoutError when binding a unspecified address (e.g. 0.0.0.0)
1 parent 215b854 commit 4744fc0

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

aiosmtpd/controller.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ def get_localhost() -> Literal["::1", "127.0.0.1"]:
7979
raise
8080

8181

82+
@public
83+
def is_unspecified_address(address: str) -> bool:
84+
unspecified_address_list = [None, '', '0.0.0.0', '::']
85+
return address in unspecified_address_list
86+
87+
8288
class _FakeServer(asyncio.StreamReaderProtocol):
8389
"""
8490
Returned by _factory_invoker() in lieu of an SMTP instance in case
@@ -421,7 +427,7 @@ def _trigger_server(self):
421427
"""
422428
# At this point, if self.hostname is Falsy, it most likely is "" (bind to all
423429
# addresses). In such case, it should be safe to connect to localhost)
424-
hostname = self.hostname or self._localhost
430+
hostname = self._localhost if is_unspecified_address(self.hostname) is True else self.hostname
425431
with ExitStack() as stk:
426432
s = stk.enter_context(create_connection((hostname, self.port), 1.0))
427433
if self.ssl_context:

aiosmtpd/docs/NEWS.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Fixed/Improved
1414
--------------
1515
* All Controllers now have more rationale design, as they are now composited from a Base + a Mixin
1616
* A whole bunch of annotations
17-
17+
* Fix bind unspecified address controller will raise TimeoutError
1818

1919
1.4.2 (2021-03-08)
2020
=====================

aiosmtpd/tests/test_server.py

+14
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
UnixSocketUnthreadedController,
2929
_FakeServer,
3030
get_localhost,
31+
is_unspecified_address,
3132
)
3233
from aiosmtpd.handlers import Sink
3334
from aiosmtpd.smtp import SMTP as Server
@@ -292,6 +293,13 @@ def test_hostname_none(self):
292293
cont.start()
293294
finally:
294295
cont.stop()
296+
297+
def test_hostname_unspecified(self):
298+
cont = Controller(Sink(), hostname="0.0.0.0")
299+
try:
300+
cont.start()
301+
finally:
302+
cont.stop()
295303

296304
def test_testconn_raises(self, mocker: MockFixture):
297305
mocker.patch("socket.socket.recv", side_effect=RuntimeError("MockError"))
@@ -347,6 +355,12 @@ def test_getlocalhost_error(self, mocker):
347355
assert exc.value.errno == errno.EFAULT
348356
mock_makesock.assert_called_with(socket.AF_INET6, socket.SOCK_STREAM)
349357

358+
def test_is_unspecified_address(self):
359+
assert is_unspecified_address("127.0.0.1") is False
360+
assert is_unspecified_address("0.0.0.0") is True
361+
assert is_unspecified_address("::") is True
362+
assert is_unspecified_address("") is True
363+
350364
def test_stop_default(self):
351365
controller = Controller(Sink())
352366
with pytest.raises(AssertionError, match="SMTP daemon not running"):

0 commit comments

Comments
 (0)