socks4a.py.bak
1 """ 2 SOCKS4a proxy module 3 """ 4 # pylint: disable=attribute-defined-outside-init 5 import logging 6 import socket 7 import struct 8 9 from proxy import GeneralProxyError, Proxy, ProxyError 10 11 logger = logging.getLogger('default') 12 13 14 class Socks4aError(ProxyError): 15 """SOCKS4a error base class""" 16 errorCodes = ( 17 "Request granted", 18 "Request rejected or failed", 19 "Request rejected because SOCKS server cannot connect to identd" 20 " on the client", 21 "Request rejected because the client program and identd report" 22 " different user-ids", 23 "Unknown error" 24 ) 25 26 27 class Socks4a(Proxy): 28 """SOCKS4a proxy class""" 29 def __init__(self, address=None): 30 Proxy.__init__(self, address) 31 self.ipaddr = None 32 self.destport = address[1] 33 34 def state_init(self): 35 """Protocol initialisation (before connection is established)""" 36 self.set_state("auth_done", 0) 37 return True 38 39 def state_pre_connect(self): 40 """Handle feedback from SOCKS4a while it is connecting on our behalf""" 41 # Get the response 42 if self.read_buf[0:1] != chr(0x00).encode(): 43 # bad data 44 self.close() 45 raise GeneralProxyError(1) 46 elif self.read_buf[1:2] != chr(0x5A).encode(): 47 # Connection failed 48 self.close() 49 if ord(self.read_buf[1:2]) in (91, 92, 93): 50 # socks 4 error 51 raise Socks4aError(ord(self.read_buf[1:2]) - 90) 52 else: 53 raise Socks4aError(4) 54 # Get the bound address/port 55 self.boundport = struct.unpack(">H", self.read_buf[2:4])[0] 56 self.boundaddr = self.read_buf[4:] 57 self.__proxysockname = (self.boundaddr, self.boundport) 58 if self.ipaddr: 59 self.__proxypeername = ( 60 socket.inet_ntoa(self.ipaddr), self.destination[1]) 61 else: 62 self.__proxypeername = (self.destination[0], self.destport) 63 self.set_state("proxy_handshake_done", length=8) 64 return True 65 66 def proxy_sock_name(self): 67 """ 68 Handle return value when using SOCKS4a for DNS resolving 69 instead of connecting. 70 """ 71 return socket.inet_ntoa(self.__proxysockname[0]) 72 73 74 class Socks4aConnection(Socks4a): 75 """Child SOCKS4a class used for making outbound connections.""" 76 def __init__(self, address): 77 Socks4a.__init__(self, address=address) 78 79 def state_auth_done(self): 80 """Request connection to be made""" 81 # Now we can request the actual connection 82 rmtrslv = False 83 self.append_write_buf( 84 struct.pack('>BBH', 0x04, 0x01, self.destination[1])) 85 # If the given destination address is an IP address, we'll 86 # use the IPv4 address request even if remote resolving was specified. 87 try: 88 self.ipaddr = socket.inet_aton(self.destination[0]) 89 self.append_write_buf(self.ipaddr) 90 except socket.error: 91 # Well it's not an IP number, so it's probably a DNS name. 92 if self._remote_dns: 93 # Resolve remotely 94 rmtrslv = True 95 self.ipaddr = None 96 self.append_write_buf( 97 struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) 98 else: 99 # Resolve locally 100 self.ipaddr = socket.inet_aton( 101 socket.gethostbyname(self.destination[0])) 102 self.append_write_buf(self.ipaddr) 103 if self._auth: 104 self.append_write_buf(self._auth[0]) 105 self.append_write_buf(chr(0x00).encode()) 106 if rmtrslv: 107 self.append_write_buf(self.destination[0] + chr(0x00).encode()) 108 self.set_state("pre_connect", length=0, expectBytes=8) 109 return True 110 111 def state_pre_connect(self): 112 """Tell SOCKS4a to initiate a connection""" 113 try: 114 return Socks4a.state_pre_connect(self) 115 except Socks4aError as e: 116 self.close_reason = e.message 117 self.set_state("close") 118 119 120 class Socks4aResolver(Socks4a): 121 """DNS resolver class using SOCKS4a""" 122 def __init__(self, host): 123 self.host = host 124 self.port = 8444 125 Socks4a.__init__(self, address=(self.host, self.port)) 126 127 def state_auth_done(self): 128 """Request connection to be made""" 129 # Now we can request the actual connection 130 self.append_write_buf( 131 struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) 132 self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) 133 if self._auth: 134 self.append_write_buf(self._auth[0]) 135 self.append_write_buf(chr(0x00).encode()) 136 self.append_write_buf(self.host + chr(0x00).encode()) 137 self.set_state("pre_connect", length=0, expectBytes=8) 138 return True 139 140 def resolved(self): 141 """ 142 Resolving is done, process the return value. To use this within 143 PyBitmessage, a callback needs to be implemented which hasn't 144 been done yet. 145 """ 146 logger.debug( 147 'Resolved %s as %s', self.host, self.proxy_sock_name())