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 | |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 43 | import base64 |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 44 | import socket |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 45 | import struct |
| 46 | import sys |
Joe Gregorio | 5e3a5fa | 2010-10-11 13:03:56 -0400 | [diff] [blame] | 47 | |
| 48 | if getattr(socket, 'socket', None) is None: |
| 49 | raise ImportError('socket.socket missing, proxy support unusable') |
| 50 | |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 51 | PROXY_TYPE_SOCKS4 = 1 |
| 52 | PROXY_TYPE_SOCKS5 = 2 |
| 53 | PROXY_TYPE_HTTP = 3 |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 54 | PROXY_TYPE_HTTP_NO_TUNNEL = 4 |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 55 | |
| 56 | _defaultproxy = None |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 57 | _orgsocket = socket.socket |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 58 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 59 | class ProxyError(Exception): pass |
| 60 | class GeneralProxyError(ProxyError): pass |
| 61 | class Socks5AuthError(ProxyError): pass |
| 62 | class Socks5Error(ProxyError): pass |
| 63 | class Socks4Error(ProxyError): pass |
| 64 | class HTTPError(ProxyError): pass |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 65 | |
| 66 | _generalerrors = ("success", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 67 | "invalid data", |
| 68 | "not connected", |
| 69 | "not available", |
| 70 | "bad proxy type", |
| 71 | "bad input") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 72 | |
| 73 | _socks5errors = ("succeeded", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 74 | "general SOCKS server failure", |
| 75 | "connection not allowed by ruleset", |
| 76 | "Network unreachable", |
| 77 | "Host unreachable", |
| 78 | "Connection refused", |
| 79 | "TTL expired", |
| 80 | "Command not supported", |
| 81 | "Address type not supported", |
| 82 | "Unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 83 | |
| 84 | _socks5autherrors = ("succeeded", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 85 | "authentication is required", |
| 86 | "all offered authentication methods were rejected", |
| 87 | "unknown username or invalid password", |
| 88 | "unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 89 | |
| 90 | _socks4errors = ("request granted", |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 91 | "request rejected or failed", |
| 92 | "request rejected because SOCKS server cannot connect to identd on the client", |
| 93 | "request rejected because the client program and identd report different user-ids", |
| 94 | "unknown error") |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 95 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 96 | 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] | 97 | """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) |
| 98 | Sets a default proxy which all further socksocket objects will use, |
| 99 | unless explicitly changed. |
| 100 | """ |
| 101 | global _defaultproxy |
| 102 | _defaultproxy = (proxytype, addr, port, rdns, username, password) |
| 103 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 104 | def wrapmodule(module): |
| 105 | """wrapmodule(module) |
| 106 | Attempts to replace a module's socket library with a SOCKS socket. Must set |
| 107 | a default proxy using setdefaultproxy(...) first. |
| 108 | This will only work on modules that import socket directly into the namespace; |
| 109 | most of the Python Standard Library falls into this category. |
| 110 | """ |
| 111 | if _defaultproxy != None: |
| 112 | module.socket.socket = socksocket |
| 113 | else: |
| 114 | raise GeneralProxyError((4, "no proxy specified")) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 115 | |
| 116 | class socksocket(socket.socket): |
| 117 | """socksocket([family[, type[, proto]]]) -> socket object |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 118 | Open a SOCKS enabled socket. The parameters are the same as |
| 119 | those of the standard socket init. In order for SOCKS to work, |
| 120 | you must specify family=AF_INET, type=SOCK_STREAM and proto=0. |
| 121 | """ |
| 122 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 123 | def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): |
| 124 | _orgsocket.__init__(self, family, type, proto, _sock) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 125 | if _defaultproxy != None: |
| 126 | self.__proxy = _defaultproxy |
| 127 | else: |
| 128 | self.__proxy = (None, None, None, None, None, None) |
| 129 | self.__proxysockname = None |
| 130 | self.__proxypeername = None |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 131 | self.__httptunnel = True |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 132 | |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 133 | def __recvall(self, count): |
| 134 | """__recvall(count) -> data |
| 135 | Receive EXACTLY the number of bytes requested from the socket. |
| 136 | Blocks until the required number of bytes have been received. |
| 137 | """ |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 138 | data = self.recv(count) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 139 | while len(data) < count: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 140 | d = self.recv(count-len(data)) |
| 141 | if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) |
| 142 | data = data + d |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 143 | return data |
| 144 | |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 145 | def sendall(self, content, *args): |
| 146 | """ override socket.socket.sendall method to rewrite the header |
| 147 | for non-tunneling proxies if needed |
| 148 | """ |
| 149 | if not self.__httptunnel: |
| 150 | content = self.__rewriteproxy(content) |
| 151 | return super(socksocket, self).sendall(content, *args) |
| 152 | |
| 153 | def __rewriteproxy(self, header): |
| 154 | """ rewrite HTTP request headers to support non-tunneling proxies |
| 155 | (i.e. those which do not support the CONNECT method). |
| 156 | This only works for HTTP (not HTTPS) since HTTPS requires tunneling. |
| 157 | """ |
| 158 | host, endpt = None, None |
| 159 | hdrs = header.split("\r\n") |
| 160 | for hdr in hdrs: |
| 161 | if hdr.lower().startswith("host:"): |
| 162 | host = hdr |
| 163 | elif hdr.lower().startswith("get") or hdr.lower().startswith("post"): |
| 164 | endpt = hdr |
| 165 | if host and endpt: |
| 166 | hdrs.remove(host) |
| 167 | hdrs.remove(endpt) |
| 168 | host = host.split(" ")[1] |
| 169 | endpt = endpt.split(" ") |
| 170 | if (self.__proxy[4] != None and self.__proxy[5] != None): |
| 171 | hdrs.insert(0, self.__getauthheader()) |
| 172 | hdrs.insert(0, "Host: %s" % host) |
| 173 | hdrs.insert(0, "%s http://%s%s %s" % (endpt[0], host, endpt[1], endpt[2])) |
| 174 | return "\r\n".join(hdrs) |
| 175 | |
| 176 | def __getauthheader(self): |
| 177 | auth = self.__proxy[4] + ":" + self.__proxy[5] |
| 178 | return "Proxy-Authorization: Basic " + base64.b64encode(auth) |
| 179 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 180 | 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] | 181 | """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) |
| 182 | Sets the proxy to be used. |
| 183 | proxytype - The type of the proxy to be used. Three types |
| 184 | are supported: PROXY_TYPE_SOCKS4 (including socks4a), |
| 185 | PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP |
| 186 | addr - The address of the server (IP or DNS). |
| 187 | port - The port of the server. Defaults to 1080 for SOCKS |
| 188 | servers and 8080 for HTTP proxy servers. |
| 189 | rdns - Should DNS queries be preformed on the remote side |
| 190 | (rather than the local side). The default is True. |
| 191 | Note: This has no effect with SOCKS4 servers. |
| 192 | username - Username to authenticate with to the server. |
| 193 | The default is no authentication. |
| 194 | password - Password to authenticate with to the server. |
| 195 | Only relevant when username is also provided. |
| 196 | """ |
| 197 | self.__proxy = (proxytype, addr, port, rdns, username, password) |
| 198 | |
| 199 | def __negotiatesocks5(self, destaddr, destport): |
| 200 | """__negotiatesocks5(self,destaddr,destport) |
| 201 | Negotiates a connection through a SOCKS5 server. |
| 202 | """ |
| 203 | # First we'll send the authentication packages we support. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 204 | if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 205 | # The username/password details were supplied to the |
| 206 | # setproxy method so we support the USERNAME/PASSWORD |
| 207 | # authentication (in addition to the standard none). |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 208 | self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 209 | else: |
| 210 | # No username/password were entered, therefore we |
| 211 | # only support connections with no authentication. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 212 | self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 213 | # We'll receive the server's response to determine which |
| 214 | # method was selected |
| 215 | chosenauth = self.__recvall(2) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 216 | if chosenauth[0:1] != chr(0x05).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 217 | self.close() |
| 218 | raise GeneralProxyError((1, _generalerrors[1])) |
| 219 | # Check the chosen authentication method |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 220 | if chosenauth[1:2] == chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 221 | # No authentication is required |
| 222 | pass |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 223 | elif chosenauth[1:2] == chr(0x02).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 224 | # Okay, we need to perform a basic username/password |
| 225 | # authentication. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 226 | 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] | 227 | authstat = self.__recvall(2) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 228 | if authstat[0:1] != chr(0x01).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 229 | # Bad response |
| 230 | self.close() |
| 231 | raise GeneralProxyError((1, _generalerrors[1])) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 232 | if authstat[1:2] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 233 | # Authentication failed |
| 234 | self.close() |
| 235 | raise Socks5AuthError((3, _socks5autherrors[3])) |
| 236 | # Authentication succeeded |
| 237 | else: |
| 238 | # Reaching here is always bad |
| 239 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 240 | if chosenauth[1] == chr(0xFF).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 241 | raise Socks5AuthError((2, _socks5autherrors[2])) |
| 242 | else: |
| 243 | raise GeneralProxyError((1, _generalerrors[1])) |
| 244 | # Now we can request the actual connection |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 245 | req = struct.pack('BBB', 0x05, 0x01, 0x00) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 246 | # If the given destination address is an IP address, we'll |
| 247 | # use the IPv4 address request even if remote resolving was specified. |
| 248 | try: |
| 249 | ipaddr = socket.inet_aton(destaddr) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 250 | req = req + chr(0x01).encode() + ipaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 251 | except socket.error: |
| 252 | # 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] | 253 | if self.__proxy[3]: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 254 | # Resolve remotely |
| 255 | ipaddr = None |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 256 | req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 257 | else: |
| 258 | # Resolve locally |
| 259 | ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 260 | req = req + chr(0x01).encode() + ipaddr |
| 261 | req = req + struct.pack(">H", destport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 262 | self.sendall(req) |
| 263 | # Get the response |
| 264 | resp = self.__recvall(4) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 265 | if resp[0:1] != chr(0x05).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 266 | self.close() |
| 267 | raise GeneralProxyError((1, _generalerrors[1])) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 268 | elif resp[1:2] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 269 | # Connection failed |
| 270 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 271 | if ord(resp[1:2])<=8: |
| 272 | raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 273 | else: |
| 274 | raise Socks5Error((9, _socks5errors[9])) |
| 275 | # Get the bound address/port |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 276 | elif resp[3:4] == chr(0x01).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 277 | boundaddr = self.__recvall(4) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 278 | elif resp[3:4] == chr(0x03).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 279 | resp = resp + self.recv(1) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 280 | boundaddr = self.__recvall(ord(resp[4:5])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 281 | else: |
| 282 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 283 | raise GeneralProxyError((1,_generalerrors[1])) |
| 284 | boundport = struct.unpack(">H", self.__recvall(2))[0] |
| 285 | self.__proxysockname = (boundaddr, boundport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 286 | if ipaddr != None: |
| 287 | self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) |
| 288 | else: |
| 289 | self.__proxypeername = (destaddr, destport) |
| 290 | |
| 291 | def getproxysockname(self): |
| 292 | """getsockname() -> address info |
| 293 | Returns the bound IP address and port number at the proxy. |
| 294 | """ |
| 295 | return self.__proxysockname |
| 296 | |
| 297 | def getproxypeername(self): |
| 298 | """getproxypeername() -> address info |
| 299 | Returns the IP and port number of the proxy. |
| 300 | """ |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 301 | return _orgsocket.getpeername(self) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 302 | |
| 303 | def getpeername(self): |
| 304 | """getpeername() -> address info |
| 305 | Returns the IP address and port number of the destination |
| 306 | machine (note: getproxypeername returns the proxy) |
| 307 | """ |
| 308 | return self.__proxypeername |
| 309 | |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 310 | def __negotiatesocks4(self,destaddr,destport): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 311 | """__negotiatesocks4(self,destaddr,destport) |
| 312 | Negotiates a connection through a SOCKS4 server. |
| 313 | """ |
| 314 | # Check if the destination address provided is an IP address |
| 315 | rmtrslv = False |
| 316 | try: |
| 317 | ipaddr = socket.inet_aton(destaddr) |
| 318 | except socket.error: |
| 319 | # It's a DNS name. Check where it should be resolved. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 320 | if self.__proxy[3]: |
| 321 | ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 322 | rmtrslv = True |
| 323 | else: |
| 324 | ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) |
| 325 | # Construct the request packet |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 326 | req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 327 | # The username parameter is considered userid for SOCKS4 |
| 328 | if self.__proxy[4] != None: |
| 329 | req = req + self.__proxy[4] |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 330 | req = req + chr(0x00).encode() |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 331 | # DNS name if remote resolving is required |
| 332 | # NOTE: This is actually an extension to the SOCKS4 protocol |
| 333 | # called SOCKS4A and may not be supported in all cases. |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 334 | if rmtrslv: |
| 335 | req = req + destaddr + chr(0x00).encode() |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 336 | self.sendall(req) |
| 337 | # Get the response from the server |
| 338 | resp = self.__recvall(8) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 339 | if resp[0:1] != chr(0x00).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 340 | # Bad data |
| 341 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 342 | raise GeneralProxyError((1,_generalerrors[1])) |
| 343 | if resp[1:2] != chr(0x5A).encode(): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 344 | # Server returned an error |
| 345 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 346 | if ord(resp[1:2]) in (91, 92, 93): |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 347 | self.close() |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 348 | raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 349 | else: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 350 | raise Socks4Error((94, _socks4errors[4])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 351 | # Get the bound address/port |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 352 | 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] | 353 | if rmtrslv != None: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 354 | self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 355 | else: |
| 356 | self.__proxypeername = (destaddr, destport) |
| 357 | |
| 358 | def __negotiatehttp(self, destaddr, destport): |
| 359 | """__negotiatehttp(self,destaddr,destport) |
| 360 | Negotiates a connection through an HTTP server. |
| 361 | """ |
| 362 | # If we need to resolve locally, we do this now |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 363 | if not self.__proxy[3]: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 364 | addr = socket.gethostbyname(destaddr) |
| 365 | else: |
| 366 | addr = destaddr |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 367 | headers = ["CONNECT ", addr, ":", str(destport), " HTTP/1.1\r\n"] |
| 368 | headers += ["Host: ", destaddr, "\r\n"] |
| 369 | if (self.__proxy[4] != None and self.__proxy[5] != None): |
| 370 | headers += [self.__getauthheader(), "\r\n"] |
| 371 | headers.append("\r\n") |
| 372 | self.sendall("".join(headers).encode()) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 373 | # We read the response until we get the string "\r\n\r\n" |
| 374 | resp = self.recv(1) |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 375 | while resp.find("\r\n\r\n".encode()) == -1: |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 376 | resp = resp + self.recv(1) |
| 377 | # We just need the first line to check if the connection |
| 378 | # was successful |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 379 | statusline = resp.splitlines()[0].split(" ".encode(), 2) |
| 380 | 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] | 381 | self.close() |
| 382 | raise GeneralProxyError((1, _generalerrors[1])) |
| 383 | try: |
| 384 | statuscode = int(statusline[1]) |
| 385 | except ValueError: |
| 386 | self.close() |
| 387 | raise GeneralProxyError((1, _generalerrors[1])) |
| 388 | if statuscode != 200: |
| 389 | self.close() |
| 390 | raise HTTPError((statuscode, statusline[2])) |
| 391 | self.__proxysockname = ("0.0.0.0", 0) |
| 392 | self.__proxypeername = (addr, destport) |
| 393 | |
| 394 | def connect(self, destpair): |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 395 | """connect(self, despair) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 396 | Connects to the specified destination through a proxy. |
| 397 | destpar - A tuple of the IP/DNS address and the port number. |
| 398 | (identical to socket's connect). |
| 399 | To select the proxy server use setproxy(). |
| 400 | """ |
| 401 | # Do a minimal input check first |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 402 | 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] | 403 | raise GeneralProxyError((5, _generalerrors[5])) |
| 404 | if self.__proxy[0] == PROXY_TYPE_SOCKS5: |
| 405 | if self.__proxy[2] != None: |
| 406 | portnum = self.__proxy[2] |
| 407 | else: |
| 408 | portnum = 1080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 409 | _orgsocket.connect(self, (self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 410 | self.__negotiatesocks5(destpair[0], destpair[1]) |
| 411 | elif self.__proxy[0] == PROXY_TYPE_SOCKS4: |
| 412 | if self.__proxy[2] != None: |
| 413 | portnum = self.__proxy[2] |
| 414 | else: |
| 415 | portnum = 1080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 416 | _orgsocket.connect(self,(self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 417 | self.__negotiatesocks4(destpair[0], destpair[1]) |
| 418 | elif self.__proxy[0] == PROXY_TYPE_HTTP: |
| 419 | if self.__proxy[2] != None: |
| 420 | portnum = self.__proxy[2] |
| 421 | else: |
| 422 | portnum = 8080 |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 423 | _orgsocket.connect(self,(self.__proxy[1], portnum)) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 424 | self.__negotiatehttp(destpair[0], destpair[1]) |
Joe Gregorio | 5b457c3 | 2011-10-07 08:32:30 -0400 | [diff] [blame] | 425 | elif self.__proxy[0] == PROXY_TYPE_HTTP_NO_TUNNEL: |
| 426 | if self.__proxy[2] != None: |
| 427 | portnum = self.__proxy[2] |
| 428 | else: |
| 429 | portnum = 8080 |
| 430 | _orgsocket.connect(self,(self.__proxy[1],portnum)) |
| 431 | if destpair[1] == 443: |
| 432 | self.__negotiatehttp(destpair[0],destpair[1]) |
| 433 | else: |
| 434 | self.__httptunnel = False |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 435 | elif self.__proxy[0] == None: |
Joe Gregorio | e751800 | 2011-06-14 10:56:36 -0400 | [diff] [blame] | 436 | _orgsocket.connect(self, (destpair[0], destpair[1])) |
Joe Gregorio | 845a545 | 2010-09-08 13:50:34 -0400 | [diff] [blame] | 437 | else: |
| 438 | raise GeneralProxyError((4, _generalerrors[4])) |