Asyncore proxy fixes

- SOCKS5 now seems to work, SOCKS4a untested
This commit is contained in:
Peter Šurda 2017-06-24 12:23:56 +02:00
parent 916b85c862
commit 189578cba3
Signed by untrusted user: PeterSurda
GPG Key ID: 0C5F50C0B5F37D87
4 changed files with 83 additions and 44 deletions

View File

@ -1,13 +1,36 @@
import socket import socket
import time
import state
from advanceddispatcher import AdvancedDispatcher from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
from debug import logger
import network.connectionpool 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): class Proxy(AdvancedDispatcher):
# these are global, and if you change config during runtime, all active/new # these are global, and if you change config during runtime, all active/new
@ -22,7 +45,8 @@ class Proxy(AdvancedDispatcher):
@proxy.setter @proxy.setter
def proxy(self, address): 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 raise ValueError
self.__class__._proxy = address self.__class__._proxy = address
@ -42,18 +66,17 @@ class Proxy(AdvancedDispatcher):
self.isOutbound = True self.isOutbound = True
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(self.proxy) self.connect(self.proxy)
print "connecting in background to %s:%i" % (self.proxy[0], self.proxy[1])
def handle_connect(self): def handle_connect(self):
self.set_state("init")
try: try:
AdvancedDispatcher.handle_connect(self) AdvancedDispatcher.handle_connect(self)
except socket.error as e: except socket.error as e:
if e.errno in asyncore._DISCONNECTED: 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 return
self.state_init()
def state_proxy_handshake_done(self): 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() self.connectedAt = time.time()
return False return False

View File

@ -1,11 +1,14 @@
import socket import socket
import struct import struct
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore
from proxy import Proxy, ProxyError, GeneralProxyError 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): class Socks4a(Proxy):
@ -24,22 +27,20 @@ class Socks4a(Proxy):
if self.read_buf[0:1] != chr(0x00).encode(): if self.read_buf[0:1] != chr(0x00).encode():
# bad data # bad data
self.close() self.close()
raise Socks4aError raise GeneralProxyError(1)
elif self.read_buf[1:2] != chr(0x5A).encode(): elif self.read_buf[1:2] != chr(0x5A).encode():
# Connection failed # Connection failed
self.close() self.close()
if ord(self.read_buf[1:2]) in (91, 92, 93): if ord(self.read_buf[1:2]) in (91, 92, 93):
# socks 4 erro # socks 4 error
raise Socks4aError raise Socks4aError(ord(self.read_buf[1:2]) - 90)
#raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])-90]))
else: else:
raise Socks4aError raise Socks4aError(4)
#raise Socks4aError((94, _socks4aerrors[4]))
# Get the bound address/port # Get the bound address/port
self.boundport = struct.unpack(">H", self.read_buf[2:4])[0] self.boundport = struct.unpack(">H", self.read_buf[2:4])[0]
self.boundaddr = self.read_buf[4:] self.boundaddr = self.read_buf[4:]
self.__proxysockname = (self.boundaddr, self.boundport) self.__proxysockname = (self.boundaddr, self.boundport)
if self.ipaddr != None: if self.ipaddr:
self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1])
else: else:
self.__proxypeername = (self.destination[0], self.destport) self.__proxypeername = (self.destination[0], self.destport)

View File

@ -1,13 +1,27 @@
import socket import socket
import struct import struct
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore
from proxy import Proxy, ProxyError, GeneralProxyError from proxy import Proxy, ProxyError, GeneralProxyError
import network.connectionpool
class Socks5AuthError(ProxyError): pass class Socks5AuthError(ProxyError):
class Socks5Error(ProxyError): pass 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): class Socks5(Proxy):
@ -30,7 +44,7 @@ class Socks5(Proxy):
self.read_buf = self.read_buf[2:] self.read_buf = self.read_buf[2:]
if ret[0] != 5: if ret[0] != 5:
# general error # general error
raise GeneralProxyError raise GeneralProxyError(1)
elif ret[1] == 0: elif ret[1] == 0:
# no auth required # no auth required
self.set_state("auth_done", 2) 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.writeQueue.put(struct.pack('BB', 1, len(self._auth[0])) + \
self._auth[0] + struct.pack('B', len(self._auth[1])) + \ self._auth[0] + struct.pack('B', len(self._auth[1])) + \
self._auth[1]) self._auth[1])
self.set_state("auth_1", 2) self.set_state("auth_needed", 2)
else: else:
if ret[1] == 0xff: if ret[1] == 0xff:
# auth error # auth error
raise Socks5AuthError raise Socks5AuthError(2)
else: else:
# other error # other error
raise Socks5Error raise GeneralProxyError(1)
def state_auth_needed(self): def state_auth_needed(self):
if not self.read_buf_sufficient(2): if not self.read_buf_sufficient(2):
@ -54,10 +68,10 @@ class Socks5(Proxy):
ret = struct.unpack('BB', self.read_buf) ret = struct.unpack('BB', self.read_buf)
if ret[0] != 1: if ret[0] != 1:
# general error # general error
raise Socks5Error raise GeneralProxyError(1)
if ret[1] != 0: if ret[1] != 0:
# auth error # auth error
raise Socks5AuthError raise Socks5AuthError(3)
# all ok # all ok
self.set_state = ("auth_done", 2) self.set_state = ("auth_done", 2)
@ -66,19 +80,15 @@ class Socks5(Proxy):
return False return False
# Get the response # Get the response
if self.read_buf[0:1] != chr(0x05).encode(): if self.read_buf[0:1] != chr(0x05).encode():
# general error
self.close() self.close()
raise Socks5Error raise GeneralProxyError(1)
elif self.read_buf[1:2] != chr(0x00).encode(): elif self.read_buf[1:2] != chr(0x00).encode():
# Connection failed # Connection failed
self.close() self.close()
if ord(self.read_buf[1:2])<=8: if ord(self.read_buf[1:2])<=8:
# socks 5 erro raise Socks5Error(ord(self.read_buf[1:2]))
raise Socks5Error
#raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else: else:
raise Socks5Error raise Socks5Error(9)
#raise Socks5Error((9, _socks5errors[9]))
# Get the bound address/port # Get the bound address/port
elif self.read_buf[3:4] == chr(0x01).encode(): elif self.read_buf[3:4] == chr(0x01).encode():
self.set_state("proxy_addr_1", 4) self.set_state("proxy_addr_1", 4)
@ -86,8 +96,7 @@ class Socks5(Proxy):
self.set_state("proxy_addr_2_1", 4) self.set_state("proxy_addr_2_1", 4)
else: else:
self.close() self.close()
#raise GeneralProxyError((1,_generalerrors[1])) raise GeneralProxyError(1)
raise GeneralProxyError
def state_proxy_addr_1(self): def state_proxy_addr_1(self):
if not self.read_buf_sufficient(4): if not self.read_buf_sufficient(4):
@ -112,7 +121,7 @@ class Socks5(Proxy):
return False return False
self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] self.boundport = struct.unpack(">H", self.read_buf[0:2])[0]
self.__proxysockname = (self.boundaddr, self.boundport) 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]) self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1])
else: else:
self.__proxypeername = (self.destination[0], self.destport) self.__proxypeername = (self.destination[0], self.destport)

View File

@ -181,7 +181,10 @@ class Socks5BMConnection(Socks5Connection, TCPConnection):
TCPConnection.__init__(self, address=address, sock=self.socket) TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init") 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) self.set_state("bm_header", expectBytes=protocol.Header.size)
return False return False
@ -192,7 +195,10 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
TCPConnection.__init__(self, address=address, sock=self.socket) TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init") 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) self.set_state("bm_header", expectBytes=protocol.Header.size)
return False return False
@ -216,7 +222,7 @@ class TCPServer(AdvancedDispatcher):
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
close(sock) sock.close()
return return
try: try:
network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock))