From 189578cba389423a54c6541d60353a8c209bd62e Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Sat, 24 Jun 2017 12:23:56 +0200 Subject: [PATCH] Asyncore proxy fixes - SOCKS5 now seems to work, SOCKS4a untested --- src/network/proxy.py | 41 +++++++++++++++++++++++++------- src/network/socks4a.py | 21 +++++++++-------- src/network/socks5.py | 53 ++++++++++++++++++++++++------------------ src/network/tcp.py | 12 +++++++--- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/src/network/proxy.py b/src/network/proxy.py index 3b7cc848..c131c22a 100644 --- a/src/network/proxy.py +++ b/src/network/proxy.py @@ -1,13 +1,36 @@ import socket - -import state +import time from advanceddispatcher import AdvancedDispatcher import asyncore_pollchoose as asyncore +from debug import logger import network.connectionpool +import state + +class ProxyError(Exception): + errorCodes = ("UnknownError") + + def __init__(self, code): + self.code = code + try: + self.message = self.__class__.errorCodes[self.code] + except IndexError: + self.message = self.__class__.errorCodes[-1] + super(ProxyError, self).__init__(self.message) + + +class GeneralProxyError(ProxyError): + errorCodes = ("Success", + "Invalid data", + "Not connected", + "Not available", + "Bad proxy type", + "Bad input", + "Timed out", + "Network unreachable", + "Connection refused", + "Host unreachable") -class ProxyError(Exception): pass -class GeneralProxyError(ProxyError): pass class Proxy(AdvancedDispatcher): # these are global, and if you change config during runtime, all active/new @@ -22,7 +45,8 @@ class Proxy(AdvancedDispatcher): @proxy.setter def proxy(self, address): - if type(address) != tuple or (len(address) < 2) or (type(str(address[0])) != type('')) or (type(address[1]) != int): + if not isinstance(address, tuple) or (len(address) < 2) or \ + (not isinstance(address[0], str) or not isinstance(address[1], int)): raise ValueError self.__class__._proxy = address @@ -42,18 +66,17 @@ class Proxy(AdvancedDispatcher): self.isOutbound = True self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect(self.proxy) - print "connecting in background to %s:%i" % (self.proxy[0], self.proxy[1]) def handle_connect(self): + self.set_state("init") try: AdvancedDispatcher.handle_connect(self) except socket.error as e: if e.errno in asyncore._DISCONNECTED: - logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e))) + logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e)) return + self.state_init() def state_proxy_handshake_done(self): - self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, network.connectionpool.BMConnectionPool().streams, False)) self.connectedAt = time.time() return False - diff --git a/src/network/socks4a.py b/src/network/socks4a.py index 08bd18cf..61f3ec7d 100644 --- a/src/network/socks4a.py +++ b/src/network/socks4a.py @@ -1,11 +1,14 @@ import socket import struct -from advanceddispatcher import AdvancedDispatcher -import asyncore_pollchoose as asyncore from proxy import Proxy, ProxyError, GeneralProxyError -class Socks4aError(ProxyError): pass +class Socks4aError(ProxyError): + errorCodes = ("Request granted", + "Request rejected or failed", + "Request rejected because SOCKS server cannot connect to identd on the client", + "Request rejected because the client program and identd report different user-ids", + "Unknown error") class Socks4a(Proxy): @@ -24,22 +27,20 @@ class Socks4a(Proxy): if self.read_buf[0:1] != chr(0x00).encode(): # bad data self.close() - raise Socks4aError + raise GeneralProxyError(1) elif self.read_buf[1:2] != chr(0x5A).encode(): # Connection failed self.close() if ord(self.read_buf[1:2]) in (91, 92, 93): - # socks 4 erro - raise Socks4aError - #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])-90])) + # socks 4 error + raise Socks4aError(ord(self.read_buf[1:2]) - 90) else: - raise Socks4aError - #raise Socks4aError((94, _socks4aerrors[4])) + raise Socks4aError(4) # Get the bound address/port self.boundport = struct.unpack(">H", self.read_buf[2:4])[0] self.boundaddr = self.read_buf[4:] self.__proxysockname = (self.boundaddr, self.boundport) - if self.ipaddr != None: + if self.ipaddr: self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) diff --git a/src/network/socks5.py b/src/network/socks5.py index 6745308c..89c3c9b2 100644 --- a/src/network/socks5.py +++ b/src/network/socks5.py @@ -1,13 +1,27 @@ import socket import struct -from advanceddispatcher import AdvancedDispatcher -import asyncore_pollchoose as asyncore from proxy import Proxy, ProxyError, GeneralProxyError -import network.connectionpool -class Socks5AuthError(ProxyError): pass -class Socks5Error(ProxyError): pass +class Socks5AuthError(ProxyError): + errorCodes = ("Succeeded", + "Authentication is required", + "All offered authentication methods were rejected", + "Unknown username or invalid password", + "Unknown error") + + +class Socks5Error(ProxyError): + errorCodes = ("Succeeded", + "General SOCKS server failure", + "Connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", + "Unknown error") class Socks5(Proxy): @@ -30,7 +44,7 @@ class Socks5(Proxy): self.read_buf = self.read_buf[2:] if ret[0] != 5: # general error - raise GeneralProxyError + raise GeneralProxyError(1) elif ret[1] == 0: # no auth required self.set_state("auth_done", 2) @@ -39,14 +53,14 @@ class Socks5(Proxy): self.writeQueue.put(struct.pack('BB', 1, len(self._auth[0])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \ self._auth[1]) - self.set_state("auth_1", 2) + self.set_state("auth_needed", 2) else: if ret[1] == 0xff: # auth error - raise Socks5AuthError + raise Socks5AuthError(2) else: # other error - raise Socks5Error + raise GeneralProxyError(1) def state_auth_needed(self): if not self.read_buf_sufficient(2): @@ -54,10 +68,10 @@ class Socks5(Proxy): ret = struct.unpack('BB', self.read_buf) if ret[0] != 1: # general error - raise Socks5Error + raise GeneralProxyError(1) if ret[1] != 0: # auth error - raise Socks5AuthError + raise Socks5AuthError(3) # all ok self.set_state = ("auth_done", 2) @@ -66,19 +80,15 @@ class Socks5(Proxy): return False # Get the response if self.read_buf[0:1] != chr(0x05).encode(): - # general error self.close() - raise Socks5Error + raise GeneralProxyError(1) elif self.read_buf[1:2] != chr(0x00).encode(): # Connection failed self.close() if ord(self.read_buf[1:2])<=8: - # socks 5 erro - raise Socks5Error - #raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) + raise Socks5Error(ord(self.read_buf[1:2])) else: - raise Socks5Error - #raise Socks5Error((9, _socks5errors[9])) + raise Socks5Error(9) # Get the bound address/port elif self.read_buf[3:4] == chr(0x01).encode(): self.set_state("proxy_addr_1", 4) @@ -86,8 +96,7 @@ class Socks5(Proxy): self.set_state("proxy_addr_2_1", 4) else: self.close() - #raise GeneralProxyError((1,_generalerrors[1])) - raise GeneralProxyError + raise GeneralProxyError(1) def state_proxy_addr_1(self): if not self.read_buf_sufficient(4): @@ -112,14 +121,14 @@ class Socks5(Proxy): return False self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] self.__proxysockname = (self.boundaddr, self.boundport) - if self.ipaddr != None: + if self.ipaddr is not None: self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) else: self.__proxypeername = (self.destination[0], self.destport) self.set_state("proxy_handshake_done", 2) def proxy_sock_name(self): - return socket.inet_ntoa(self.__proxysockname[0]) + return socket.inet_ntoa(self.__proxysockname[0]) class Socks5Connection(Socks5): diff --git a/src/network/tcp.py b/src/network/tcp.py index 8a6b5705..bd925063 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -181,7 +181,10 @@ class Socks5BMConnection(Socks5Connection, TCPConnection): TCPConnection.__init__(self, address=address, sock=self.socket) self.set_state("init") - def state_socks_handshake_done(self): + def state_proxy_handshake_done(self): + Socks5Connection.state_proxy_handshake_done(self) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) return False @@ -192,7 +195,10 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection): TCPConnection.__init__(self, address=address, sock=self.socket) self.set_state("init") - def state_socks_handshake_done(self): + def state_proxy_handshake_done(self): + Socks4aConnection.state_proxy_handshake_done(self) + self.writeQueue.put(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ + network.connectionpool.BMConnectionPool().streams, False)) self.set_state("bm_header", expectBytes=protocol.Header.size) return False @@ -216,7 +222,7 @@ class TCPServer(AdvancedDispatcher): len(network.connectionpool.BMConnectionPool().outboundConnections) > \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): - close(sock) + sock.close() return try: network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock))