1717 - support no authentication (other proxy)
1818 - support no authentication + user/pass authentication (Tor)
1919 - proxy on IPv6
20+ - proxy over unix domain sockets
2021
2122- Create various proxies (as threads)
2223- Create nodes that connect to them
3940- Test passing unknown -onlynet
4041"""
4142
43+ import os
4244import socket
45+ import tempfile
4346
4447from test_framework .socks5 import Socks5Configuration , Socks5Command , Socks5Server , AddressType
4548from test_framework .test_framework import BitcoinTestFramework
4649from test_framework .util import (
4750 assert_equal ,
4851 p2p_port ,
4952)
50- from test_framework .netutil import test_ipv6_local
53+ from test_framework .netutil import test_ipv6_local , test_unix_socket
5154
5255# Networks returned by RPC getpeerinfo.
5356NET_UNROUTABLE = "not_publicly_routable"
6063# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
6164NETWORKS = frozenset ({NET_IPV4 , NET_IPV6 , NET_ONION , NET_I2P , NET_CJDNS })
6265
66+ # Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit
67+ socket_path = tempfile .NamedTemporaryFile ().name
6368
6469class ProxyTest (BitcoinTestFramework ):
6570 def set_test_params (self ):
66- self .num_nodes = 5
71+ self .num_nodes = 7
6772 self .setup_clean_chain = True
6873
6974 def setup_nodes (self ):
7075 self .have_ipv6 = test_ipv6_local ()
76+ self .have_unix_sockets = test_unix_socket ()
7177 # Create two proxies on different ports
7278 # ... one unauthenticated
7379 self .conf1 = Socks5Configuration ()
@@ -89,13 +95,25 @@ def setup_nodes(self):
8995 else :
9096 self .log .warning ("Testing without local IPv6 support" )
9197
98+ if self .have_unix_sockets :
99+ self .conf4 = Socks5Configuration ()
100+ self .conf4 .af = socket .AF_UNIX
101+ self .conf4 .addr = socket_path
102+ self .conf4 .unauth = True
103+ self .conf4 .auth = True
104+ else :
105+ self .log .warning ("Testing without local unix domain sockets support" )
106+
92107 self .serv1 = Socks5Server (self .conf1 )
93108 self .serv1 .start ()
94109 self .serv2 = Socks5Server (self .conf2 )
95110 self .serv2 .start ()
96111 if self .have_ipv6 :
97112 self .serv3 = Socks5Server (self .conf3 )
98113 self .serv3 .start ()
114+ if self .have_unix_sockets :
115+ self .serv4 = Socks5Server (self .conf4 )
116+ self .serv4 .start ()
99117
100118 # We will not try to connect to this.
101119 self .i2p_sam = ('127.0.0.1' , 7656 )
@@ -109,10 +127,15 @@ def setup_nodes(self):
109127 ['-listen' , f'-proxy={ self .conf2 .addr [0 ]} :{ self .conf2 .addr [1 ]} ' ,'-proxyrandomize=1' ],
110128 [],
111129 ['-listen' , f'-proxy={ self .conf1 .addr [0 ]} :{ self .conf1 .addr [1 ]} ' ,'-proxyrandomize=1' ,
112- '-cjdnsreachable' ]
130+ '-cjdnsreachable' ],
131+ [],
132+ []
113133 ]
114134 if self .have_ipv6 :
115135 args [3 ] = ['-listen' , f'-proxy=[{ self .conf3 .addr [0 ]} ]:{ self .conf3 .addr [1 ]} ' ,'-proxyrandomize=0' , '-noonion' ]
136+ if self .have_unix_sockets :
137+ args [5 ] = ['-listen' , f'-proxy=unix:{ socket_path } ' ]
138+ args [6 ] = ['-listen' , f'-onion=unix:{ socket_path } ' ]
116139 self .add_nodes (self .num_nodes , extra_args = args )
117140 self .start_nodes ()
118141
@@ -124,7 +147,7 @@ def network_test(self, node, addr, network):
124147 def node_test (self , node , * , proxies , auth , test_onion , test_cjdns ):
125148 rv = []
126149 addr = "15.61.23.23:1234"
127- self .log .debug (f"Test: outgoing IPv4 connection through node for address { addr } " )
150+ self .log .debug (f"Test: outgoing IPv4 connection through node { node . index } for address { addr } " )
128151 node .addnode (addr , "onetry" )
129152 cmd = proxies [0 ].queue .get ()
130153 assert isinstance (cmd , Socks5Command )
@@ -140,7 +163,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
140163
141164 if self .have_ipv6 :
142165 addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
143- self .log .debug (f"Test: outgoing IPv6 connection through node for address { addr } " )
166+ self .log .debug (f"Test: outgoing IPv6 connection through node { node . index } for address { addr } " )
144167 node .addnode (addr , "onetry" )
145168 cmd = proxies [1 ].queue .get ()
146169 assert isinstance (cmd , Socks5Command )
@@ -156,7 +179,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
156179
157180 if test_onion :
158181 addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
159- self .log .debug (f"Test: outgoing onion connection through node for address { addr } " )
182+ self .log .debug (f"Test: outgoing onion connection through node { node . index } for address { addr } " )
160183 node .addnode (addr , "onetry" )
161184 cmd = proxies [2 ].queue .get ()
162185 assert isinstance (cmd , Socks5Command )
@@ -171,7 +194,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
171194
172195 if test_cjdns :
173196 addr = "[fc00:1:2:3:4:5:6:7]:8888"
174- self .log .debug (f"Test: outgoing CJDNS connection through node for address { addr } " )
197+ self .log .debug (f"Test: outgoing CJDNS connection through node { node . index } for address { addr } " )
175198 node .addnode (addr , "onetry" )
176199 cmd = proxies [1 ].queue .get ()
177200 assert isinstance (cmd , Socks5Command )
@@ -185,7 +208,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
185208 self .network_test (node , addr , network = NET_CJDNS )
186209
187210 addr = "node.noumenon:8333"
188- self .log .debug (f"Test: outgoing DNS name connection through node for address { addr } " )
211+ self .log .debug (f"Test: outgoing DNS name connection through node { node . index } for address { addr } " )
189212 node .addnode (addr , "onetry" )
190213 cmd = proxies [3 ].queue .get ()
191214 assert isinstance (cmd , Socks5Command )
@@ -230,6 +253,12 @@ def run_test(self):
230253 proxies = [self .serv1 , self .serv1 , self .serv1 , self .serv1 ],
231254 auth = False , test_onion = True , test_cjdns = True )
232255
256+ if self .have_unix_sockets :
257+ self .node_test (self .nodes [5 ],
258+ proxies = [self .serv4 , self .serv4 , self .serv4 , self .serv4 ],
259+ auth = True , test_onion = True , test_cjdns = False )
260+
261+
233262 def networks_dict (d ):
234263 r = {}
235264 for x in d ['networks' ]:
@@ -315,6 +344,37 @@ def networks_dict(d):
315344 assert_equal (n4 ['i2p' ]['reachable' ], False )
316345 assert_equal (n4 ['cjdns' ]['reachable' ], True )
317346
347+ if self .have_unix_sockets :
348+ n5 = networks_dict (nodes_network_info [5 ])
349+ assert_equal (NETWORKS , n5 .keys ())
350+ for net in NETWORKS :
351+ if net == NET_I2P :
352+ expected_proxy = ''
353+ expected_randomize = False
354+ else :
355+ expected_proxy = 'unix:' + self .conf4 .addr # no port number
356+ expected_randomize = True
357+ assert_equal (n5 [net ]['proxy' ], expected_proxy )
358+ assert_equal (n5 [net ]['proxy_randomize_credentials' ], expected_randomize )
359+ assert_equal (n5 ['onion' ]['reachable' ], True )
360+ assert_equal (n5 ['i2p' ]['reachable' ], False )
361+ assert_equal (n5 ['cjdns' ]['reachable' ], False )
362+
363+ n6 = networks_dict (nodes_network_info [6 ])
364+ assert_equal (NETWORKS , n6 .keys ())
365+ for net in NETWORKS :
366+ if net != NET_ONION :
367+ expected_proxy = ''
368+ expected_randomize = False
369+ else :
370+ expected_proxy = 'unix:' + self .conf4 .addr # no port number
371+ expected_randomize = True
372+ assert_equal (n6 [net ]['proxy' ], expected_proxy )
373+ assert_equal (n6 [net ]['proxy_randomize_credentials' ], expected_randomize )
374+ assert_equal (n6 ['onion' ]['reachable' ], True )
375+ assert_equal (n6 ['i2p' ]['reachable' ], False )
376+ assert_equal (n6 ['cjdns' ]['reachable' ], False )
377+
318378 self .stop_node (1 )
319379
320380 self .log .info ("Test passing invalid -proxy hostname raises expected init error" )
@@ -383,6 +443,18 @@ def networks_dict(d):
383443 msg = "Error: Unknown network specified in -onlynet: 'abc'"
384444 self .nodes [1 ].assert_start_raises_init_error (expected_msg = msg )
385445
446+ self .log .info ("Test passing too-long unix path to -proxy raises init error" )
447+ self .nodes [1 ].extra_args = [f"-proxy=unix:{ 'x' * 1000 } " ]
448+ if self .have_unix_sockets :
449+ msg = f"Error: Invalid -proxy address or hostname: 'unix:{ 'x' * 1000 } '"
450+ else :
451+ # If unix sockets are not supported, the file path is incorrectly interpreted as host:port
452+ msg = f"Error: Invalid port specified in -proxy: 'unix:{ 'x' * 1000 } '"
453+ self .nodes [1 ].assert_start_raises_init_error (expected_msg = msg )
454+
455+ # Cleanup socket path we established outside the individual test directory.
456+ if self .have_unix_sockets :
457+ os .unlink (socket_path )
386458
387459if __name__ == '__main__' :
388460 ProxyTest ().main ()
0 commit comments