Exemplo n.º 1
0
def original_addr(csock: socket.socket) -> typing.Tuple[str, int]:
    # Get the original destination on Linux.
    # In theory, this can be done using the following syscalls:
    #     sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
    #     sock.getsockopt(SOL_IPV6, SO_ORIGINAL_DST, 28)
    #
    # In practice, it is a bit more complex:
    #  1. We cannot rely on sock.family to decide which syscall to use because of IPv4-mapped
    #     IPv6 addresses. If sock.family is AF_INET6 while sock.getsockname() is ::ffff:127.0.0.1,
    #     we need to call the IPv4 version to get a result.
    #  2. We can't just try the IPv4 syscall and then do IPv6 if that doesn't work,
    #     because doing the wrong syscall can apparently crash the whole Python runtime.
    # As such, we use a heuristic to check which syscall to do.
    is_ipv4 = "." in csock.getsockname()[0]  # either 127.0.0.1 or ::ffff:127.0.0.1
    if is_ipv4:
        # the struct returned here should only have 8 bytes, but invoking sock.getsockopt
        # with buflen=8 doesn't work.
        dst = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
        port, raw_ip = struct.unpack_from("!2xH4s", dst)
        ip = socket.inet_ntop(socket.AF_INET, raw_ip)
    else:
        dst = csock.getsockopt(SOL_IPV6, SO_ORIGINAL_DST, 28)
        port, raw_ip = struct.unpack_from("!2xH4x16s", dst)
        ip = socket.inet_ntop(socket.AF_INET6, raw_ip)
    return ip, port
Exemplo n.º 2
0
def get_sock_cred(conn: socket.socket) -> ta.Tuple[int, int, int]:
    if LINUX:
        creds = conn.getsockopt(socket.SOL_SOCKET, libc.SO_PEERCRED, struct.calcsize('3i'))
    elif DARWIN:
        creds = conn.getsockopt(libc.SOL_LOCAL, libc.LOCAL_PEERCRED, struct.calcsize('3i'))
    else:
        raise OSError

    pid, uid, gid = struct.unpack('3i', creds)
    return pid, uid, gid
Exemplo n.º 3
0
def _save_ttl(sock: socket.socket):
    old_ttl = sock.getsockopt(socket.SOL_IP, socket.IP_TTL)
    try:
        yield
    finally:
        sock.setsockopt(socket.SOL_IP, socket.IP_TTL, old_ttl)
    return
Exemplo n.º 4
0
def read_from_socket(s: socket) -> bytearray:
    data = bytearray()
    buff_size = s.getsockopt(SOL_SOCKET, SO_RCVBUF)
    while True:
        chunk = s.recv(buff_size)
        if chunk:
            data += chunk
        if len(chunk) == 0 or chunk[-1:] == b'/n':
            break
        return data
Exemplo n.º 5
0
def get_original_destination(socket: Socket) -> Tuple[str, int]:
    """
    Extracts the original destination from the socket.
    """
    SO_ORIGINAL_DST = 80  # Not in socket module

    original_dst = socket.getsockopt(SOL_IP, SO_ORIGINAL_DST, 16)
    _, port, a1, a2, a3, a4 = unpack("!HHBBBBxxxxxxxx", original_dst)
    host = f"{a1}.{a2}.{a3}.{a4}"

    return (host, port)
Exemplo n.º 6
0
	def __init__(self):
		global server_socket, serverpid
		server_socket = Socket(AF_INET, SOCK_STREAM)
		server_socket.setblocking(0)
		try:
			server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR,
				server_socket.getsockopt(SOL_SOCKET, SO_REUSEADDR) | 1)
		except error:
			pass

		serverpid = server_socket.fileno()
		pollster.register(serverpid, RE)
