Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fe56a3

Browse files
committed
#14758: add IPv6 support to smtpd.
Patch by Milan Oberkirch.
1 parent 1144da5 commit 6fe56a3

5 files changed

Lines changed: 58 additions & 15 deletions

File tree

Doc/library/smtpd.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ SMTPServer Objects
6868
.. versionchanged:: 3.4
6969
The *map* argument was added.
7070

71-
.. versionchanged:: 3.5
72-
the *decode_data* argument was added.
71+
.. versionchanged:: 3.5 the *decode_data* argument was added, and *localaddr*
72+
and *remoteaddr* may now contain IPv6 addresses.
7373

7474

7575
DebuggingServer Objects

Doc/whatsnew/3.5.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ smtpd
194194
is ``True`` for backward compatibility reasons, but will change to ``False``
195195
in Python 3.6. (Contributed by Maciej Szulik in :issue:`19662`.)
196196

197+
* It is now possible to provide, directly or via name resolution, IPv6
198+
addresses in the :class:`~smtpd.SMTPServer` constructor, and have it
199+
successfully connect. (Contributed by Milan Oberkirch in :issue:`14758`.)
200+
197201
socket
198202
------
199203

Lib/smtpd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,8 @@ def __init__(self, localaddr, remoteaddr,
610610
self._decode_data = decode_data
611611
asyncore.dispatcher.__init__(self, map=map)
612612
try:
613-
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
613+
gai_results = socket.getaddrinfo(*localaddr)
614+
self.create_socket(gai_results[0][0], gai_results[0][1])
614615
# try to re-use a server port if possible
615616
self.set_reuse_addr()
616617
self.bind(localaddr)

Lib/test/mock_socket.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ def close(self):
3535
class MockSocket:
3636
"""Mock socket object used by smtpd and smtplib tests.
3737
"""
38-
def __init__(self):
38+
def __init__(self, family=None):
3939
global _reply_data
40+
self.family = family
4041
self.output = []
4142
self.lines = []
4243
if _reply_data:
@@ -108,8 +109,7 @@ def close(self):
108109

109110

110111
def socket(family=None, type=None, proto=None):
111-
return MockSocket()
112-
112+
return MockSocket(family)
113113

114114
def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT,
115115
source_address=None):
@@ -144,13 +144,16 @@ def gethostname():
144144
def gethostbyname(name):
145145
return ""
146146

147+
def getaddrinfo(host, port):
148+
return socket_module.getaddrinfo(host, port)
147149

148150
gaierror = socket_module.gaierror
149151
error = socket_module.error
150152

151153

152154
# Constants
153-
AF_INET = None
155+
AF_INET = socket_module.AF_INET
156+
AF_INET6 = socket_module.AF_INET6
154157
SOCK_STREAM = None
155158
SOL_SOCKET = None
156159
SO_REUSEADDR = None

Lib/test/test_smtpd.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def setUp(self):
3636
smtpd.socket = asyncore.socket = mock_socket
3737

3838
def test_process_message_unimplemented(self):
39-
server = smtpd.SMTPServer('a', 'b', decode_data=True)
39+
server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
40+
decode_data=True)
4041
conn, addr = server.accept()
4142
channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
4243

@@ -52,19 +53,39 @@ def write_line(line):
5253

5354
def test_decode_data_default_warns(self):
5455
with self.assertWarns(DeprecationWarning):
55-
smtpd.SMTPServer('a', 'b')
56+
smtpd.SMTPServer((support.HOST, 0), ('b', 0))
5657

5758
def tearDown(self):
5859
asyncore.close_all()
5960
asyncore.socket = smtpd.socket = socket
6061

6162

