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