Skip to content

Commit bfe5192

Browse files
committed
test: cover UNIX sockets in feature_proxy.py
1 parent c65c0d0 commit bfe5192

File tree

2 files changed

+89
-8
lines changed

2 files changed

+89
-8
lines changed

test/functional/feature_proxy.py

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
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
@@ -39,15 +40,17 @@
3940
- Test passing unknown -onlynet
4041
"""
4142

43+
import os
4244
import socket
45+
import tempfile
4346

4447
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
4548
from test_framework.test_framework import BitcoinTestFramework
4649
from 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.
5356
NET_UNROUTABLE = "not_publicly_routable"
@@ -60,14 +63,17 @@
6063
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
6164
NETWORKS = 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

6469
class 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

387459
if __name__ == '__main__':
388460
ProxyTest().main()

test/functional/test_framework/netutil.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,12 @@ def test_ipv6_local():
158158
except socket.error:
159159
have_ipv6 = False
160160
return have_ipv6
161+
162+
def test_unix_socket():
163+
'''Return True if UNIX sockets are available on this platform.'''
164+
try:
165+
socket.AF_UNIX
166+
except AttributeError:
167+
return False
168+
else:
169+
return True

0 commit comments

Comments
 (0)