Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 1 | """SocksiPy - Python SOCKS module. |
| 2 | Version 1.00 |
| 3 | |
| 4 | Copyright 2006 Dan-Haim. All rights reserved. |
| 5 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 6 | Redistribution and use in source and binary forms, with or without modification, |
| 7 | are permitted provided that the following conditions are met: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 8 | 1. Redistributions of source code must retain the above copyright notice, this |
| 9 | list of conditions and the following disclaimer. |
| 10 | 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 | this list of conditions and the following disclaimer in the documentation |
| 12 | and/or other materials provided with the distribution. |
| 13 | 3. Neither the name of Dan Haim nor the names of his contributors may be used |
| 14 | to endorse or promote products derived from this software without specific |
| 15 | prior written permission. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 16 | |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 17 | THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED |
| 18 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 19 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 20 | EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA |
| 23 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 25 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 26 | |
| 27 | |
| 28 | This module provides a standard socket-like interface for Python |
| 29 | for tunneling connections through SOCKS proxies. |
| 30 | |
| 31 | """ |
| 32 | |
| 33 | """ |
| 34 | |
| 35 | Minor modifications made by Christopher Gilbert (http://motomastyle.com/) |
| 36 | for use in PyLoris (http://pyloris.sourceforge.net/) |
| 37 | |
| 38 | Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/) |
| 39 | mainly to merge bug fixes found in Sourceforge |
| 40 | |
| 41 | """ |
| 42 | |
| 43 | import socket |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 44 | import struct |
| 45 | import sys |
Joe Gregorio | 5e3a5fa | 2010-10-11 13:03:56 -0400 | [diff] [blame] | 46 | |
| 47 | if getattr(socket, 'socket', None) is None: |
| 48 | raise ImportError('socket.socket missing, proxy support unusable') |
| 49 | |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 50 | PROXY_TYPE_SOCKS4 = 1 |
| 51 | PROXY_TYPE_SOCKS5 = 2 |
| 52 | PROXY_TYPE_HTTP = 3 |
| 53 | |
| 54 | _defaultproxy = None |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 55 | _orgsocket = socket.socket |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 56 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 57 | class ProxyError(Exception): pass |
| 58 | class GeneralProxyError(ProxyError): pass |
| 59 | class Socks5AuthError(ProxyError): pass |
| 60 | class Socks5Error(ProxyError): pass |
| 61 | class Socks4Error(ProxyError): pass |
| 62 | class HTTPError(ProxyError): pass |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 63 | |
| 64 | _generalerrors = ("success", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 65 | "invalid data", |
| 66 | "not connected", |
| 67 | "not available", |
| 68 | "bad proxy type", |
| 69 | "bad input") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 70 | |
| 71 | _socks5errors = ("succeeded", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 72 | "general SOCKS server failure", |
| 73 | "connection not allowed by ruleset", |
| 74 | "Network unreachable", |
| 75 | "Host unreachable", |
| 76 | "Connection refused", |
| 77 | "TTL expired", |
| 78 | "Command not supported", |
| 79 | "Address type not supported", |
| 80 | "Unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 81 | |
| 82 | _socks5autherrors = ("succeeded", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 83 | "authentication is required", |
| 84 | "all offered authentication methods were rejected", |
| 85 | "unknown username or invalid password", |
| 86 | "unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 87 | |
| 88 | _socks4errors = ("request granted", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 89 | "request rejected or failed", |
| 90 | "request rejected because SOCKS server cannot connect to identd on the client", |
| 91 | "request rejected because the client program and identd report different user-ids", |
| 92 | "unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 93 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 94 | def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 95 | """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) |
| 96 | Sets a default proxy which all further socksocket objects will use, |
| 97 | unless explicitly changed. |
| 98 | """ |
| 99 | global _defaultproxy |
| 100 | _defaultproxy = (proxytype, addr, port, rdns, username, password) |
| 101 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 102 | def wrapmodule(module): |
| 103 | """wrapmodule(module) |
| 104 | Attempts to replace a module's socket library with a SOCKS socket. Must set |
| 105 | a default proxy using setdefaultproxy(...) first. |
| 106 | This will only work on modules that import socket directly into the namespace; |
| 107 | most of the Python Standard Library falls into this category. |
| 108 | """ |
| 109 | if _defaultproxy != None: |
| 110 | module.socket.socket = socksocket |
| 111 | else: |
| 112 | raise GeneralProxyError((4, "no proxy specified")) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 113 | |
| 114 | class socksocket(socket.socket): |
| 115 | """socksocket([family[, type[, proto]]]) -> socket object |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 116 | Open a SOCKS enabled socket. The parameters are the same as |
| 117 | those of the standard socket init. In order for SOCKS to work, |
| 118 | you must specify family=AF_INET, type=SOCK_STREAM and proto=0. |
| 119 | """ |
| 120 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 121 | def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): |
| 122 | _orgsocket.__init__(self, family, type, proto, _sock) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 123 | if _defaultproxy != None: |
| 124 | self.__proxy = _defaultproxy |
| 125 | else: |
| 126 | self.__proxy = (None, None, None, None, None, None) |
| 127 | self.__proxysockname = None |
| 128 | self.__proxypeername = None |
| 129 | |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 130 | def __recvall(self, count): |
| 131 | """__recvall(count) -> data |
| 132 | Receive EXACTLY the number of bytes requested from the socket. |
| 133 | Blocks until the required number of bytes have been received. |
| 134 | """ |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 135 | data = self.recv(count) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 136 | while len(data) < count: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 137 | d = self.recv(count-len(data)) |
| 138 | if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) |
| 139 | data = data + d |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 140 | return data |
| 141 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 142 | def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 143 | """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) |
| 144 | Sets the proxy to be used. |
| 145 | proxytype - The type of the proxy to be used. Three types |
| 146 | are supported: PROXY_TYPE_SOCKS4 (including socks4a), |
| 147 | PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP |
| 148 | addr - The address of the server (IP or DNS). |
| 149 | port - The port of the server. Defaults to 1080 for SOCKS |
| 150 | servers and 8080 for HTTP proxy servers. |
| 151 | rdns - Should DNS queries be preformed on the remote side |
| 152 | (rather than the local side). The default is True. |
| 153 | Note: This has no effect with SOCKS4 servers. |
| 154 | username - Username to authenticate with to the server. |
| 155 | The default is no authentication. |
| 156 | password - Password to authenticate with to the server. |
| 157 | Only relevant when username is also provided. |
| 158 | """ |
| 159 | self.__proxy = (proxytype, addr, port, rdns, username, password) |
| 160 | |
| 161 | def __negotiatesocks5(self, destaddr, destport): |
| 162 | """__negotiatesocks5(self,destaddr,destport) |
| 163 | Negotiates a connection through a SOCKS5 server. |
| 164 | """ |
| 165 | # First we'll send the authentication packages we support. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 166 | if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 167 | # The username/password details were supplied to the |
| 168 | # setproxy method so we support the USERNAME/PASSWORD |
| 169 | # authentication (in addition to the standard none). |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 170 | self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 171 | else: |
| 172 | # No username/password were entered, therefore we |
| 173 | # only support connections with no authentication. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 174 | self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 175 | # We'll receive the server's response to determine which |
| 176 | # method was selected |
| 177 | chosenauth = self.__recvall(2) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 178 | if chosenauth[0:1] != chr(0x05).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 179 | self.close() |
| 180 | raise GeneralProxyError((1, _generalerrors[1])) |
| 181 | # Check the chosen authentication method |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 182 | if chosenauth[1:2] == chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 183 | # No authentication is required |
| 184 | pass |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 185 | elif chosenauth[1:2] == chr(0x02).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 186 | # Okay, we need to perform a basic username/password |
| 187 | # authentication. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 188 | self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 189 | authstat = self.__recvall(2) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 190 | if authstat[0:1] != chr(0x01).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 191 | # Bad response |
| 192 | self.close() |
| 193 | raise GeneralProxyError((1, _generalerrors[1])) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 194 | if authstat[1:2] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 195 | # Authentication failed |
| 196 | self.close() |
| 197 | raise Socks5AuthError((3, _socks5autherrors[3])) |
| 198 | # Authentication succeeded |
| 199 | else: |
| 200 | # Reaching here is always bad |
| 201 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 202 | if chosenauth[1] == chr(0xFF).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 203 | raise Socks5AuthError((2, _socks5autherrors[2])) |
| 204 | else: |
| 205 | raise GeneralProxyError((1, _generalerrors[1])) |
| 206 | # Now we can request the actual connection |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 207 | req = struct.pack('BBB', 0x05, 0x01, 0x00) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 208 | # If the given destination address is an IP address, we'll |
| 209 | # use the IPv4 address request even if remote resolving was specified. |
| 210 | try: |
| 211 | ipaddr = socket.inet_aton(destaddr) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 212 | req = req + chr(0x01).encode() + ipaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 213 | except socket.error: |
| 214 | # Well it's not an IP number, so it's probably a DNS name. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 215 | if self.__proxy[3]: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 216 | # Resolve remotely |
| 217 | ipaddr = None |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 218 | req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 219 | else: |
| 220 | # Resolve locally |
| 221 | ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 222 | req = req + chr(0x01).encode() + ipaddr |
| 223 | req = req + struct.pack(">H", destport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 224 | self.sendall(req) |
| 225 | # Get the response |
| 226 | resp = self.__recvall(4) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 227 | if resp[0:1] != chr(0x05).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 228 | self.close() |
| 229 | raise GeneralProxyError((1, _generalerrors[1])) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 230 | elif resp[1:2] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 231 | # Connection failed |
| 232 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 233 | if ord(resp[1:2])<=8: |
| 234 | raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 235 | else: |
| 236 | raise Socks5Error((9, _socks5errors[9])) |
| 237 | # Get the bound address/port |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 238 | elif resp[3:4] == chr(0x01).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 239 | boundaddr = self.__recvall(4) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 240 | elif resp[3:4] == chr(0x03).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 241 | resp = resp + self.recv(1) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 242 | boundaddr = self.__recvall(ord(resp[4:5])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 243 | else: |
| 244 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 245 | raise GeneralProxyError((1,_generalerrors[1])) |
| 246 | boundport = struct.unpack(">H", self.__recvall(2))[0] |
| 247 | self.__proxysockname = (boundaddr, boundport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 248 | if ipaddr != None: |
| 249 | self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) |
| 250 | else: |
| 251 | self.__proxypeername = (destaddr, destport) |
| 252 | |
| 253 | def getproxysockname(self): |
| 254 | """getsockname() -> address info |
| 255 | Returns the bound IP address and port number at the proxy. |
| 256 | """ |
| 257 | return self.__proxysockname |
| 258 | |
| 259 | def getproxypeername(self): |
| 260 | """getproxypeername() -> address info |
| 261 | Returns the IP and port number of the proxy. |
| 262 | """ |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 263 | return _orgsocket.getpeername(self) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 264 | |
| 265 | def getpeername(self): |
| 266 | """getpeername() -> address info |
| 267 | Returns the IP address and port number of the destination |
| 268 | machine (note: getproxypeername returns the proxy) |
| 269 | """ |
| 270 | return self.__proxypeername |
| 271 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 272 | def __negotiatesocks4(self,destaddr,destport): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 273 | """__negotiatesocks4(self,destaddr,destport) |
| 274 | Negotiates a connection through a SOCKS4 server. |
| 275 | """ |
| 276 | # Check if the destination address provided is an IP address |
| 277 | rmtrslv = False |
| 278 | try: |
| 279 | ipaddr = socket.inet_aton(destaddr) |
| 280 | except socket.error: |
| 281 | # It's a DNS name. Check where it should be resolved. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 282 | if self.__proxy[3]: |
| 283 | ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 284 | rmtrslv = True |
| 285 | else: |
| 286 | ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) |
| 287 | # Construct the request packet |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 288 | req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 289 | # The username parameter is considered userid for SOCKS4 |
| 290 | if self.__proxy[4] != None: |
| 291 | req = req + self.__proxy[4] |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 292 | req = req + chr(0x00).encode() |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 293 | # DNS name if remote resolving is required |
| 294 | # NOTE: This is actually an extension to the SOCKS4 protocol |
| 295 | # called SOCKS4A and may not be supported in all cases. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 296 | if rmtrslv: |
| 297 | req = req + destaddr + chr(0x00).encode() |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 298 | self.sendall(req) |
| 299 | # Get the response from the server |
| 300 | resp = self.__recvall(8) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 301 | if resp[0:1] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 302 | # Bad data |
| 303 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 304 | raise GeneralProxyError((1,_generalerrors[1])) |
| 305 | if resp[1:2] != chr(0x5A).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 306 | # Server returned an error |
| 307 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 308 | if ord(resp[1:2]) in (91, 92, 93): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 309 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 310 | raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 311 | else: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 312 | raise Socks4Error((94, _socks4errors[4])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 313 | # Get the bound address/port |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 314 | self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 315 | if rmtrslv != None: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 316 | self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 317 | else: |
| 318 | self.__proxypeername = (destaddr, destport) |
| 319 | |
| 320 | def __negotiatehttp(self, destaddr, destport): |
| 321 | """__negotiatehttp(self,destaddr,destport) |
| 322 | Negotiates a connection through an HTTP server. |
| 323 | """ |
| 324 | # If we need to resolve locally, we do this now |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 325 | if not self.__proxy[3]: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 326 | addr = socket.gethostbyname(destaddr) |
| 327 | else: |
| 328 | addr = destaddr |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 329 | self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode()) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 330 | # We read the response until we get the string "\r\n\r\n" |
| 331 | resp = self.recv(1) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 332 | while resp.find("\r\n\r\n".encode()) == -1: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 333 | resp = resp + self.recv(1) |
| 334 | # We just need the first line to check if the connection |
| 335 | # was successful |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 336 | statusline = resp.splitlines()[0].split(" ".encode(), 2) |
| 337 | if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 338 | self.close() |
| 339 | raise GeneralProxyError((1, _generalerrors[1])) |
| 340 | try: |
| 341 | statuscode = int(statusline[1]) |
| 342 | except ValueError: |
| 343 | self.close() |
| 344 | raise GeneralProxyError((1, _generalerrors[1])) |
| 345 | if statuscode != 200: |
| 346 | self.close() |
| 347 | raise HTTPError((statuscode, statusline[2])) |
| 348 | self.__proxysockname = ("0.0.0.0", 0) |
| 349 | self.__proxypeername = (addr, destport) |
| 350 | |
| 351 | def connect(self, destpair): |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 352 | """connect(self, despair) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 353 | Connects to the specified destination through a proxy. |
| 354 | destpar - A tuple of the IP/DNS address and the port number. |
| 355 | (identical to socket's connect). |
| 356 | To select the proxy server use setproxy(). |
| 357 | """ |
| 358 | # Do a minimal input check first |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 359 | if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 360 | raise GeneralProxyError((5, _generalerrors[5])) |
| 361 | if self.__proxy[0] == PROXY_TYPE_SOCKS5: |
| 362 | if self.__proxy[2] != None: |
| 363 | portnum = self.__proxy[2] |
| 364 | else: |
| 365 | portnum = 1080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 366 | _orgsocket.connect(self, (self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 367 | self.__negotiatesocks5(destpair[0], destpair[1]) |
| 368 | elif self.__proxy[0] == PROXY_TYPE_SOCKS4: |
| 369 | if self.__proxy[2] != None: |
| 370 | portnum = self.__proxy[2] |
| 371 | else: |
| 372 | portnum = 1080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 373 | _orgsocket.connect(self,(self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 374 | self.__negotiatesocks4(destpair[0], destpair[1]) |
| 375 | elif self.__proxy[0] == PROXY_TYPE_HTTP: |
| 376 | if self.__proxy[2] != None: |
| 377 | portnum = self.__proxy[2] |
| 378 | else: |
| 379 | portnum = 8080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 380 | _orgsocket.connect(self,(self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 381 | self.__negotiatehttp(destpair[0], destpair[1]) |
| 382 | elif self.__proxy[0] == None: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 383 | _orgsocket.connect(self, (destpair[0], destpair[1])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 384 | else: |
| 385 | raise GeneralProxyError((4, _generalerrors[4])) |