You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

proxy_test.py 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2015-2016 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. """Test bitcoind with different proxy configuration.
  6. Test plan:
  7. - Start bitcoind's with different proxy configurations
  8. - Use addnode to initiate connections
  9. - Verify that proxies are connected to, and the right connection command is given
  10. - Proxy configurations to test on bitcoind side:
  11. - `-proxy` (proxy everything)
  12. - `-onion` (proxy just onions)
  13. - `-proxyrandomize` Circuit randomization
  14. - Proxy configurations to test on proxy side,
  15. - support no authentication (other proxy)
  16. - support no authentication + user/pass authentication (Tor)
  17. - proxy on IPv6
  18. - Create various proxies (as threads)
  19. - Create bitcoinds that connect to them
  20. - Manipulate the bitcoinds using addnode (onetry) an observe effects
  21. addnode connect to IPv4
  22. addnode connect to IPv6
  23. addnode connect to onion
  24. addnode connect to generic DNS name
  25. """
  26. import socket
  27. import os
  28. from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
  29. from test_framework.test_framework import BitcoinTestFramework
  30. from test_framework.util import (
  31. PORT_MIN,
  32. PORT_RANGE,
  33. start_nodes,
  34. assert_equal,
  35. )
  36. from test_framework.netutil import test_ipv6_local
  37. RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
  38. class ProxyTest(BitcoinTestFramework):
  39. def __init__(self):
  40. super().__init__()
  41. self.num_nodes = 4
  42. self.setup_clean_chain = False
  43. def setup_nodes(self):
  44. self.have_ipv6 = test_ipv6_local()
  45. # Create two proxies on different ports
  46. # ... one unauthenticated
  47. self.conf1 = Socks5Configuration()
  48. self.conf1.addr = ('127.0.0.1', RANGE_BEGIN + (os.getpid() % 1000))
  49. self.conf1.unauth = True
  50. self.conf1.auth = False
  51. # ... one supporting authenticated and unauthenticated (Tor)
  52. self.conf2 = Socks5Configuration()
  53. self.conf2.addr = ('127.0.0.1', RANGE_BEGIN + 1000 + (os.getpid() % 1000))
  54. self.conf2.unauth = True
  55. self.conf2.auth = True
  56. if self.have_ipv6:
  57. # ... one on IPv6 with similar configuration
  58. self.conf3 = Socks5Configuration()
  59. self.conf3.af = socket.AF_INET6
  60. self.conf3.addr = ('::1', RANGE_BEGIN + 2000 + (os.getpid() % 1000))
  61. self.conf3.unauth = True
  62. self.conf3.auth = True
  63. else:
  64. self.log.warning("Testing without local IPv6 support")
  65. self.serv1 = Socks5Server(self.conf1)
  66. self.serv1.start()
  67. self.serv2 = Socks5Server(self.conf2)
  68. self.serv2.start()
  69. if self.have_ipv6:
  70. self.serv3 = Socks5Server(self.conf3)
  71. self.serv3.start()
  72. # Note: proxies are not used to connect to local nodes
  73. # this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost
  74. args = [
  75. ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
  76. ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
  77. ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
  78. []
  79. ]
  80. if self.have_ipv6:
  81. args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion']
  82. return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args)
  83. def node_test(self, node, proxies, auth, test_onion=True):
  84. rv = []
  85. # Test: outgoing IPv4 connection through node
  86. node.addnode("15.61.23.23:1234", "onetry")
  87. cmd = proxies[0].queue.get()
  88. assert(isinstance(cmd, Socks5Command))
  89. # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
  90. assert_equal(cmd.atyp, AddressType.DOMAINNAME)
  91. assert_equal(cmd.addr, b"15.61.23.23")
  92. assert_equal(cmd.port, 1234)
  93. if not auth:
  94. assert_equal(cmd.username, None)
  95. assert_equal(cmd.password, None)
  96. rv.append(cmd)
  97. if self.have_ipv6:
  98. # Test: outgoing IPv6 connection through node
  99. node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
  100. cmd = proxies[1].queue.get()
  101. assert(isinstance(cmd, Socks5Command))
  102. # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
  103. assert_equal(cmd.atyp, AddressType.DOMAINNAME)
  104. assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534")
  105. assert_equal(cmd.port, 5443)
  106. if not auth:
  107. assert_equal(cmd.username, None)
  108. assert_equal(cmd.password, None)
  109. rv.append(cmd)
  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. # Test: outgoing DNS name connection through node
  123. node.addnode("node.noumenon:8333", "onetry")
  124. cmd = proxies[3].queue.get()
  125. assert(isinstance(cmd, Socks5Command))
  126. assert_equal(cmd.atyp, AddressType.DOMAINNAME)
  127. assert_equal(cmd.addr, b"node.noumenon")
  128. assert_equal(cmd.port, 8333)
  129. if not auth:
  130. assert_equal(cmd.username, None)
  131. assert_equal(cmd.password, None)
  132. rv.append(cmd)
  133. return rv
  134. def run_test(self):
  135. # basic -proxy
  136. self.node_test(self.nodes[0], [self.serv1, self.serv1, self.serv1, self.serv1], False)
  137. # -proxy plus -onion
  138. self.node_test(self.nodes[1], [self.serv1, self.serv1, self.serv2, self.serv1], False)
  139. # -proxy plus -onion, -proxyrandomize
  140. rv = self.node_test(self.nodes[2], [self.serv2, self.serv2, self.serv2, self.serv2], True)
  141. # Check that credentials as used for -proxyrandomize connections are unique
  142. credentials = set((x.username,x.password) for x in rv)
  143. assert_equal(len(credentials), len(rv))
  144. if self.have_ipv6:
  145. # proxy on IPv6 localhost
  146. self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False, False)
  147. def networks_dict(d):
  148. r = {}
  149. for x in d['networks']:
  150. r[x['name']] = x
  151. return r
  152. # test RPC getnetworkinfo
  153. n0 = networks_dict(self.nodes[0].getnetworkinfo())
  154. for net in ['ipv4','ipv6','onion']:
  155. assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr))
  156. assert_equal(n0[net]['proxy_randomize_credentials'], True)
  157. assert_equal(n0['onion']['reachable'], True)
  158. n1 = networks_dict(self.nodes[1].getnetworkinfo())
  159. for net in ['ipv4','ipv6']:
  160. assert_equal(n1[net]['proxy'], '%s:%i' % (self.conf1.addr))
  161. assert_equal(n1[net]['proxy_randomize_credentials'], False)
  162. assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr))
  163. assert_equal(n1['onion']['proxy_randomize_credentials'], False)
  164. assert_equal(n1['onion']['reachable'], True)
  165. n2 = networks_dict(self.nodes[2].getnetworkinfo())
  166. for net in ['ipv4','ipv6','onion']:
  167. assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr))
  168. assert_equal(n2[net]['proxy_randomize_credentials'], True)
  169. assert_equal(n2['onion']['reachable'], True)
  170. if self.have_ipv6:
  171. n3 = networks_dict(self.nodes[3].getnetworkinfo())
  172. for net in ['ipv4','ipv6']:
  173. assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
  174. assert_equal(n3[net]['proxy_randomize_credentials'], False)
  175. assert_equal(n3['onion']['reachable'], False)
  176. if __name__ == '__main__':
  177. ProxyTest().main()