| 127 | return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password) |
| 128 | |
| 129 | class Socks5Connection(): |
| 130 | def __init__(self, serv, conn): |
| 131 | self.serv = serv |
| 132 | self.conn = conn |
| 133 | # Socket-pair used to wake up blocking forwarding select |
| 134 | # Note: a pipe could be used as well, but that does not work with select() on Windows |
| 135 | self.wakeup_socket_pair = socket.socketpair() |
| 136 | # Index of this handler (within the server) |
| 137 | self.handler_index = None |
| 138 | |
| 139 | def handle(self): |
| 140 | """Handle socks5 request according to RFC1928.""" |
| 141 | log_exception_prefix = "Socks5Connection.handle(): " |
| 142 | try: |
| 143 | log_exception_prefix = ("Socks5Connection.handle(" |
| 144 | f"client={format_sock(self.conn, local=False)}, " |
| 145 | f"proxy={format_sock(self.conn, local=True)}): ") |
| 146 | |
| 147 | # Verify socks version |
| 148 | ver = recvall(self.conn, 1)[0] |
| 149 | if ver != 0x05: |
| 150 | raise IOError('Invalid socks version %i' % ver) |
| 151 | # Choose authentication method |
| 152 | nmethods = recvall(self.conn, 1)[0] |
| 153 | methods = bytearray(recvall(self.conn, nmethods)) |
| 154 | method = None |
| 155 | if 0x02 in methods and self.serv.conf.auth: |
| 156 | method = 0x02 # username/password |
| 157 | elif 0x00 in methods and self.serv.conf.unauth: |
| 158 | method = 0x00 # unauthenticated |
| 159 | if method is None: |
| 160 | raise IOError('No supported authentication method was offered') |
| 161 | # Send response |
| 162 | self.conn.sendall(bytearray([0x05, method])) |
| 163 | # Read authentication (optional) |
| 164 | username = None |
| 165 | password = None |
| 166 | if method == 0x02: |
| 167 | ver = recvall(self.conn, 1)[0] |
| 168 | if ver != 0x01: |
| 169 | raise IOError('Invalid auth packet version %i' % ver) |
| 170 | ulen = recvall(self.conn, 1)[0] |
| 171 | username = str(recvall(self.conn, ulen)) |
| 172 | plen = recvall(self.conn, 1)[0] |
| 173 | password = str(recvall(self.conn, plen)) |
| 174 | # Send authentication response |
| 175 | self.conn.sendall(bytearray([0x01, 0x00])) |
| 176 | |
| 177 | # Read connect request |
| 178 | ver, cmd, _, atyp = recvall(self.conn, 4) |
| 179 | if ver != 0x05: |
| 180 | raise IOError('Invalid socks version %i in connect request' % ver) |
| 181 | if cmd != Command.CONNECT: |
| 182 | raise IOError('Unhandled command %i in connect request' % cmd) |
| 183 | |
| 184 | if atyp == AddressType.IPV4: |
| 185 | addr = recvall(self.conn, 4) |
| 186 | elif atyp == AddressType.DOMAINNAME: |