feature_proxy.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2015-present 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  
   7  Test plan:
   8  - Start bitcoind's with different proxy configurations
   9  - Use addnode to initiate connections
  10  - Verify that proxies are connected to, and the right connection command is given
  11  - Proxy configurations to test on bitcoind side:
  12      - `-proxy` (proxy everything)
  13      - `-onion` (proxy just onions)
  14      - `-proxyrandomize` Circuit randomization
  15      - `-cjdnsreachable`
  16  - Proxy configurations to test on proxy side,
  17      - support no authentication (other proxy)
  18      - support no authentication + user/pass authentication (Tor)
  19      - proxy on IPv6
  20      - proxy over unix domain sockets
  21  
  22  - Create various proxies (as threads)
  23  - Create nodes that connect to them
  24  - Manipulate the peer connections using addnode (onetry) and observe effects
  25  - Test the getpeerinfo `network` field for the peer
  26  
  27  addnode connect to IPv4
  28  addnode connect to IPv6
  29  addnode connect to onion
  30  addnode connect to generic DNS name
  31  addnode connect to a CJDNS address
  32  
  33  - Test getnetworkinfo for each node
  34  
  35  - Test passing invalid -proxy
  36  - Test passing invalid -onion
  37  - Test passing invalid -i2psam
  38  - Test passing -onlynet=onion without -proxy or -onion
  39  - Test passing -onlynet=onion with -onion=0 and with -noonion
  40  - Test passing unknown -onlynet
  41  """
  42  
  43  import os
  44  import socket
  45  import tempfile
  46  
  47  from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
  48  from test_framework.test_framework import BitcoinTestFramework
  49  from test_framework.util import assert_equal
  50  from test_framework.netutil import test_ipv6_local, test_unix_socket
  51  
  52  # Networks returned by RPC getpeerinfo.
  53  NET_UNROUTABLE = "not_publicly_routable"
  54  NET_IPV4 = "ipv4"
  55  NET_IPV6 = "ipv6"
  56  NET_ONION = "onion"
  57  NET_I2P = "i2p"
  58  NET_CJDNS = "cjdns"
  59  
  60  # Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
  61  NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS})
  62  
  63  # Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit
  64  socket_path = tempfile.NamedTemporaryFile().name
  65  
  66  class ProxyTest(BitcoinTestFramework):
  67      def set_test_params(self):
  68          self.num_nodes = 7
  69          self.setup_clean_chain = True
  70  
  71      def setup_nodes(self):
  72          self.have_ipv6 = test_ipv6_local()
  73          self.have_unix_sockets = test_unix_socket()
  74          # Create two proxies on different ports.
  75          # Use port=0 to let the OS assign available ports, avoiding
  76          # "address already in use" errors from concurrent tests.
  77          # ... one unauthenticated
  78          self.conf1 = Socks5Configuration()
  79          self.conf1.addr = ('127.0.0.1', 0)
  80          self.conf1.unauth = True
  81          self.conf1.auth = False
  82          # ... one supporting authenticated and unauthenticated (Tor)
  83          self.conf2 = Socks5Configuration()
  84          self.conf2.addr = ('127.0.0.1', 0)
  85          self.conf2.unauth = True
  86          self.conf2.auth = True
  87          if self.have_ipv6:
  88              # ... one on IPv6 with similar configuration
  89              self.conf3 = Socks5Configuration()
  90              self.conf3.af = socket.AF_INET6
  91              self.conf3.addr = ('::1', 0)
  92              self.conf3.unauth = True
  93              self.conf3.auth = True
  94          else:
  95              self.log.warning("Testing without local IPv6 support")
  96  
  97          if self.have_unix_sockets:
  98              self.conf4 = Socks5Configuration()
  99              self.conf4.af = socket.AF_UNIX
 100              self.conf4.addr = socket_path
 101              self.conf4.unauth = True
 102              self.conf4.auth = True
 103          else:
 104              self.log.warning("Testing without local unix domain sockets support")
 105  
 106          self.serv1 = Socks5Server(self.conf1)
 107          self.serv1.start()
 108          self.serv2 = Socks5Server(self.conf2)
 109          self.serv2.start()
 110          if self.have_ipv6:
 111              self.serv3 = Socks5Server(self.conf3)
 112              self.serv3.start()
 113          if self.have_unix_sockets:
 114              self.serv4 = Socks5Server(self.conf4)
 115              self.serv4.start()
 116  
 117          # We will not try to connect to this.
 118          self.i2p_sam = ('127.0.0.1', 7656)
 119  
 120          # Note: proxies are not used to connect to local nodes. This is because the proxy to
 121          # use is based on CService.GetNetwork(), which returns NET_UNROUTABLE for localhost.
 122          args = [
 123              ['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1'],
 124              ['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}',f'-onion={self.conf2.addr[0]}:{self.conf2.addr[1]}',
 125                  f'-i2psam={self.i2p_sam[0]}:{self.i2p_sam[1]}', '-i2pacceptincoming=0', '-proxyrandomize=0'],
 126              ['-listen', f'-proxy={self.conf2.addr[0]}:{self.conf2.addr[1]}','-proxyrandomize=1'],
 127              [],
 128              ['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1',
 129                  '-cjdnsreachable'],
 130              [],
 131              []
 132          ]
 133          if self.have_ipv6:
 134              args[3] = ['-listen', f'-proxy=[{self.conf3.addr[0]}]:{self.conf3.addr[1]}','-proxyrandomize=0', '-noonion']
 135          if self.have_unix_sockets:
 136              args[5] = ['-listen', f'-proxy=unix:{socket_path}']
 137              args[6] = ['-listen', f'-onion=unix:{socket_path}']
 138          # This test launches many nodes; disable prevout prefetching so we don't spin up a
 139          # thread pool for each one.
 140          args = [a + ['-prevoutfetchthreads=0'] for a in args]
 141          self.add_nodes(self.num_nodes, extra_args=args)
 142          self.start_nodes()
 143  
 144      def network_test(self, node, addr, network):
 145          for peer in node.getpeerinfo():
 146              if peer["addr"] == addr:
 147                  assert_equal(peer["network"], network)
 148  
 149      def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
 150          rv = []
 151          addr = "15.61.23.23:1234"
 152          self.log.debug(f"Test: outgoing IPv4 connection through node {node.index} for address {addr}")
 153          # v2transport=False is used to avoid reconnections with v1 being scheduled. These could interfere with later checks.
 154          node.addnode(addr, "onetry", v2transport=False)
 155          cmd = proxies[0].queue.get()
 156          assert isinstance(cmd, Socks5Command)
 157          # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
 158          assert_equal(cmd.atyp, AddressType.DOMAINNAME)
 159          assert_equal(cmd.addr, b"15.61.23.23")
 160          assert_equal(cmd.port, 1234)
 161          if not auth:
 162              assert_equal(cmd.username, None)
 163              assert_equal(cmd.password, None)
 164          rv.append(cmd)
 165          self.network_test(node, addr, network=NET_IPV4)
 166  
 167          if self.have_ipv6:
 168              addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
 169              self.log.debug(f"Test: outgoing IPv6 connection through node {node.index} for address {addr}")
 170              node.addnode(addr, "onetry", v2transport=False)
 171              cmd = proxies[1].queue.get()
 172              assert isinstance(cmd, Socks5Command)
 173              # Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
 174              assert_equal(cmd.atyp, AddressType.DOMAINNAME)
 175              assert_equal(cmd.addr, b"1233:3432:2434:2343:3234:2345:6546:4534")
 176              assert_equal(cmd.port, 5443)
 177              if not auth:
 178                  assert_equal(cmd.username, None)
 179                  assert_equal(cmd.password, None)
 180              rv.append(cmd)
 181              self.network_test(node, addr, network=NET_IPV6)
 182  
 183          if test_onion:
 184              addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
 185              self.log.debug(f"Test: outgoing onion connection through node {node.index} for address {addr}")
 186              node.addnode(addr, "onetry", v2transport=False)
 187              cmd = proxies[2].queue.get()
 188              assert isinstance(cmd, Socks5Command)
 189              assert_equal(cmd.atyp, AddressType.DOMAINNAME)
 190              assert_equal(cmd.addr, b"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")
 191              assert_equal(cmd.port, 8333)
 192              if not auth:
 193                  assert_equal(cmd.username, None)
 194                  assert_equal(cmd.password, None)
 195              rv.append(cmd)
 196              self.network_test(node, addr, network=NET_ONION)
 197  
 198          if test_cjdns:
 199              addr = "[fc00:1:2:3:4:5:6:7]:8888"
 200              self.log.debug(f"Test: outgoing CJDNS connection through node {node.index} for address {addr}")
 201              node.addnode(addr, "onetry", v2transport=False)
 202              cmd = proxies[1].queue.get()
 203              assert isinstance(cmd, Socks5Command)
 204              assert_equal(cmd.atyp, AddressType.DOMAINNAME)
 205              assert_equal(cmd.addr, b"fc00:1:2:3:4:5:6:7")
 206              assert_equal(cmd.port, 8888)
 207              if not auth:
 208                  assert_equal(cmd.username, None)
 209                  assert_equal(cmd.password, None)
 210              rv.append(cmd)
 211              self.network_test(node, addr, network=NET_CJDNS)
 212  
 213          addr = "node.noumenon:8333"
 214          self.log.debug(f"Test: outgoing DNS name connection through node {node.index} for address {addr}")
 215          node.addnode(addr, "onetry", v2transport=False)
 216          cmd = proxies[3].queue.get()
 217          assert isinstance(cmd, Socks5Command)
 218          assert_equal(cmd.atyp, AddressType.DOMAINNAME)
 219          assert_equal(cmd.addr, b"node.noumenon")
 220          assert_equal(cmd.port, 8333)
 221          if not auth:
 222              assert_equal(cmd.username, None)
 223              assert_equal(cmd.password, None)
 224          rv.append(cmd)
 225          self.network_test(node, addr, network=NET_UNROUTABLE)
 226  
 227          return rv
 228  
 229      def run_test(self):
 230          # basic -proxy
 231          self.node_test(self.nodes[0],
 232              proxies=[self.serv1, self.serv1, self.serv1, self.serv1],
 233              auth=False, test_onion=True, test_cjdns=False)
 234  
 235          # -proxy plus -onion
 236          self.node_test(self.nodes[1],
 237              proxies=[self.serv1, self.serv1, self.serv2, self.serv1],
 238              auth=False, test_onion=True, test_cjdns=False)
 239  
 240          # -proxy plus -onion, -proxyrandomize
 241          rv = self.node_test(self.nodes[2],
 242              proxies=[self.serv2, self.serv2, self.serv2, self.serv2],
 243              auth=True, test_onion=True, test_cjdns=False)
 244          # Check that credentials as used for -proxyrandomize connections are unique
 245          credentials = set((x.username,x.password) for x in rv)
 246          assert_equal(len(credentials), len(rv))
 247  
 248          if self.have_ipv6:
 249              # proxy on IPv6 localhost
 250              self.node_test(self.nodes[3],
 251                  proxies=[self.serv3, self.serv3, self.serv3, self.serv3],
 252                  auth=False, test_onion=False, test_cjdns=False)
 253  
 254          # -proxy=unauth -proxyrandomize=1 -cjdnsreachable
 255          self.node_test(self.nodes[4],
 256              proxies=[self.serv1, self.serv1, self.serv1, self.serv1],
 257              auth=False, test_onion=True, test_cjdns=True)
 258  
 259          if self.have_unix_sockets:
 260              self.node_test(self.nodes[5],
 261                  proxies=[self.serv4, self.serv4, self.serv4, self.serv4],
 262                  auth=True, test_onion=True, test_cjdns=False)
 263  
 264  
 265          def networks_dict(d):
 266              r = {}
 267              for x in d['networks']:
 268                  r[x['name']] = x
 269              return r
 270  
 271          self.log.info("Test RPC getnetworkinfo")
 272          nodes_network_info = []
 273  
 274          self.log.debug("Test that setting -proxy disables local address discovery, i.e. -discover=0")
 275          for node in self.nodes:
 276              network_info = node.getnetworkinfo()
 277              assert_equal(network_info["localaddresses"], [])
 278              nodes_network_info.append(network_info)
 279  
 280          n0 = networks_dict(nodes_network_info[0])
 281          assert_equal(NETWORKS, n0.keys())
 282          for net in NETWORKS:
 283              if net == NET_I2P:
 284                  expected_proxy = ''
 285                  expected_randomize = False
 286              else:
 287                  expected_proxy = '%s:%i' % (self.conf1.addr)
 288                  expected_randomize = True
 289              assert_equal(n0[net]['proxy'], expected_proxy)
 290              assert_equal(n0[net]['proxy_randomize_credentials'], expected_randomize)
 291          assert_equal(n0['onion']['reachable'], True)
 292          assert_equal(n0['i2p']['reachable'], False)
 293          assert_equal(n0['cjdns']['reachable'], False)
 294  
 295          n1 = networks_dict(nodes_network_info[1])
 296          assert_equal(NETWORKS, n1.keys())
 297          for net in ['ipv4', 'ipv6']:
 298              assert_equal(n1[net]['proxy'], f'{self.conf1.addr[0]}:{self.conf1.addr[1]}')
 299              assert_equal(n1[net]['proxy_randomize_credentials'], False)
 300          assert_equal(n1['onion']['proxy'], f'{self.conf2.addr[0]}:{self.conf2.addr[1]}')
 301          assert_equal(n1['onion']['proxy_randomize_credentials'], False)
 302          assert_equal(n1['onion']['reachable'], True)
 303          assert_equal(n1['i2p']['proxy'], f'{self.i2p_sam[0]}:{self.i2p_sam[1]}')
 304          assert_equal(n1['i2p']['proxy_randomize_credentials'], False)
 305          assert_equal(n1['i2p']['reachable'], True)
 306  
 307          n2 = networks_dict(nodes_network_info[2])
 308          assert_equal(NETWORKS, n2.keys())
 309          proxy = f'{self.conf2.addr[0]}:{self.conf2.addr[1]}'
 310          for net in NETWORKS:
 311              if net == NET_I2P:
 312                  expected_proxy = ''
 313                  expected_randomize = False
 314              else:
 315                  expected_proxy = proxy
 316                  expected_randomize = True
 317              assert_equal(n2[net]['proxy'], expected_proxy)
 318              assert_equal(n2[net]['proxy_randomize_credentials'], expected_randomize)
 319          assert_equal(n2['onion']['reachable'], True)
 320          assert_equal(n2['i2p']['reachable'], False)
 321          assert_equal(n2['cjdns']['reachable'], False)
 322  
 323          if self.have_ipv6:
 324              n3 = networks_dict(nodes_network_info[3])
 325              assert_equal(NETWORKS, n3.keys())
 326              proxy = f'[{self.conf3.addr[0]}]:{self.conf3.addr[1]}'
 327              for net in NETWORKS:
 328                  expected_proxy = '' if net == NET_I2P or net == NET_ONION else proxy
 329                  assert_equal(n3[net]['proxy'], expected_proxy)
 330                  assert_equal(n3[net]['proxy_randomize_credentials'], False)
 331              assert_equal(n3['onion']['reachable'], False)
 332              assert_equal(n3['i2p']['reachable'], False)
 333              assert_equal(n3['cjdns']['reachable'], False)
 334  
 335          n4 = networks_dict(nodes_network_info[4])
 336          assert_equal(NETWORKS, n4.keys())
 337          for net in NETWORKS:
 338              if net == NET_I2P:
 339                  expected_proxy = ''
 340                  expected_randomize = False
 341              else:
 342                  expected_proxy = '%s:%i' % (self.conf1.addr)
 343                  expected_randomize = True
 344              assert_equal(n4[net]['proxy'], expected_proxy)
 345              assert_equal(n4[net]['proxy_randomize_credentials'], expected_randomize)
 346          assert_equal(n4['onion']['reachable'], True)
 347          assert_equal(n4['i2p']['reachable'], False)
 348          assert_equal(n4['cjdns']['reachable'], True)
 349  
 350          if self.have_unix_sockets:
 351              n5 = networks_dict(nodes_network_info[5])
 352              assert_equal(NETWORKS, n5.keys())
 353              for net in NETWORKS:
 354                  if net == NET_I2P:
 355                      expected_proxy = ''
 356                      expected_randomize = False
 357                  else:
 358                      expected_proxy = 'unix:' + self.conf4.addr # no port number
 359                      expected_randomize = True
 360                  assert_equal(n5[net]['proxy'], expected_proxy)
 361                  assert_equal(n5[net]['proxy_randomize_credentials'], expected_randomize)
 362              assert_equal(n5['onion']['reachable'], True)
 363              assert_equal(n5['i2p']['reachable'], False)
 364              assert_equal(n5['cjdns']['reachable'], False)
 365  
 366              n6 = networks_dict(nodes_network_info[6])
 367              assert_equal(NETWORKS, n6.keys())
 368              for net in NETWORKS:
 369                  if net != NET_ONION:
 370                      expected_proxy = ''
 371                      expected_randomize = False
 372                  else:
 373                      expected_proxy = 'unix:' + self.conf4.addr # no port number
 374                      expected_randomize = True
 375                  assert_equal(n6[net]['proxy'], expected_proxy)
 376                  assert_equal(n6[net]['proxy_randomize_credentials'], expected_randomize)
 377              assert_equal(n6['onion']['reachable'], True)
 378              assert_equal(n6['i2p']['reachable'], False)
 379              assert_equal(n6['cjdns']['reachable'], False)
 380  
 381          self.stop_node(1)
 382  
 383          self.log.info("Test passing invalid -proxy hostname raises expected init error")
 384          self.nodes[1].extra_args = ["-proxy=abc..abc:23456"]
 385          msg = "Error: Invalid -proxy address or hostname: 'abc..abc:23456'"
 386          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 387  
 388          self.log.info("Test passing invalid -proxy port raises expected init error")
 389          self.nodes[1].extra_args = ["-proxy=192.0.0.1:def"]
 390          msg = "Error: Invalid port specified in -proxy: '192.0.0.1:def'"
 391          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 392  
 393          self.log.info("Test passing invalid -onion hostname raises expected init error")
 394          self.nodes[1].extra_args = ["-onion=xyz..xyz:23456"]
 395          msg = "Error: Invalid -onion address or hostname: 'xyz..xyz:23456'"
 396          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 397  
 398          self.log.info("Test passing invalid -onion port raises expected init error")
 399          self.nodes[1].extra_args = ["-onion=192.0.0.1:def"]
 400          msg = "Error: Invalid port specified in -onion: '192.0.0.1:def'"
 401          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 402  
 403          self.log.info("Test passing invalid -i2psam hostname raises expected init error")
 404          self.nodes[1].extra_args = ["-i2psam=def..def:23456"]
 405          msg = "Error: Invalid -i2psam address or hostname: 'def..def:23456'"
 406          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 407  
 408          self.log.info("Test passing invalid -i2psam port raises expected init error")
 409          self.nodes[1].extra_args = ["-i2psam=192.0.0.1:def"]
 410          msg = "Error: Invalid port specified in -i2psam: '192.0.0.1:def'"
 411          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 412  
 413          self.log.info("Test passing invalid -onlynet=i2p without -i2psam raises expected init error")
 414          self.nodes[1].extra_args = ["-onlynet=i2p"]
 415          msg = "Error: Outbound connections restricted to i2p (-onlynet=i2p) but -i2psam is not provided"
 416          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 417  
 418          self.log.info("Test passing invalid -onlynet=cjdns without -cjdnsreachable raises expected init error")
 419          self.nodes[1].extra_args = ["-onlynet=cjdns"]
 420          msg = "Error: Outbound connections restricted to CJDNS (-onlynet=cjdns) but -cjdnsreachable is not provided"
 421          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 422  
 423          self.log.info("Test passing -onlynet=onion with -onion=0/-noonion raises expected init error")
 424          msg = (
 425              "Error: Outbound connections restricted to Tor (-onlynet=onion) but "
 426              "the proxy for reaching the Tor network is explicitly forbidden: -onion=0"
 427          )
 428          for arg in ["-onion=0", "-noonion"]:
 429              self.nodes[1].extra_args = ["-onlynet=onion", arg]
 430              self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 431  
 432          self.log.info("Test passing -onlynet=onion without -proxy, -onion or -listenonion raises expected init error")
 433          self.nodes[1].extra_args = ["-onlynet=onion", "-listenonion=0"]
 434          msg = (
 435              "Error: Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
 436              "reaching the Tor network is not provided: none of -proxy, -onion or -listenonion is given"
 437          )
 438          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 439  
 440          self.log.info("Test passing -onlynet=onion without -proxy or -onion but with -listenonion=1 is ok")
 441          self.start_node(1, extra_args=["-onlynet=onion", "-listenonion=1"])
 442          self.stop_node(1)
 443  
 444          self.log.info("Test passing unknown network to -onlynet raises expected init error")
 445          self.nodes[1].extra_args = ["-onlynet=abc"]
 446          msg = "Error: Unknown network specified in -onlynet: 'abc'"
 447          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 448  
 449          self.log.info("Test passing trailing '=' raises expected init error")
 450          self.nodes[1].extra_args = ["-proxy=127.0.0.1:9050="]
 451          msg = "Error: Invalid -proxy address or hostname, ends with '=': '127.0.0.1:9050='"
 452          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 453  
 454          self.log.info("Test passing unrecognized network raises expected init error")
 455          self.nodes[1].extra_args = ["-proxy=127.0.0.1:9050=foo"]
 456          msg = "Error: Unrecognized network in -proxy='127.0.0.1:9050=foo': 'foo'"
 457          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 458  
 459          self.log.info("Test passing proxy only for IPv6")
 460          self.start_node(1, extra_args=["-proxy=127.6.6.6:6666=ipv6"])
 461          nets = networks_dict(self.nodes[1].getnetworkinfo())
 462          assert_equal(nets["ipv4"]["proxy"], "")
 463          assert_equal(nets["ipv6"]["proxy"], "127.6.6.6:6666")
 464          self.stop_node(1)
 465  
 466          self.log.info("Test passing separate proxy for IPv4 and IPv6")
 467          self.start_node(1, extra_args=["-proxy=127.4.4.4:4444=ipv4", "-proxy=127.6.6.6:6666=ipv6"])
 468          nets = networks_dict(self.nodes[1].getnetworkinfo())
 469          assert_equal(nets["ipv4"]["proxy"], "127.4.4.4:4444")
 470          assert_equal(nets["ipv6"]["proxy"], "127.6.6.6:6666")
 471          self.stop_node(1)
 472  
 473          self.log.info("Test overriding the Onion proxy")
 474          self.start_node(1, extra_args=["-proxy=127.1.1.1:1111", "-proxy=127.2.2.2:2222=onion"])
 475          nets = networks_dict(self.nodes[1].getnetworkinfo())
 476          assert_equal(nets["ipv4"]["proxy"], "127.1.1.1:1111")
 477          assert_equal(nets["ipv6"]["proxy"], "127.1.1.1:1111")
 478          assert_equal(nets["onion"]["proxy"], "127.2.2.2:2222")
 479          self.stop_node(1)
 480  
 481          self.log.info("Test removing CJDNS proxy")
 482          self.start_node(1, extra_args=["-proxy=127.1.1.1:1111", "-proxy=0=cjdns"])
 483          nets = networks_dict(self.nodes[1].getnetworkinfo())
 484          assert_equal(nets["ipv4"]["proxy"], "127.1.1.1:1111")
 485          assert_equal(nets["ipv6"]["proxy"], "127.1.1.1:1111")
 486          assert_equal(nets["onion"]["proxy"], "127.1.1.1:1111")
 487          assert_equal(nets["cjdns"]["proxy"], "")
 488          self.stop_node(1)
 489  
 490          self.log.info("Test passing too-long unix path to -proxy raises init error")
 491          self.nodes[1].extra_args = [f"-proxy=unix:{'x' * 1000}"]
 492          if self.have_unix_sockets:
 493              msg = f"Error: Invalid -proxy address or hostname: 'unix:{'x' * 1000}'"
 494          else:
 495              # If unix sockets are not supported, the file path is incorrectly interpreted as host:port
 496              msg = f"Error: Invalid port specified in -proxy: 'unix:{'x' * 1000}'"
 497          self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
 498  
 499          # Cleanup socket path we established outside the individual test directory.
 500          if self.have_unix_sockets:
 501              os.unlink(socket_path)
 502  
 503  if __name__ == '__main__':
 504      ProxyTest(__file__).main()
 505