17db96d56Sopenharmony_ciimport contextlib 27db96d56Sopenharmony_ciimport errno 37db96d56Sopenharmony_ciimport socket 47db96d56Sopenharmony_ciimport unittest 57db96d56Sopenharmony_ciimport sys 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_cifrom .. import support 87db96d56Sopenharmony_cifrom . import warnings_helper 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ciHOST = "localhost" 117db96d56Sopenharmony_ciHOSTv4 = "127.0.0.1" 127db96d56Sopenharmony_ciHOSTv6 = "::1" 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_ci# WASI SDK 15.0 does not provide gethostname, stub raises OSError ENOTSUP. 157db96d56Sopenharmony_cihas_gethostname = not support.is_wasi 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_cidef find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): 197db96d56Sopenharmony_ci """Returns an unused port that should be suitable for binding. This is 207db96d56Sopenharmony_ci achieved by creating a temporary socket with the same family and type as 217db96d56Sopenharmony_ci the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to 227db96d56Sopenharmony_ci the specified host address (defaults to 0.0.0.0) with the port set to 0, 237db96d56Sopenharmony_ci eliciting an unused ephemeral port from the OS. The temporary socket is 247db96d56Sopenharmony_ci then closed and deleted, and the ephemeral port is returned. 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_ci Either this method or bind_port() should be used for any tests where a 277db96d56Sopenharmony_ci server socket needs to be bound to a particular port for the duration of 287db96d56Sopenharmony_ci the test. Which one to use depends on whether the calling code is creating 297db96d56Sopenharmony_ci a python socket, or if an unused port needs to be provided in a constructor 307db96d56Sopenharmony_ci or passed to an external program (i.e. the -accept argument to openssl's 317db96d56Sopenharmony_ci s_server mode). Always prefer bind_port() over find_unused_port() where 327db96d56Sopenharmony_ci possible. Hard coded ports should *NEVER* be used. As soon as a server 337db96d56Sopenharmony_ci socket is bound to a hard coded port, the ability to run multiple instances 347db96d56Sopenharmony_ci of the test simultaneously on the same host is compromised, which makes the 357db96d56Sopenharmony_ci test a ticking time bomb in a buildbot environment. On Unix buildbots, this 367db96d56Sopenharmony_ci may simply manifest as a failed test, which can be recovered from without 377db96d56Sopenharmony_ci intervention in most cases, but on Windows, the entire python process can 387db96d56Sopenharmony_ci completely and utterly wedge, requiring someone to log in to the buildbot 397db96d56Sopenharmony_ci and manually kill the affected process. 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci (This is easy to reproduce on Windows, unfortunately, and can be traced to 427db96d56Sopenharmony_ci the SO_REUSEADDR socket option having different semantics on Windows versus 437db96d56Sopenharmony_ci Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind, 447db96d56Sopenharmony_ci listen and then accept connections on identical host/ports. An EADDRINUSE 457db96d56Sopenharmony_ci OSError will be raised at some point (depending on the platform and 467db96d56Sopenharmony_ci the order bind and listen were called on each socket). 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_ci However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE 497db96d56Sopenharmony_ci will ever be raised when attempting to bind two identical host/ports. When 507db96d56Sopenharmony_ci accept() is called on each socket, the second caller's process will steal 517db96d56Sopenharmony_ci the port from the first caller, leaving them both in an awkwardly wedged 527db96d56Sopenharmony_ci state where they'll no longer respond to any signals or graceful kills, and 537db96d56Sopenharmony_ci must be forcibly killed via OpenProcess()/TerminateProcess(). 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option 567db96d56Sopenharmony_ci instead of SO_REUSEADDR, which effectively affords the same semantics as 577db96d56Sopenharmony_ci SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open 587db96d56Sopenharmony_ci Source world compared to Windows ones, this is a common mistake. A quick 597db96d56Sopenharmony_ci look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when 607db96d56Sopenharmony_ci openssl.exe is called with the 's_server' option, for example. See 617db96d56Sopenharmony_ci http://bugs.python.org/issue2550 for more info. The following site also 627db96d56Sopenharmony_ci has a very thorough description about the implications of both REUSEADDR 637db96d56Sopenharmony_ci and EXCLUSIVEADDRUSE on Windows: 647db96d56Sopenharmony_ci https://learn.microsoft.com/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci XXX: although this approach is a vast improvement on previous attempts to 677db96d56Sopenharmony_ci elicit unused ports, it rests heavily on the assumption that the ephemeral 687db96d56Sopenharmony_ci port returned to us by the OS won't immediately be dished back out to some 697db96d56Sopenharmony_ci other process when we close and delete our temporary socket but before our 707db96d56Sopenharmony_ci calling code has a chance to bind the returned port. We can deal with this 717db96d56Sopenharmony_ci issue if/when we come across it. 727db96d56Sopenharmony_ci """ 737db96d56Sopenharmony_ci 747db96d56Sopenharmony_ci with socket.socket(family, socktype) as tempsock: 757db96d56Sopenharmony_ci port = bind_port(tempsock) 767db96d56Sopenharmony_ci del tempsock 777db96d56Sopenharmony_ci return port 787db96d56Sopenharmony_ci 797db96d56Sopenharmony_cidef bind_port(sock, host=HOST): 807db96d56Sopenharmony_ci """Bind the socket to a free port and return the port number. Relies on 817db96d56Sopenharmony_ci ephemeral ports in order to ensure we are using an unbound port. This is 827db96d56Sopenharmony_ci important as many tests may be running simultaneously, especially in a 837db96d56Sopenharmony_ci buildbot environment. This method raises an exception if the sock.family 847db96d56Sopenharmony_ci is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR 857db96d56Sopenharmony_ci or SO_REUSEPORT set on it. Tests should *never* set these socket options 867db96d56Sopenharmony_ci for TCP/IP sockets. The only case for setting these options is testing 877db96d56Sopenharmony_ci multicasting via multiple UDP sockets. 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. 907db96d56Sopenharmony_ci on Windows), it will be set on the socket. This will prevent anyone else 917db96d56Sopenharmony_ci from bind()'ing to our host/port for the duration of the test. 927db96d56Sopenharmony_ci """ 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: 957db96d56Sopenharmony_ci if hasattr(socket, 'SO_REUSEADDR'): 967db96d56Sopenharmony_ci if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: 977db96d56Sopenharmony_ci raise support.TestFailed("tests should never set the " 987db96d56Sopenharmony_ci "SO_REUSEADDR socket option on " 997db96d56Sopenharmony_ci "TCP/IP sockets!") 1007db96d56Sopenharmony_ci if hasattr(socket, 'SO_REUSEPORT'): 1017db96d56Sopenharmony_ci try: 1027db96d56Sopenharmony_ci if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: 1037db96d56Sopenharmony_ci raise support.TestFailed("tests should never set the " 1047db96d56Sopenharmony_ci "SO_REUSEPORT socket option on " 1057db96d56Sopenharmony_ci "TCP/IP sockets!") 1067db96d56Sopenharmony_ci except OSError: 1077db96d56Sopenharmony_ci # Python's socket module was compiled using modern headers 1087db96d56Sopenharmony_ci # thus defining SO_REUSEPORT but this process is running 1097db96d56Sopenharmony_ci # under an older kernel that does not support SO_REUSEPORT. 1107db96d56Sopenharmony_ci pass 1117db96d56Sopenharmony_ci if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): 1127db96d56Sopenharmony_ci sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci sock.bind((host, 0)) 1157db96d56Sopenharmony_ci port = sock.getsockname()[1] 1167db96d56Sopenharmony_ci return port 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_cidef bind_unix_socket(sock, addr): 1197db96d56Sopenharmony_ci """Bind a unix socket, raising SkipTest if PermissionError is raised.""" 1207db96d56Sopenharmony_ci assert sock.family == socket.AF_UNIX 1217db96d56Sopenharmony_ci try: 1227db96d56Sopenharmony_ci sock.bind(addr) 1237db96d56Sopenharmony_ci except PermissionError: 1247db96d56Sopenharmony_ci sock.close() 1257db96d56Sopenharmony_ci raise unittest.SkipTest('cannot bind AF_UNIX sockets') 1267db96d56Sopenharmony_ci 1277db96d56Sopenharmony_cidef _is_ipv6_enabled(): 1287db96d56Sopenharmony_ci """Check whether IPv6 is enabled on this host.""" 1297db96d56Sopenharmony_ci if socket.has_ipv6: 1307db96d56Sopenharmony_ci sock = None 1317db96d56Sopenharmony_ci try: 1327db96d56Sopenharmony_ci sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 1337db96d56Sopenharmony_ci sock.bind((HOSTv6, 0)) 1347db96d56Sopenharmony_ci return True 1357db96d56Sopenharmony_ci except OSError: 1367db96d56Sopenharmony_ci pass 1377db96d56Sopenharmony_ci finally: 1387db96d56Sopenharmony_ci if sock: 1397db96d56Sopenharmony_ci sock.close() 1407db96d56Sopenharmony_ci return False 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ciIPV6_ENABLED = _is_ipv6_enabled() 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci_bind_nix_socket_error = None 1467db96d56Sopenharmony_cidef skip_unless_bind_unix_socket(test): 1477db96d56Sopenharmony_ci """Decorator for tests requiring a functional bind() for unix sockets.""" 1487db96d56Sopenharmony_ci if not hasattr(socket, 'AF_UNIX'): 1497db96d56Sopenharmony_ci return unittest.skip('No UNIX Sockets')(test) 1507db96d56Sopenharmony_ci global _bind_nix_socket_error 1517db96d56Sopenharmony_ci if _bind_nix_socket_error is None: 1527db96d56Sopenharmony_ci from .os_helper import TESTFN, unlink 1537db96d56Sopenharmony_ci path = TESTFN + "can_bind_unix_socket" 1547db96d56Sopenharmony_ci with socket.socket(socket.AF_UNIX) as sock: 1557db96d56Sopenharmony_ci try: 1567db96d56Sopenharmony_ci sock.bind(path) 1577db96d56Sopenharmony_ci _bind_nix_socket_error = False 1587db96d56Sopenharmony_ci except OSError as e: 1597db96d56Sopenharmony_ci _bind_nix_socket_error = e 1607db96d56Sopenharmony_ci finally: 1617db96d56Sopenharmony_ci unlink(path) 1627db96d56Sopenharmony_ci if _bind_nix_socket_error: 1637db96d56Sopenharmony_ci msg = 'Requires a functional unix bind(): %s' % _bind_nix_socket_error 1647db96d56Sopenharmony_ci return unittest.skip(msg)(test) 1657db96d56Sopenharmony_ci else: 1667db96d56Sopenharmony_ci return test 1677db96d56Sopenharmony_ci 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_cidef get_socket_conn_refused_errs(): 1707db96d56Sopenharmony_ci """ 1717db96d56Sopenharmony_ci Get the different socket error numbers ('errno') which can be received 1727db96d56Sopenharmony_ci when a connection is refused. 1737db96d56Sopenharmony_ci """ 1747db96d56Sopenharmony_ci errors = [errno.ECONNREFUSED] 1757db96d56Sopenharmony_ci if hasattr(errno, 'ENETUNREACH'): 1767db96d56Sopenharmony_ci # On Solaris, ENETUNREACH is returned sometimes instead of ECONNREFUSED 1777db96d56Sopenharmony_ci errors.append(errno.ENETUNREACH) 1787db96d56Sopenharmony_ci if hasattr(errno, 'EADDRNOTAVAIL'): 1797db96d56Sopenharmony_ci # bpo-31910: socket.create_connection() fails randomly 1807db96d56Sopenharmony_ci # with EADDRNOTAVAIL on Travis CI 1817db96d56Sopenharmony_ci errors.append(errno.EADDRNOTAVAIL) 1827db96d56Sopenharmony_ci if hasattr(errno, 'EHOSTUNREACH'): 1837db96d56Sopenharmony_ci # bpo-37583: The destination host cannot be reached 1847db96d56Sopenharmony_ci errors.append(errno.EHOSTUNREACH) 1857db96d56Sopenharmony_ci if not IPV6_ENABLED: 1867db96d56Sopenharmony_ci errors.append(errno.EAFNOSUPPORT) 1877db96d56Sopenharmony_ci return errors 1887db96d56Sopenharmony_ci 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci_NOT_SET = object() 1917db96d56Sopenharmony_ci 1927db96d56Sopenharmony_ci@contextlib.contextmanager 1937db96d56Sopenharmony_cidef transient_internet(resource_name, *, timeout=_NOT_SET, errnos=()): 1947db96d56Sopenharmony_ci """Return a context manager that raises ResourceDenied when various issues 1957db96d56Sopenharmony_ci with the internet connection manifest themselves as exceptions.""" 1967db96d56Sopenharmony_ci nntplib = warnings_helper.import_deprecated("nntplib") 1977db96d56Sopenharmony_ci import urllib.error 1987db96d56Sopenharmony_ci if timeout is _NOT_SET: 1997db96d56Sopenharmony_ci timeout = support.INTERNET_TIMEOUT 2007db96d56Sopenharmony_ci 2017db96d56Sopenharmony_ci default_errnos = [ 2027db96d56Sopenharmony_ci ('ECONNREFUSED', 111), 2037db96d56Sopenharmony_ci ('ECONNRESET', 104), 2047db96d56Sopenharmony_ci ('EHOSTUNREACH', 113), 2057db96d56Sopenharmony_ci ('ENETUNREACH', 101), 2067db96d56Sopenharmony_ci ('ETIMEDOUT', 110), 2077db96d56Sopenharmony_ci # socket.create_connection() fails randomly with 2087db96d56Sopenharmony_ci # EADDRNOTAVAIL on Travis CI. 2097db96d56Sopenharmony_ci ('EADDRNOTAVAIL', 99), 2107db96d56Sopenharmony_ci ] 2117db96d56Sopenharmony_ci default_gai_errnos = [ 2127db96d56Sopenharmony_ci ('EAI_AGAIN', -3), 2137db96d56Sopenharmony_ci ('EAI_FAIL', -4), 2147db96d56Sopenharmony_ci ('EAI_NONAME', -2), 2157db96d56Sopenharmony_ci ('EAI_NODATA', -5), 2167db96d56Sopenharmony_ci # Encountered when trying to resolve IPv6-only hostnames 2177db96d56Sopenharmony_ci ('WSANO_DATA', 11004), 2187db96d56Sopenharmony_ci ] 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ci denied = support.ResourceDenied("Resource %r is not available" % resource_name) 2217db96d56Sopenharmony_ci captured_errnos = errnos 2227db96d56Sopenharmony_ci gai_errnos = [] 2237db96d56Sopenharmony_ci if not captured_errnos: 2247db96d56Sopenharmony_ci captured_errnos = [getattr(errno, name, num) 2257db96d56Sopenharmony_ci for (name, num) in default_errnos] 2267db96d56Sopenharmony_ci gai_errnos = [getattr(socket, name, num) 2277db96d56Sopenharmony_ci for (name, num) in default_gai_errnos] 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ci def filter_error(err): 2307db96d56Sopenharmony_ci n = getattr(err, 'errno', None) 2317db96d56Sopenharmony_ci if (isinstance(err, TimeoutError) or 2327db96d56Sopenharmony_ci (isinstance(err, socket.gaierror) and n in gai_errnos) or 2337db96d56Sopenharmony_ci (isinstance(err, urllib.error.HTTPError) and 2347db96d56Sopenharmony_ci 500 <= err.code <= 599) or 2357db96d56Sopenharmony_ci (isinstance(err, urllib.error.URLError) and 2367db96d56Sopenharmony_ci (("ConnectionRefusedError" in err.reason) or 2377db96d56Sopenharmony_ci ("TimeoutError" in err.reason) or 2387db96d56Sopenharmony_ci ("EOFError" in err.reason))) or 2397db96d56Sopenharmony_ci n in captured_errnos): 2407db96d56Sopenharmony_ci if not support.verbose: 2417db96d56Sopenharmony_ci sys.stderr.write(denied.args[0] + "\n") 2427db96d56Sopenharmony_ci raise denied from err 2437db96d56Sopenharmony_ci 2447db96d56Sopenharmony_ci old_timeout = socket.getdefaulttimeout() 2457db96d56Sopenharmony_ci try: 2467db96d56Sopenharmony_ci if timeout is not None: 2477db96d56Sopenharmony_ci socket.setdefaulttimeout(timeout) 2487db96d56Sopenharmony_ci yield 2497db96d56Sopenharmony_ci except nntplib.NNTPTemporaryError as err: 2507db96d56Sopenharmony_ci if support.verbose: 2517db96d56Sopenharmony_ci sys.stderr.write(denied.args[0] + "\n") 2527db96d56Sopenharmony_ci raise denied from err 2537db96d56Sopenharmony_ci except OSError as err: 2547db96d56Sopenharmony_ci # urllib can wrap original socket errors multiple times (!), we must 2557db96d56Sopenharmony_ci # unwrap to get at the original error. 2567db96d56Sopenharmony_ci while True: 2577db96d56Sopenharmony_ci a = err.args 2587db96d56Sopenharmony_ci if len(a) >= 1 and isinstance(a[0], OSError): 2597db96d56Sopenharmony_ci err = a[0] 2607db96d56Sopenharmony_ci # The error can also be wrapped as args[1]: 2617db96d56Sopenharmony_ci # except socket.error as msg: 2627db96d56Sopenharmony_ci # raise OSError('socket error', msg) from msg 2637db96d56Sopenharmony_ci elif len(a) >= 2 and isinstance(a[1], OSError): 2647db96d56Sopenharmony_ci err = a[1] 2657db96d56Sopenharmony_ci else: 2667db96d56Sopenharmony_ci break 2677db96d56Sopenharmony_ci filter_error(err) 2687db96d56Sopenharmony_ci raise 2697db96d56Sopenharmony_ci # XXX should we catch generic exceptions and look for their 2707db96d56Sopenharmony_ci # __cause__ or __context__? 2717db96d56Sopenharmony_ci finally: 2727db96d56Sopenharmony_ci socket.setdefaulttimeout(old_timeout) 273