Exemplo n.º 7
0
def bind_port(sock: socket.socket, host: str = HOST) -> int:
    """Bind the socket to a free port and return the port number. Relies on
    ephemeral ports in order to ensure we are using an unbound port.  This is
    important as many tests may be running simultaneously, especially in a
    buildbot environment.  This method raises an exception if the sock.family
    is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR
    or SO_REUSEPORT set on it.  Tests should *never* set these socket options
    for TCP/IP sockets.  The only case for setting these options is testing
    multicasting via multiple UDP sockets.

    Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e.
    on Windows), it will be set on the socket.  This will prevent anyone else
    from bind()'ing to our host/port for the duration of the test.
    """

    if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM:
        if hasattr(socket, "SO_REUSEADDR"):
            if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1:
                raise ValueError("tests should never set the SO_REUSEADDR "
                                 "socket option on TCP/IP sockets!")
        if hasattr(socket, "SO_REUSEPORT"):
            try:
                if sock.getsockopt(socket.SOL_SOCKET,
                                   socket.SO_REUSEPORT) == 1:
                    raise ValueError("tests should never set the SO_REUSEPORT "
                                     "socket option on TCP/IP sockets!")
            except OSError:
                # Python's socket module was compiled using modern headers
                # thus defining SO_REUSEPORT but this process is running
                # under an older kernel that does not support SO_REUSEPORT.
                pass
        if hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)

    sock.bind((host, 0))
    port = sock.getsockname()[1]
    assert isinstance(port, int)
    return port
Exemplo n.º 8
0
def get_tcp_info(connection: socket.socket):
    """Get tcp_info

    TCP_ESTABLISHED = 1,
    TCP_SYN_SENT,
    TCP_SYN_RECV,
    TCP_FIN_WAIT1,
    TCP_FIN_WAIT2,
    TCP_TIME_WAIT,
    TCP_CLOSE,
    TCP_CLOSE_WAIT,
    TCP_LAST_ACK,
    TCP_LISTEN,
    TCP_CLOSING

    ref: https://stackoverflow.com/a/18189190
    """
    fmt = "B" * 7 + "I" * 21
    tcp_info = struct.unpack(fmt, connection.getsockopt(socket.IPPROTO_TCP, socket.TCP_INFO, 92))
    return tcp_info
Exemplo n.º 9
0
def _launch_repo_server(
    *,
    repo_server_bin: Path,
    sock: socket.socket,
    snapshot_dir: Path,
    debug: bool,
):
    '''
    Invokes `repo-server` with the given snapshot; passes it ownership of
    the bound TCP socket -- it listens & accepts connections.
    '''
    # This could be a thread, but it's probably not worth the risks
    # involved in mixing threads & subprocess (yes, lots of programs do,
    # but yes, far fewer do it safely).
    with sock, subprocess.Popen([
        repo_server_bin,
        '--socket-fd', str(sock.fileno()),
        '--snapshot-dir', snapshot_dir,
        *(['--debug'] if debug else []),
    ], pass_fds=[sock.fileno()]) as server_proc:
        try:
            log.info('Waiting for repo server to listen')
            while server_proc.poll() is None:
                if sock.getsockopt(socket.SOL_SOCKET, socket.SO_ACCEPTCONN):
                    break
                time.sleep(0.1)
            yield
        finally:
            # Although `repo-server` is a read-only proxy, give it the
            # chance to do graceful cleanup.
            log.info('Trying to gracefully terminate `repo-server`')
            # `atexit` (used in an FB-specific `repo-server` plugin) only
            # works with SIGINT.  We signal once, and need to wait for it to
            # clean up the resources it must to free.  Signaling twice would
            # interrupt cleanup (because this is Python, lol).
            server_proc.send_signal(signal.SIGINT)  # `atexit` needs this
            try:
                server_proc.wait(60.0)
            except subprocess.TimeoutExpired:  # pragma: no cover
                log.info('Killing unresponsive `repo-server`')
                server_proc.kill()
Exemplo n.º 10
0
 def supports_dualstack(self, sock: socket.socket = None) -> bool:
     '''
     Checks if the system kernel supports dual stack sockets, listening to both: IPv4 + IPv6
     If a socket is provided, the check is made against the provided socket
     '''
     try:
         socket.AF_INET6
         socket.IPPROTO_IPV6
         socket.IPV6_V6ONLY
     except AttributeError:
         return False
     try:
         if sock is not None:
             return not sock.getsockopt(socket.IPPROTO_IPV6,
                                        socket.IPV6_V6ONLY)
         else:
             sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
             with contextlib.closing(sock):
                 sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
                 return True
     except socket.error:
         return False
Exemplo n.º 11
0
def original_addr(csock: socket.socket):
    odestdata = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
    _, port, a1, a2, a3, a4 = struct.unpack("!HHBBBBxxxxxxxx", odestdata)
    address = "%d.%d.%d.%d" % (a1, a2, a3, a4)
    return address, port