[Python-checkins] cpython: #14758: add IPv6 support to smtpd.

r.david.murray python-checkins at python.org
Wed Jun 11 19:49:14 CEST 2014


http://hg.python.org/cpython/rev/1efbc86a200a
changeset:   91134:1efbc86a200a
user:        R David Murray <rdmurray at bitdance.com>
date:        Wed Jun 11 13:48:58 2014 -0400
summary:
  #14758: add IPv6 support to smtpd.

Patch by Milan Oberkirch.

files:
  Doc/library/smtpd.rst   |   4 +-
  Doc/whatsnew/3.5.rst    |   4 ++
  Lib/smtpd.py            |   3 +-
  Lib/test/mock_socket.py |  11 +++--
  Lib/test/test_smtpd.py  |  51 ++++++++++++++++++++++++----
  5 files changed, 58 insertions(+), 15 deletions(-)


diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst
--- a/Doc/library/smtpd.rst
+++ b/Doc/library/smtpd.rst
@@ -68,8 +68,8 @@
    .. versionchanged:: 3.4
       The *map* argument was added.
 
-   .. versionchanged:: 3.5
-      the *decode_data* argument was added.
+   .. versionchanged:: 3.5 the *decode_data* argument was added, and *localaddr*
+      and *remoteaddr* may now contain IPv6 addresses.
 
 
 DebuggingServer Objects
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -194,6 +194,10 @@
   is ``True`` for backward compatibility reasons, but will change to ``False``
   in Python 3.6.  (Contributed by Maciej Szulik in :issue:`19662`.)
 
+* It is now possible to provide, directly or via name resolution, IPv6
+  addresses in the :class:`~smtpd.SMTPServer` constructor, and have it
+  successfully connect.  (Contributed by Milan Oberkirch in :issue:`14758`.)
+
 socket
 ------
 
diff --git a/Lib/smtpd.py b/Lib/smtpd.py
--- a/Lib/smtpd.py
+++ b/Lib/smtpd.py
@@ -610,7 +610,8 @@
         self._decode_data = decode_data
         asyncore.dispatcher.__init__(self, map=map)
         try:
-            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+            gai_results = socket.getaddrinfo(*localaddr)
+            self.create_socket(gai_results[0][0], gai_results[0][1])
             # try to re-use a server port if possible
             self.set_reuse_addr()
             self.bind(localaddr)
diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py
--- a/Lib/test/mock_socket.py
+++ b/Lib/test/mock_socket.py
@@ -35,8 +35,9 @@
 class MockSocket:
     """Mock socket object used by smtpd and smtplib tests.
     """
-    def __init__(self):
+    def __init__(self, family=None):
         global _reply_data
+        self.family = family
         self.output = []
         self.lines = []
         if _reply_data:
@@ -108,8 +109,7 @@
 
 
 def socket(family=None, type=None, proto=None):
-    return MockSocket()
-
+    return MockSocket(family)
 
 def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT,
                       source_address=None):
@@ -144,13 +144,16 @@
 def gethostbyname(name):
     return ""
 
+def getaddrinfo(host, port):
+    return socket_module.getaddrinfo(host, port)
 
 gaierror = socket_module.gaierror
 error = socket_module.error
 
 
 # Constants
-AF_INET = None
+AF_INET = socket_module.AF_INET
+AF_INET6 = socket_module.AF_INET6
 SOCK_STREAM = None
 SOL_SOCKET = None
 SO_REUSEADDR = None
diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py
--- a/Lib/test/test_smtpd.py
+++ b/Lib/test/test_smtpd.py
@@ -36,7 +36,8 @@
         smtpd.socket = asyncore.socket = mock_socket
 
     def test_process_message_unimplemented(self):
-        server = smtpd.SMTPServer('a', 'b', decode_data=True)
+        server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
+                                  decode_data=True)
         conn, addr = server.accept()
         channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
 
@@ -52,19 +53,39 @@
 
     def test_decode_data_default_warns(self):
         with self.assertWarns(DeprecationWarning):
-            smtpd.SMTPServer('a', 'b')
+            smtpd.SMTPServer((support.HOST, 0), ('b', 0))
 
     def tearDown(self):
         asyncore.close_all()
         asyncore.socket = smtpd.socket = socket
 
 
