17
17
- support no authentication (other proxy)
18
18
- support no authentication + user/pass authentication (Tor)
19
19
- proxy on IPv6
20
+ - proxy over unix domain sockets
20
21
21
22
- Create various proxies (as threads)
22
23
- Create nodes that connect to them
39
40
- Test passing unknown -onlynet
40
41
"""
41
42
43
+ import os
42
44
import socket
45
+ import tempfile
43
46
44
47
from test_framework .socks5 import Socks5Configuration , Socks5Command , Socks5Server , AddressType
45
48
from test_framework .test_framework import BitcoinTestFramework
46
49
from test_framework .util import (
47
50
assert_equal ,
48
51
p2p_port ,
49
52
)
50
- from test_framework .netutil import test_ipv6_local
53
+ from test_framework .netutil import test_ipv6_local , test_unix_socket
51
54
52
55
# Networks returned by RPC getpeerinfo.
53
56
NET_UNROUTABLE = "not_publicly_routable"
60
63
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
61
64
NETWORKS = frozenset ({NET_IPV4 , NET_IPV6 , NET_ONION , NET_I2P , NET_CJDNS })
62
65
66
+ # Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit
67
+ socket_path = tempfile .NamedTemporaryFile ().name
63
68
64
69
class ProxyTest (BitcoinTestFramework ):
65
70
def set_test_params (self ):
66
- self .num_nodes = 5
71
+ self .num_nodes = 7
67
72
self .setup_clean_chain = True
68
73
69
74
def setup_nodes (self ):
70
75
self .have_ipv6 = test_ipv6_local ()
76
+ self .have_unix_sockets = test_unix_socket ()
71
77
# Create two proxies on different ports
72
78
# ... one unauthenticated
73
79
self .conf1 = Socks5Configuration ()
@@ -89,13 +95,25 @@ def setup_nodes(self):
89
95
else :
90
96
self .log .warning ("Testing without local IPv6 support" )
91
97
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
+
92
107
self .serv1 = Socks5Server (self .conf1 )
93
108
self .serv1 .start ()
94
109
self .serv2 = Socks5Server (self .conf2 )
95
110
self .serv2 .start ()
96
111
if self .have_ipv6 :
97
112
self .serv3 = Socks5Server (self .conf3 )
98
113
self .serv3 .start ()
114
+ if self .have_unix_sockets :
115
+ self .serv4 = Socks5Server (self .conf4 )
116
+ self .serv4 .start ()
99
117
100
118
# We will not try to connect to this.
101
119
self .i2p_sam = ('127.0.0.1' , 7656 )
@@ -109,10 +127,15 @@ def setup_nodes(self):
109
127
['-listen' , f'-proxy={ self .conf2 .addr [0 ]} :{ self .conf2 .addr [1 ]} ' ,'-proxyrandomize=1' ],
110
128
[],
111
129
['-listen' , f'-proxy={ self .conf1 .addr [0 ]} :{ self .conf1 .addr [1 ]} ' ,'-proxyrandomize=1' ,
112
- '-cjdnsreachable' ]
130
+ '-cjdnsreachable' ],
131
+ [],
132
+ []
113
133
]
114
134
if self .have_ipv6 :
115
135
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 } ' ]
116
139
self .add_nodes (self .num_nodes , extra_args = args )
117
140
self .start_nodes ()
118
141
@@ -124,7 +147,7 @@ def network_test(self, node, addr, network):
124
147
def node_test (self , node , * , proxies , auth , test_onion , test_cjdns ):
125
148
rv = []
126
149
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 } " )
128
151
node .addnode (addr , "onetry" )
129
152
cmd = proxies [0 ].queue .get ()
130
153
assert isinstance (cmd , Socks5Command )
@@ -140,7 +163,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
140
163
141
164
if self .have_ipv6 :
142
165
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 } " )
144
167
node .addnode (addr , "onetry" )
145
168
cmd = proxies [1 ].queue .get ()
146
169
assert isinstance (cmd , Socks5Command )
@@ -156,7 +179,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
156
179
157
180
if test_onion :
158
181
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 } " )
160
183
node .addnode (addr , "onetry" )
161
184
cmd = proxies [2 ].queue .get ()
162
185
assert isinstance (cmd , Socks5Command )
@@ -171,7 +194,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
171
194
172
195
if test_cjdns :
173
196
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 } " )
175
198
node .addnode (addr , "onetry" )
176
199
cmd = proxies [1 ].queue .get ()
177
200
assert isinstance (cmd , Socks5Command )
@@ -185,7 +208,7 @@ def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
185
208
self .network_test (node , addr , network = NET_CJDNS )
186
209
187
210
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 } " )
189
212
node .addnode (addr , "onetry" )
190
213
cmd = proxies [3 ].queue .get ()
191
214
assert isinstance (cmd , Socks5Command )
@@ -230,6 +253,12 @@ def run_test(self):
230
253
proxies = [self .serv1 , self .serv1 , self .serv1 , self .serv1 ],
231
254
auth = False , test_onion = True , test_cjdns = True )
232
255
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
+
233
262
def networks_dict (d ):
234
263
r = {}
235
264
for x in d ['networks' ]:
@@ -315,6 +344,37 @@ def networks_dict(d):
315
344
assert_equal (n4 ['i2p' ]['reachable' ], False )
316
345
assert_equal (n4 ['cjdns' ]['reachable' ], True )
317
346
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
+
318
378
self .stop_node (1 )
319
379
320
380
self .log .info ("Test passing invalid -proxy hostname raises expected init error" )
@@ -383,6 +443,18 @@ def networks_dict(d):
383
443
msg = "Error: Unknown network specified in -onlynet: 'abc'"
384
444
self .nodes [1 ].assert_start_raises_init_error (expected_msg = msg )
385
445
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 )
386
458
387
459
if __name__ == '__main__' :
388
460
ProxyTest ().main ()
0 commit comments