blob: a458e1cdb58ecd71f15a7318d6d13807fe22df73 [file] [log] [blame]
Joe Gregorio845a5452010-09-08 13:50:34 -04001"""SocksiPy - Python SOCKS module.
2Version 1.00
3
4Copyright 2006 Dan-Haim. All rights reserved.
5
Joe Gregorioe7518002011-06-14 10:56:36 -04006Redistribution and use in source and binary forms, with or without modification,
7are permitted provided that the following conditions are met:
Joe Gregorio845a5452010-09-08 13:50:34 -040081. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
102. 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.
133. 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 Gregorioe7518002011-06-14 10:56:36 -040016
Joe Gregorio845a5452010-09-08 13:50:34 -040017THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
18WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
23OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
Joe Gregorioe7518002011-06-14 10:56:36 -040025OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
Joe Gregorio845a5452010-09-08 13:50:34 -040026
27
28This module provides a standard socket-like interface for Python
29for tunneling connections through SOCKS proxies.
30
31"""
32
33"""
34
35Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
36for use in PyLoris (http://pyloris.sourceforge.net/)
37
38Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
39mainly to merge bug fixes found in Sourceforge
40
41"""
42
43import socket
Joe Gregorioe7518002011-06-14 10:56:36 -040044import struct
45import sys
Joe Gregorio5e3a5fa2010-10-11 13:03:56 -040046
47if getattr(socket, 'socket', None) is None:
48 raise ImportError('socket.socket missing, proxy support unusable')
49
Joe Gregorio845a5452010-09-08 13:50:34 -040050PROXY_TYPE_SOCKS4 = 1
51PROXY_TYPE_SOCKS5 = 2
52PROXY_TYPE_HTTP = 3
53
54_defaultproxy = None
Joe Gregorioe7518002011-06-14 10:56:36 -040055_orgsocket = socket.socket
Joe Gregorio845a5452010-09-08 13:50:34 -040056
Joe Gregorioe7518002011-06-14 10:56:36 -040057class ProxyError(Exception): pass
58class GeneralProxyError(ProxyError): pass
59class Socks5AuthError(ProxyError): pass
60class Socks5Error(ProxyError): pass
61class Socks4Error(ProxyError): pass
62class HTTPError(ProxyError): pass
Joe Gregorio845a5452010-09-08 13:50:34 -040063
64_generalerrors = ("success",
Joe Gregorioe7518002011-06-14 10:56:36 -040065 "invalid data",
66 "not connected",
67 "not available",
68 "bad proxy type",
69 "bad input")
Joe Gregorio845a5452010-09-08 13:50:34 -040070
71_socks5errors = ("succeeded",
Joe Gregorioe7518002011-06-14 10:56:36 -040072 "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 Gregorio845a5452010-09-08 13:50:34 -040081
82_socks5autherrors = ("succeeded",
Joe Gregorioe7518002011-06-14 10:56:36 -040083 "authentication is required",
84 "all offered authentication methods were rejected",
85 "unknown username or invalid password",
86 "unknown error")
Joe Gregorio845a5452010-09-08 13:50:34 -040087
88_socks4errors = ("request granted",
Joe Gregorioe7518002011-06-14 10:56:36 -040089 "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 Gregorio845a5452010-09-08 13:50:34 -040093
Joe Gregorioe7518002011-06-14 10:56:36 -040094def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
Joe Gregorio845a5452010-09-08 13:50:34 -040095 """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 Gregorioe7518002011-06-14 10:56:36 -0400102def 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 Gregorio845a5452010-09-08 13:50:34 -0400113
114class socksocket(socket.socket):
115 """socksocket([family[, type[, proto]]]) -> socket object
Joe Gregorio845a5452010-09-08 13:50:34 -0400116 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 Gregorioe7518002011-06-14 10:56:36 -0400121 def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
122 _orgsocket.__init__(self, family, type, proto, _sock)
Joe Gregorio845a5452010-09-08 13:50:34 -0400123 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 Gregorio845a5452010-09-08 13:50:34 -0400130 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 Gregorioe7518002011-06-14 10:56:36 -0400135 data = self.recv(count)
Joe Gregorio845a5452010-09-08 13:50:34 -0400136 while len(data) < count:
Joe Gregorioe7518002011-06-14 10:56:36 -0400137 d = self.recv(count-len(data))
138 if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
139 data = data + d
Joe Gregorio845a5452010-09-08 13:50:34 -0400140 return data
141
Joe Gregorioe7518002011-06-14 10:56:36 -0400142 def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
Joe Gregorio845a5452010-09-08 13:50:34 -0400143 """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 Gregorioe7518002011-06-14 10:56:36 -0400166 if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
Joe Gregorio845a5452010-09-08 13:50:34 -0400167 # 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 Gregorioe7518002011-06-14 10:56:36 -0400170 self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
Joe Gregorio845a5452010-09-08 13:50:34 -0400171 else:
172 # No username/password were entered, therefore we
173 # only support connections with no authentication.
Joe Gregorioe7518002011-06-14 10:56:36 -0400174 self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
Joe Gregorio845a5452010-09-08 13:50:34 -0400175 # We'll receive the server's response to determine which
176 # method was selected
177 chosenauth = self.__recvall(2)
Joe Gregorioe7518002011-06-14 10:56:36 -0400178 if chosenauth[0:1] != chr(0x05).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400179 self.close()
180 raise GeneralProxyError((1, _generalerrors[1]))
181 # Check the chosen authentication method
Joe Gregorioe7518002011-06-14 10:56:36 -0400182 if chosenauth[1:2] == chr(0x00).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400183 # No authentication is required
184 pass
Joe Gregorioe7518002011-06-14 10:56:36 -0400185 elif chosenauth[1:2] == chr(0x02).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400186 # Okay, we need to perform a basic username/password
187 # authentication.
Joe Gregorioe7518002011-06-14 10:56:36 -0400188 self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
Joe Gregorio845a5452010-09-08 13:50:34 -0400189 authstat = self.__recvall(2)
Joe Gregorioe7518002011-06-14 10:56:36 -0400190 if authstat[0:1] != chr(0x01).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400191 # Bad response
192 self.close()
193 raise GeneralProxyError((1, _generalerrors[1]))
Joe Gregorioe7518002011-06-14 10:56:36 -0400194 if authstat[1:2] != chr(0x00).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400195 # 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 Gregorioe7518002011-06-14 10:56:36 -0400202 if chosenauth[1] == chr(0xFF).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400203 raise Socks5AuthError((2, _socks5autherrors[2]))
204 else:
205 raise GeneralProxyError((1, _generalerrors[1]))
206 # Now we can request the actual connection
Joe Gregorioe7518002011-06-14 10:56:36 -0400207 req = struct.pack('BBB', 0x05, 0x01, 0x00)
Joe Gregorio845a5452010-09-08 13:50:34 -0400208 # 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 Gregorioe7518002011-06-14 10:56:36 -0400212 req = req + chr(0x01).encode() + ipaddr
Joe Gregorio845a5452010-09-08 13:50:34 -0400213 except socket.error:
214 # Well it's not an IP number, so it's probably a DNS name.
Joe Gregorioe7518002011-06-14 10:56:36 -0400215 if self.__proxy[3]:
Joe Gregorio845a5452010-09-08 13:50:34 -0400216 # Resolve remotely
217 ipaddr = None
Joe Gregorioe7518002011-06-14 10:56:36 -0400218 req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr
Joe Gregorio845a5452010-09-08 13:50:34 -0400219 else:
220 # Resolve locally
221 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
Joe Gregorioe7518002011-06-14 10:56:36 -0400222 req = req + chr(0x01).encode() + ipaddr
223 req = req + struct.pack(">H", destport)
Joe Gregorio845a5452010-09-08 13:50:34 -0400224 self.sendall(req)
225 # Get the response
226 resp = self.__recvall(4)
Joe Gregorioe7518002011-06-14 10:56:36 -0400227 if resp[0:1] != chr(0x05).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400228 self.close()
229 raise GeneralProxyError((1, _generalerrors[1]))
Joe Gregorioe7518002011-06-14 10:56:36 -0400230 elif resp[1:2] != chr(0x00).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400231 # Connection failed
232 self.close()
Joe Gregorioe7518002011-06-14 10:56:36 -0400233 if ord(resp[1:2])<=8:
234 raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
Joe Gregorio845a5452010-09-08 13:50:34 -0400235 else:
236 raise Socks5Error((9, _socks5errors[9]))
237 # Get the bound address/port
Joe Gregorioe7518002011-06-14 10:56:36 -0400238 elif resp[3:4] == chr(0x01).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400239 boundaddr = self.__recvall(4)
Joe Gregorioe7518002011-06-14 10:56:36 -0400240 elif resp[3:4] == chr(0x03).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400241 resp = resp + self.recv(1)
Joe Gregorioe7518002011-06-14 10:56:36 -0400242 boundaddr = self.__recvall(ord(resp[4:5]))
Joe Gregorio845a5452010-09-08 13:50:34 -0400243 else:
244 self.close()
Joe Gregorioe7518002011-06-14 10:56:36 -0400245 raise GeneralProxyError((1,_generalerrors[1]))
246 boundport = struct.unpack(">H", self.__recvall(2))[0]
247 self.__proxysockname = (boundaddr, boundport)
Joe Gregorio845a5452010-09-08 13:50:34 -0400248 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 Gregorioe7518002011-06-14 10:56:36 -0400263 return _orgsocket.getpeername(self)
Joe Gregorio845a5452010-09-08 13:50:34 -0400264
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 Gregorioe7518002011-06-14 10:56:36 -0400272 def __negotiatesocks4(self,destaddr,destport):
Joe Gregorio845a5452010-09-08 13:50:34 -0400273 """__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 Gregorioe7518002011-06-14 10:56:36 -0400282 if self.__proxy[3]:
283 ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
Joe Gregorio845a5452010-09-08 13:50:34 -0400284 rmtrslv = True
285 else:
286 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
287 # Construct the request packet
Joe Gregorioe7518002011-06-14 10:56:36 -0400288 req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
Joe Gregorio845a5452010-09-08 13:50:34 -0400289 # The username parameter is considered userid for SOCKS4
290 if self.__proxy[4] != None:
291 req = req + self.__proxy[4]
Joe Gregorioe7518002011-06-14 10:56:36 -0400292 req = req + chr(0x00).encode()
Joe Gregorio845a5452010-09-08 13:50:34 -0400293 # 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 Gregorioe7518002011-06-14 10:56:36 -0400296 if rmtrslv:
297 req = req + destaddr + chr(0x00).encode()
Joe Gregorio845a5452010-09-08 13:50:34 -0400298 self.sendall(req)
299 # Get the response from the server
300 resp = self.__recvall(8)
Joe Gregorioe7518002011-06-14 10:56:36 -0400301 if resp[0:1] != chr(0x00).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400302 # Bad data
303 self.close()
Joe Gregorioe7518002011-06-14 10:56:36 -0400304 raise GeneralProxyError((1,_generalerrors[1]))
305 if resp[1:2] != chr(0x5A).encode():
Joe Gregorio845a5452010-09-08 13:50:34 -0400306 # Server returned an error
307 self.close()
Joe Gregorioe7518002011-06-14 10:56:36 -0400308 if ord(resp[1:2]) in (91, 92, 93):
Joe Gregorio845a5452010-09-08 13:50:34 -0400309 self.close()
Joe Gregorioe7518002011-06-14 10:56:36 -0400310 raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
Joe Gregorio845a5452010-09-08 13:50:34 -0400311 else:
Joe Gregorioe7518002011-06-14 10:56:36 -0400312 raise Socks4Error((94, _socks4errors[4]))
Joe Gregorio845a5452010-09-08 13:50:34 -0400313 # Get the bound address/port
Joe Gregorioe7518002011-06-14 10:56:36 -0400314 self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
Joe Gregorio845a5452010-09-08 13:50:34 -0400315 if rmtrslv != None:
Joe Gregorioe7518002011-06-14 10:56:36 -0400316 self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
Joe Gregorio845a5452010-09-08 13:50:34 -0400317 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 Gregorioe7518002011-06-14 10:56:36 -0400325 if not self.__proxy[3]:
Joe Gregorio845a5452010-09-08 13:50:34 -0400326 addr = socket.gethostbyname(destaddr)
327 else:
328 addr = destaddr
Joe Gregorioe7518002011-06-14 10:56:36 -0400329 self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
Joe Gregorio845a5452010-09-08 13:50:34 -0400330 # We read the response until we get the string "\r\n\r\n"
331 resp = self.recv(1)
Joe Gregorioe7518002011-06-14 10:56:36 -0400332 while resp.find("\r\n\r\n".encode()) == -1:
Joe Gregorio845a5452010-09-08 13:50:34 -0400333 resp = resp + self.recv(1)
334 # We just need the first line to check if the connection
335 # was successful
Joe Gregorioe7518002011-06-14 10:56:36 -0400336 statusline = resp.splitlines()[0].split(" ".encode(), 2)
337 if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
Joe Gregorio845a5452010-09-08 13:50:34 -0400338 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 Gregorioe7518002011-06-14 10:56:36 -0400352 """connect(self, despair)
Joe Gregorio845a5452010-09-08 13:50:34 -0400353 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 Gregorioe7518002011-06-14 10:56:36 -0400359 if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
Joe Gregorio845a5452010-09-08 13:50:34 -0400360 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 Gregorioe7518002011-06-14 10:56:36 -0400366 _orgsocket.connect(self, (self.__proxy[1], portnum))
Joe Gregorio845a5452010-09-08 13:50:34 -0400367 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 Gregorioe7518002011-06-14 10:56:36 -0400373 _orgsocket.connect(self,(self.__proxy[1], portnum))
Joe Gregorio845a5452010-09-08 13:50:34 -0400374 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 Gregorioe7518002011-06-14 10:56:36 -0400380 _orgsocket.connect(self,(self.__proxy[1], portnum))
Joe Gregorio845a5452010-09-08 13:50:34 -0400381 self.__negotiatehttp(destpair[0], destpair[1])
382 elif self.__proxy[0] == None:
Joe Gregorioe7518002011-06-14 10:56:36 -0400383 _orgsocket.connect(self, (destpair[0], destpair[1]))
Joe Gregorio845a5452010-09-08 13:50:34 -0400384 else:
385 raise GeneralProxyError((4, _generalerrors[4]))