63+
class TestFamilyDetection(unittest.TestCase):
64+
def setUp(self):
65+
smtpd.socket = asyncore.socket = mock_socket
66+
67+
def tearDown(self):
68+
asyncore.close_all()
69+
asyncore.socket = smtpd.socket = socket
70+
71+
@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
72+
def test_socket_uses_IPv6(self):
73+
server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0),
74+
decode_data=False)
75+
self.assertEqual(server.socket.family, socket.AF_INET6)
76+
77+
def test_socket_uses_IPv4(self):
78+
server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0),
79+
decode_data=False)
80+
self.assertEqual(server.socket.family, socket.AF_INET)
81+
82+
6283
class SMTPDChannelTest(unittest.TestCase):
6384
def setUp(self):
6485
smtpd.socket = asyncore.socket = mock_socket
6586
self.old_debugstream = smtpd.DEBUGSTREAM
6687
self.debug = smtpd.DEBUGSTREAM = io.StringIO()
67-
self.server = DummyServer('a', 'b')
88+
self.server = DummyServer((support.HOST, 0), ('b', 0))
6889
conn, addr = self.server.accept()
6990
self.channel = smtpd.SMTPChannel(self.server, conn, addr,
7091
decode_data=True)
@@ -79,7 +100,9 @@ def write_line(self, line):
79100
self.channel.handle_read()
80101

81102
def test_broken_connect(self):
82-
self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b')
103+
self.assertRaises(
104+
DummyDispatcherBroken, BrokenDummyServer,
105+
(support.HOST, 0), ('b', 0))
83106

84107
def test_server_accept(self):
85108
self.server.handle_accept()
@@ -513,19 +536,29 @@ def test_attribute_deprecations(self):
513536
self.channel._SMTPChannel__addr = 'spam'
514537

515538
def test_decode_data_default_warning(self):
516-
server = DummyServer('a', 'b')
539+
server = DummyServer((support.HOST, 0), ('b', 0))
517540
conn, addr = self.server.accept()
518541
with self.assertWarns(DeprecationWarning):
519542
smtpd.SMTPChannel(server, conn, addr)
520543

544+
@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
545+
class SMTPDChannelIPv6Test(SMTPDChannelTest):
546+
def setUp(self):
547+
smtpd.socket = asyncore.socket = mock_socket
548+
self.old_debugstream = smtpd.DEBUGSTREAM
549+
self.debug = smtpd.DEBUGSTREAM = io.StringIO()
550+
self.server = DummyServer((support.HOSTv6, 0), ('b', 0))
551+
conn, addr = self.server.accept()
552+
self.channel = smtpd.SMTPChannel(self.server, conn, addr,
553+
decode_data=True)
521554

522555
class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
523556

524557
def setUp(self):
525558
smtpd.socket = asyncore.socket = mock_socket
526559
self.old_debugstream = smtpd.DEBUGSTREAM
527560
self.debug = smtpd.DEBUGSTREAM = io.StringIO()
528-
self.server = DummyServer('a', 'b')
561+
self.server = DummyServer((support.HOST, 0), ('b', 0))
529562
conn, addr = self.server.accept()
530563
# Set DATA size limit to 32 bytes for easy testing
531564
self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32,
@@ -576,7 +609,8 @@ def setUp(self):
576609
smtpd.socket = asyncore.socket = mock_socket
577610
self.old_debugstream = smtpd.DEBUGSTREAM
578611
self.debug = smtpd.DEBUGSTREAM = io.StringIO()
579-
self.server = DummyServer('a', 'b', decode_data=False)
612+
self.server = DummyServer((support.HOST, 0), ('b', 0),
613+
decode_data=False)
580614
conn, addr = self.server.accept()
581615
# Set decode_data to False
582616
self.channel = smtpd.SMTPChannel(self.server, conn, addr,
@@ -620,7 +654,8 @@ def setUp(self):
620654
smtpd.socket = asyncore.socket = mock_socket
621655
self.old_debugstream = smtpd.DEBUGSTREAM
622656
self.debug = smtpd.DEBUGSTREAM = io.StringIO()
623-
self.server = DummyServer('a', 'b')
657+
self.server = DummyServer((support.HOST, 0), ('b', 0),
658+
decode_data=True)
624659
conn, addr = self.server.accept()
625660
# Set decode_data to True
626661
self.channel = smtpd.SMTPChannel(self.server, conn, addr,

0 commit comments

Comments
 (0)