Dash Core  0.12.2.1
P2P Digital Currency
proxy_test.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2015 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 import socket
6 
7 from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import *
10 from test_framework.netutil import test_ipv6_local
11 '''
12 Test plan:
13 - Start bitcoind's with different proxy configurations
14 - Use addnode to initiate connections
15 - Verify that proxies are connected to, and the right connection command is given
16 - Proxy configurations to test on bitcoind side:
17  - `-proxy` (proxy everything)
18  - `-onion` (proxy just onions)
19  - `-proxyrandomize` Circuit randomization
20 - Proxy configurations to test on proxy side,
21  - support no authentication (other proxy)
22  - support no authentication + user/pass authentication (Tor)
23  - proxy on IPv6
24 
25 - Create various proxies (as threads)
26 - Create bitcoinds that connect to them
27 - Manipulate the bitcoinds using addnode (onetry) an observe effects
28 
29 addnode connect to IPv4
30 addnode connect to IPv6
31 addnode connect to onion
32 addnode connect to generic DNS name
33 '''
34 
35 
37  def __init__(self):
39  # Create two proxies on different ports
40  # ... one unauthenticated
42  self.conf1.addr = ('127.0.0.1', 13000 + (os.getpid() % 1000))
43  self.conf1.unauth = True
44  self.conf1.auth = False
45  # ... one supporting authenticated and unauthenticated (Tor)
47  self.conf2.addr = ('127.0.0.1', 14000 + (os.getpid() % 1000))
48  self.conf2.unauth = True
49  self.conf2.auth = True
50  if self.have_ipv6:
51  # ... one on IPv6 with similar configuration
53  self.conf3.af = socket.AF_INET6
54  self.conf3.addr = ('::1', 15000 + (os.getpid() % 1000))
55  self.conf3.unauth = True
56  self.conf3.auth = True
57  else:
58  print "Warning: testing without local IPv6 support"
59 
60  self.serv1 = Socks5Server(self.conf1)
61  self.serv1.start()
62  self.serv2 = Socks5Server(self.conf2)
63  self.serv2.start()
64  if self.have_ipv6:
65  self.serv3 = Socks5Server(self.conf3)
66  self.serv3.start()
67 
68  def setup_nodes(self):
69  # Note: proxies are not used to connect to local nodes
70  # this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost
71  args = [
72  ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
73  ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
74  ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
75  []
76  ]
77  if self.have_ipv6:
78  args[3] = ['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion']
79  return start_nodes(4, self.options.tmpdir, extra_args=args)
80 
81  def node_test(self, node, proxies, auth, test_onion=True):
82  rv = []
83  # Test: outgoing IPv4 connection through node
84  node.addnode("15.61.23.23:1234", "onetry")
85  cmd = proxies[0].queue.get()
86  assert(isinstance(cmd, Socks5Command))
87  # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
88  assert_equal(cmd.atyp, AddressType.DOMAINNAME)
89  assert_equal(cmd.addr, b"15.61.23.23")
90  assert_equal(cmd.port, 1234)
91  if not auth:
92  assert_equal(cmd.username, None)
93  assert_equal(cmd.password, None)
94  rv.append(cmd)
95 
96  if self.have_ipv6:
97  # Test: outgoing IPv6 connection through node
98  node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
99  cmd = proxies[1].queue.get()
100  assert(isinstance(cmd, Socks5Command))
101  # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
102  assert_equal(cmd.atyp, AddressType.DOMAINNAME)
103  assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534")
104  assert_equal(cmd.port, 5443)
105  if not auth:
106  assert_equal(cmd.username, None)
107  assert_equal(cmd.password, None)
108  rv.append(cmd)
109 
110  if test_onion:
111  # Test: outgoing onion connection through node
112  node.addnode("bitcoinostk4e4re.onion:8333", "onetry")
113  cmd = proxies[2].queue.get()
114  assert(isinstance(cmd, Socks5Command))
115  assert_equal(cmd.atyp, AddressType.DOMAINNAME)
116  assert_equal(cmd.addr, b"bitcoinostk4e4re.onion")
117  assert_equal(cmd.port, 8333)
118  if not auth:
119  assert_equal(cmd.username, None)
120  assert_equal(cmd.password, None)
121  rv.append(cmd)
122 
123  # Test: outgoing DNS name connection through node
124  node.addnode("node.noumenon:8333", "onetry")
125  cmd = proxies[3].queue.get()
126  assert(isinstance(cmd, Socks5Command))
127  assert_equal(cmd.atyp, AddressType.DOMAINNAME)
128  assert_equal(cmd.addr, b"node.noumenon")
129  assert_equal(cmd.port, 8333)
130  if not auth:
131  assert_equal(cmd.username, None)
132  assert_equal(cmd.password, None)
133  rv.append(cmd)
134 
135  return rv
136 
137  def run_test(self):
138  # basic -proxy
139  self.node_test(self.nodes[0], [self.serv1, self.serv1, self.serv1, self.serv1], False)
140 
141  # -proxy plus -onion
142  self.node_test(self.nodes[1], [self.serv1, self.serv1, self.serv2, self.serv1], False)
143 
144  # -proxy plus -onion, -proxyrandomize
145  rv = self.node_test(self.nodes[2], [self.serv2, self.serv2, self.serv2, self.serv2], True)
146  # Check that credentials as used for -proxyrandomize connections are unique
147  credentials = set((x.username,x.password) for x in rv)
148  assert_equal(len(credentials), len(rv))
149 
150  if self.have_ipv6:
151  # proxy on IPv6 localhost
152  self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False, False)
153 
154  def networks_dict(d):
155  r = {}
156  for x in d['networks']:
157  r[x['name']] = x
158  return r
159 
160  # test RPC getnetworkinfo
161  n0 = networks_dict(self.nodes[0].getnetworkinfo())
162  for net in ['ipv4','ipv6','onion']:
163  assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr))
164  assert_equal(n0[net]['proxy_randomize_credentials'], True)
165  assert_equal(n0['onion']['reachable'], True)
166 
167  n1 = networks_dict(self.nodes[1].getnetworkinfo())
168  for net in ['ipv4','ipv6']:
169  assert_equal(n1[net]['proxy'], '%s:%i' % (self.conf1.addr))
170  assert_equal(n1[net]['proxy_randomize_credentials'], False)
171  assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr))
172  assert_equal(n1['onion']['proxy_randomize_credentials'], False)
173  assert_equal(n1['onion']['reachable'], True)
174 
175  n2 = networks_dict(self.nodes[2].getnetworkinfo())
176  for net in ['ipv4','ipv6','onion']:
177  assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr))
178  assert_equal(n2[net]['proxy_randomize_credentials'], True)
179  assert_equal(n2['onion']['reachable'], True)
180 
181  if self.have_ipv6:
182  n3 = networks_dict(self.nodes[3].getnetworkinfo())
183  for net in ['ipv4','ipv6']:
184  assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
185  assert_equal(n3[net]['proxy_randomize_credentials'], False)
186  assert_equal(n3['onion']['reachable'], False)
187 
188 if __name__ == '__main__':
189  ProxyTest().main()
190 
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None)
Definition: util.py:305
def setup_nodes(self)
Definition: proxy_test.py:68
def node_test(self, node, proxies, auth, test_onion=True)
Definition: proxy_test.py:81
UniValue getnetworkinfo(const UniValue &params, bool fHelp)
Definition: net.cpp:392
def __init__(self)
Definition: proxy_test.py:37
Implementation classes.
Definition: socks5.py:33
def assert_equal(thing1, thing2)
Definition: util.py:461