Skip to content

Commit 8b0994e

Browse files
committed
Backport of socket.has_dualstack_ipv6() implementation (python 3.8)
1 parent bc37725 commit 8b0994e

File tree

3 files changed

+30
-22
lines changed

3 files changed

+30
-22
lines changed

pyftpdlib/handlers.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import traceback
1616
from datetime import datetime
1717

18+
from .utils import has_dualstack_ipv6
19+
1820
try:
1921
import grp
2022
import pwd
@@ -336,24 +338,6 @@ def _is_ssl_sock(sock):
336338
return SSL is not None and isinstance(sock, SSL.Connection)
337339

338340

339-
def _support_hybrid_ipv6():
340-
"""Return True if it is possible to use hybrid IPv6/IPv4 sockets
341-
on this platform.
342-
"""
343-
# Note: IPPROTO_IPV6 constant is broken on Windows, see:
344-
# https://bugs.python.org/issue6926
345-
try:
346-
if not socket.has_ipv6:
347-
return False
348-
with contextlib.closing(socket.socket(socket.AF_INET6)) as sock:
349-
return not sock.getsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY)
350-
except (OSError, AttributeError):
351-
return False
352-
353-
354-
SUPPORTS_HYBRID_IPV6 = _support_hybrid_ipv6()
355-
356-
357341
class _FileReadWriteError(OSError):
358342
"""Exception raised when reading or writing a file during a transfer."""
359343

@@ -2195,7 +2179,7 @@ def ftp_EPRT(self, line):
21952179
# test if AF_INET6 and IPV6_V6ONLY
21962180
if (
21972181
self.socket.family == socket.AF_INET6
2198-
and not SUPPORTS_HYBRID_IPV6
2182+
and not has_dualstack_ipv6()
21992183
):
22002184
self.respond("522 Network protocol not supported (use 2).")
22012185
else:

pyftpdlib/utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
# Use of this source code is governed by MIT license that can be
33
# found in the LICENSE file.
44

5+
import contextlib
56
import os
7+
import socket
68
import sys
79

810

@@ -73,3 +75,25 @@ def strerror(err):
7375
if isinstance(err, OSError):
7476
return os.strerror(err.errno)
7577
return str(err)
78+
79+
80+
# backport of Python 3.8 socket.has_dualstack_ipv6()
81+
@memoize
82+
def has_dualstack_ipv6():
83+
"""Return True if the platform supports creating a SOCK_STREAM socket
84+
which can handle both AF_INET and AF_INET6 (IPv4 / IPv6) connections.
85+
"""
86+
if (
87+
not socket.has_ipv6
88+
or not hasattr(socket, "IPPROTO_IPV6")
89+
or not hasattr(socket, "IPV6_V6ONLY")
90+
):
91+
return False
92+
try:
93+
with contextlib.closing(
94+
socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
95+
) as sock:
96+
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
97+
return True
98+
except OSError:
99+
return False

tests/test_functional.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
import pytest
2222

2323
from pyftpdlib.filesystems import AbstractedFS
24-
from pyftpdlib.handlers import SUPPORTS_HYBRID_IPV6
2524
from pyftpdlib.handlers import DTPHandler
2625
from pyftpdlib.handlers import FTPHandler
2726
from pyftpdlib.handlers import ThrottledDTPHandler
2827
from pyftpdlib.ioloop import IOLoop
2928
from pyftpdlib.servers import FTPServer
29+
from pyftpdlib.utils import has_dualstack_ipv6
3030

3131
from . import BUFSIZE
3232
from . import CI_TESTING
@@ -2130,7 +2130,7 @@ def cmdresp(self, cmd):
21302130

21312131
@disable_log_warning
21322132
def test_eprt(self):
2133-
if not SUPPORTS_HYBRID_IPV6:
2133+
if not has_dualstack_ipv6():
21342134
# test wrong proto
21352135
with pytest.raises(ftplib.error_perm, match="522"):
21362136
self.client.sendcmd(
@@ -2294,7 +2294,7 @@ def test_eprt_v6(self):
22942294

22952295

22962296
@pytest.mark.skipif(
2297-
not SUPPORTS_HYBRID_IPV6, reason="IPv4/6 dual stack not supported"
2297+
not has_dualstack_ipv6(), reason="IPv4/6 dual stack not supported"
22982298
)
22992299
class TestIPv6MixedEnvironment(PyftpdlibTestCase):
23002300
"""By running the server by specifying "::" as IP address the

0 commit comments

Comments
 (0)