+class TestFamilyDetection(unittest.TestCase):
+    def setUp(self):
+        smtpd.socket = asyncore.socket = mock_socket
+
+    def tearDown(self):
+        asyncore.close_all()
+        asyncore.socket = smtpd.socket = socket
+
+    @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
+    def test_socket_uses_IPv6(self):
+        server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0),
+                                  decode_data=False)
+        self.assertEqual(server.socket.family, socket.AF_INET6)
+
+    def test_socket_uses_IPv4(self):
+        server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0),
+                                  decode_data=False)
+        self.assertEqual(server.socket.family, socket.AF_INET)
+
+
 class SMTPDChannelTest(unittest.TestCase):
     def setUp(self):
         smtpd.socket = asyncore.socket = mock_socket
         self.old_debugstream = smtpd.DEBUGSTREAM
         self.debug = smtpd.DEBUGSTREAM = io.StringIO()
-        self.server = DummyServer('a', 'b')
+        self.server = DummyServer((support.HOST, 0), ('b', 0))
         conn, addr = self.server.accept()
         self.channel = smtpd.SMTPChannel(self.server, conn, addr,
                                          decode_data=True)
@@ -79,7 +100,9 @@
         self.channel.handle_read()
 
     def test_broken_connect(self):
-        self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b')
+        self.assertRaises(
+            DummyDispatcherBroken, BrokenDummyServer,
+            (support.HOST, 0), ('b', 0))
 
     def test_server_accept(self):
         self.server.handle_accept()
@@ -513,11 +536,21 @@
             self.channel._SMTPChannel__addr = 'spam'
 
     def test_decode_data_default_warning(self):
-        server = DummyServer('a', 'b')
+        server = DummyServer((support.HOST, 0), ('b', 0))
         conn, addr = self.server.accept()
         with self.assertWarns(DeprecationWarning):
             smtpd.SMTPChannel(server, conn, addr)
 
+ at unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
+class SMTPDChannelIPv6Test(SMTPDChannelTest):
+    def setUp(self):
+        smtpd.socket = asyncore.socket = mock_socket
+        self.old_debugstream = smtpd.DEBUGSTREAM
+        self.debug = smtpd.DEBUGSTREAM = io.StringIO()
+        self.server = DummyServer((support.HOSTv6, 0), ('b', 0))
+        conn, addr = self.server.accept()
+        self.channel = smtpd.SMTPChannel(self.server, conn, addr,
+                                         decode_data=True)
 
 class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
 
@@ -525,7 +558,7 @@
         smtpd.socket = asyncore.socket = mock_socket
         self.old_debugstream = smtpd.DEBUGSTREAM
         self.debug = smtpd.DEBUGSTREAM = io.StringIO()
-        self.server = DummyServer('a', 'b')
+        self.server = DummyServer((support.HOST, 0), ('b', 0))
         conn, addr = self.server.accept()
         # Set DATA size limit to 32 bytes for easy testing
         self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32,
@@ -576,7 +609,8 @@
         smtpd.socket = asyncore.socket = mock_socket
         self.old_debugstream = smtpd.DEBUGSTREAM
         self.debug = smtpd.DEBUGSTREAM = io.StringIO()
-        self.server = DummyServer('a', 'b', decode_data=False)
+        self.server = DummyServer((support.HOST, 0), ('b', 0),
+                                  decode_data=False)
         conn, addr = self.server.accept()
         # Set decode_data to False
         self.channel = smtpd.SMTPChannel(self.server, conn, addr,
@@ -620,7 +654,8 @@
         smtpd.socket = asyncore.socket = mock_socket
         self.old_debugstream = smtpd.DEBUGSTREAM
         self.debug = smtpd.DEBUGSTREAM = io.StringIO()
-        self.server = DummyServer('a', 'b')
+        self.server = DummyServer((support.HOST, 0), ('b', 0),
+                                  decode_data=True)
         conn, addr = self.server.accept()
         # Set decode_data to True
         self.channel = smtpd.SMTPChannel(self.server, conn, addr,

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list