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