Dash Core  0.12.2.1
P2P Digital Currency
socks5.py
Go to the documentation of this file.
1 # Copyright (c) 2015 The Bitcoin Core developers
2 # Distributed under the MIT software license, see the accompanying
3 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 '''
5 Dummy Socks5 server for testing.
6 '''
7 from __future__ import print_function, division, unicode_literals
8 import socket, threading, Queue
9 import traceback, sys
10 
11 
12 class Command:
13  CONNECT = 0x01
14 
16  IPV4 = 0x01
17  DOMAINNAME = 0x03
18  IPV6 = 0x04
19 
20 
21 def recvall(s, n):
22  '''Receive n bytes from a socket, or fail'''
23  rv = bytearray()
24  while n > 0:
25  d = s.recv(n)
26  if not d:
27  raise IOError('Unexpected end of stream')
28  rv.extend(d)
29  n -= len(d)
30  return rv
31 
32 
33 class Socks5Configuration(object):
34  '''Proxy configuration'''
35  def __init__(self):
36  self.addr = None # Bind address (must be set)
37  self.af = socket.AF_INET # Bind address family
38  self.unauth = False # Support unauthenticated
39  self.auth = False # Support authentication
40 
41 class Socks5Command(object):
42  '''Information about an incoming socks5 command'''
43  def __init__(self, cmd, atyp, addr, port, username, password):
44  self.cmd = cmd # Command (one of Command.*)
45  self.atyp = atyp # Address type (one of AddressType.*)
46  self.addr = addr # Address
47  self.port = port # Port to connect to
48  self.username = username
49  self.password = password
50  def __repr__(self):
51  return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password)
52 
53 class Socks5Connection(object):
54  def __init__(self, serv, conn, peer):
55  self.serv = serv
56  self.conn = conn
57  self.peer = peer
58 
59  def handle(self):
60  '''
61  Handle socks5 request according to RFC1928
62  '''
63  try:
64  # Verify socks version
65  ver = recvall(self.conn, 1)[0]
66  if ver != 0x05:
67  raise IOError('Invalid socks version %i' % ver)
68  # Choose authentication method
69  nmethods = recvall(self.conn, 1)[0]
70  methods = bytearray(recvall(self.conn, nmethods))
71  method = None
72  if 0x02 in methods and self.serv.conf.auth:
73  method = 0x02 # username/password
74  elif 0x00 in methods and self.serv.conf.unauth:
75  method = 0x00 # unauthenticated
76  if method is None:
77  raise IOError('No supported authentication method was offered')
78  # Send response
79  self.conn.sendall(bytearray([0x05, method]))
80  # Read authentication (optional)
81  username = None
82  password = None
83  if method == 0x02:
84  ver = recvall(self.conn, 1)[0]
85  if ver != 0x01:
86  raise IOError('Invalid auth packet version %i' % ver)
87  ulen = recvall(self.conn, 1)[0]
88  username = str(recvall(self.conn, ulen))
89  plen = recvall(self.conn, 1)[0]
90  password = str(recvall(self.conn, plen))
91  # Send authentication response
92  self.conn.sendall(bytearray([0x01, 0x00]))
93 
94  # Read connect request
95  (ver,cmd,rsv,atyp) = recvall(self.conn, 4)
96  if ver != 0x05:
97  raise IOError('Invalid socks version %i in connect request' % ver)
98  if cmd != Command.CONNECT:
99  raise IOError('Unhandled command %i in connect request' % cmd)
100 
101  if atyp == AddressType.IPV4:
102  addr = recvall(self.conn, 4)
103  elif atyp == AddressType.DOMAINNAME:
104  n = recvall(self.conn, 1)[0]
105  addr = recvall(self.conn, n)
106  elif atyp == AddressType.IPV6:
107  addr = recvall(self.conn, 16)
108  else:
109  raise IOError('Unknown address type %i' % atyp)
110  port_hi,port_lo = recvall(self.conn, 2)
111  port = (port_hi << 8) | port_lo
112 
113  # Send dummy response
114  self.conn.sendall(bytearray([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
115 
116  cmdin = Socks5Command(cmd, atyp, addr, port, username, password)
117  self.serv.queue.put(cmdin)
118  print('Proxy: ', cmdin)
119  # Fall through to disconnect
120  except Exception as e:
121  traceback.print_exc(file=sys.stderr)
122  self.serv.queue.put(e)
123  finally:
124  self.conn.close()
125 
126 class Socks5Server(object):
127  def __init__(self, conf):
128  self.conf = conf
129  self.s = socket.socket(conf.af)
130  self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
131  self.s.bind(conf.addr)
132  self.s.listen(5)
133  self.running = False
134  self.thread = None
135  self.queue = Queue.Queue() # report connections and exceptions to client
136 
137  def run(self):
138  while self.running:
139  (sockconn, peer) = self.s.accept()
140  if self.running:
141  conn = Socks5Connection(self, sockconn, peer)
142  thread = threading.Thread(None, conn.handle)
143  thread.daemon = True
144  thread.start()
145 
146  def start(self):
147  assert(not self.running)
148  self.running = True
149  self.thread = threading.Thread(None, self.run)
150  self.thread.daemon = True
151  self.thread.start()
152 
153  def stop(self):
154  self.running = False
155  # connect to self to end run loop
156  s = socket.socket(self.conf.af)
157  s.connect(self.conf.addr)
158  s.close()
159  self.thread.join()
160 
def __init__(self, cmd, atyp, addr, port, username, password)
Definition: socks5.py:43
Protocol constants.
Definition: socks5.py:12
def recvall(s, n)
Definition: socks5.py:21
Implementation classes.
Definition: socks5.py:33
def __init__(self, serv, conn, peer)
Definition: socks5.py:54