Repository: bitcoin/bitcoin
File: test/functional/test_framework/socks5.py (Python)

Code before:
        if not d:
            raise IOError('Unexpected end of stream')
        rv.extend(d)
        n -= len(d)
    return rv

# Implementation classes
class Socks5Configuration():
    """Proxy configuration."""
    def __init__(self):
        self.addr = None # Bind address (must be set)
        self.af = socket.AF_INET # Bind address family
        self.unauth = False  # Support unauthenticated
        self.auth = False  # Support authentication
        self.keep_alive = False  # Do not automatically close connections

class Socks5Command():
    """Information about an incoming socks5 command."""
    def __init__(self, cmd, atyp, addr, port, username, password):
        self.cmd = cmd # Command (one of Command.*)
        self.atyp = atyp # Address type (one of AddressType.*)
        self.addr = addr # Address
        self.port = port # Port to connect to
        self.username = username
        self.password = password
    def __repr__(self):
        return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password)

class Socks5Connection():
    def __init__(self, serv, conn):
        self.serv = serv
        self.conn = conn

    def handle(self):
        """Handle socks5 request according to RFC192."""
        try:
            # Verify socks version
            ver = recvall(self.conn, 1)[0]
            if ver != 0x05:
                raise IOError('Invalid socks version %i' % ver)
            # Choose authentication method
            nmethods = recvall(self.conn, 1)[0]
            methods = bytearray(recvall(self.conn, nmethods))
            method = None
            if 0x02 in methods and self.serv.conf.auth:
                method = 0x02 # username/password
            elif 0x00 in methods and self.serv.conf.unauth:
                method = 0x00 # unauthenticated
            if method is None:
                raise IOError('No supported authentication method was offered')
            # Send response

Review comment (suggestion):
https://github.com/bitcoin/bitcoin/pull/29420/commits/1e76be04318a484124b82199260f23b72801e459

Instead of the counter, you could also use `dest = self.destiantions.pop()` below and then log your warning once the array is empty

Code after:
def recvall(s, n):
    """Receive n bytes from a socket, or fail."""
    rv = bytearray()
    while n > 0:
        d = s.recv(n)
        if not d:
            raise IOError('Unexpected end of stream')
        rv.extend(d)
        n -= len(d)
    return rv

def sendall(s, data):
    """Send all data to a socket, or fail."""
    sent = 0
    while sent < len(data):
        _, wlist, _ = select.select([], [s], [])
        if len(wlist) > 0:
            n = s.send(data[sent:])
            if n == 0:
                raise IOError('send() on socket returned 0')
            sent += n

def forward_sockets(a, b):
    """Forward data received on socket a to socket b and vice versa, until EOF is received on one of the sockets."""
    # Mark as non-blocking so that we do not end up in a deadlock-like situation
    # where we block and wait on data from `a` while there is data ready to be
    # received on `b` and forwarded to `a`. And at the same time the application
    # at `a` is not sending anything because it waits for the data from `b` to
    # respond.
    a.setblocking(False)
    b.setblocking(False)
    sockets = [a, b]
    done = False
    while not done:
        rlist, _, xlist = select.select(sockets, [], sockets)
        if len(xlist) > 0:
            raise IOError('Exceptional condition on socket')
        for s in rlist:
            data = s.recv(4096)
            if data is None or len(data) == 0:
                done = True
                break
            if s == a:
                sendall(b, data)
            else:
                sendall(a, data)

# Implementation classes
class Socks5Configuration():
    """Proxy configuration."""
    def __init__(self):