2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2019-02-21 02:03:13 +01:00
# Copyright (c) 2014-2019 The Bitcoin Core developers
2014-10-23 03:48:19 +02:00
# Distributed under the MIT software license, see the accompanying
2014-04-07 17:29:36 +02:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Test running bitcoind with the -rpcbind and -rpcallowip options. """
2014-04-07 17:29:36 +02:00
2017-03-22 15:29:39 +01:00
import sys
2018-07-07 00:10:35 +02:00
from test_framework . netutil import all_interfaces , addr_to_hex , get_bind_addrs , test_ipv6_local
2017-05-18 22:36:39 +02:00
from test_framework . test_framework import BitcoinTestFramework , SkipTest
2018-07-07 00:10:35 +02:00
from test_framework . util import assert_equal , assert_raises_rpc_error , get_rpc_proxy , rpc_port , rpc_url
2014-04-07 17:29:36 +02:00
2016-07-25 00:31:05 +02:00
class RPCBindTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2016-07-25 00:31:05 +02:00
self . setup_clean_chain = True
2018-03-06 22:48:15 +01:00
self . bind_to_localhost_only = False
2016-07-25 00:31:05 +02:00
self . num_nodes = 1
2019-12-06 15:37:49 +01:00
self . supports_cli = False
2014-06-27 10:05:46 +02:00
2016-07-25 00:31:05 +02:00
def setup_network ( self ) :
2017-08-24 17:37:25 +02:00
self . add_nodes ( self . num_nodes , None )
2014-04-07 17:29:36 +02:00
2018-02-22 15:43:26 +01:00
def add_options ( self , parser ) :
2018-08-11 22:24:14 +02:00
parser . add_argument ( " --ipv4 " , action = ' store_true ' , dest = " run_ipv4 " , help = " Run ipv4 tests only " , default = False )
parser . add_argument ( " --ipv6 " , action = ' store_true ' , dest = " run_ipv6 " , help = " Run ipv6 tests only " , default = False )
parser . add_argument ( " --nonloopback " , action = ' store_true ' , dest = " run_nonloopback " , help = " Run non-loopback tests only " , default = False )
2018-02-22 15:43:26 +01:00
2016-07-25 00:31:05 +02:00
def run_bind_test ( self , allow_ips , connect_to , addresses , expected ) :
'''
Start a node with requested rpcallowip and rpcbind parameters ,
then try to connect , and check if the set of bound addresses
matches the expected set .
'''
2017-06-09 22:35:17 +02:00
self . log . info ( " Bind test for %s " % str ( addresses ) )
2016-07-25 00:31:05 +02:00
expected = [ ( addr_to_hex ( addr ) , port ) for ( addr , port ) in expected ]
base_args = [ ' -disablewallet ' , ' -nolisten ' ]
if allow_ips :
base_args + = [ ' -rpcallowip= ' + x for x in allow_ips ]
binds = [ ' -rpcbind= ' + addr for addr in addresses ]
2017-06-09 22:35:17 +02:00
self . nodes [ 0 ] . rpchost = connect_to
self . start_node ( 0 , base_args + binds )
2017-06-02 20:30:36 +02:00
pid = self . nodes [ 0 ] . process . pid
2017-02-22 11:29:43 +01:00
assert_equal ( set ( get_bind_addrs ( pid ) ) , set ( expected ) )
2017-03-24 04:56:31 +01:00
self . stop_nodes ( )
2016-07-25 00:31:05 +02:00
def run_allowip_test ( self , allow_ips , rpchost , rpcport ) :
'''
2016-09-25 15:01:31 +02:00
Start a node with rpcallow IP , and request getnetworkinfo
2016-07-25 00:31:05 +02:00
at a non - localhost IP .
'''
2017-06-09 22:35:17 +02:00
self . log . info ( " Allow IP test for %s : %d " % ( rpchost , rpcport ) )
2018-12-03 18:15:07 +01:00
node_args = \
[ ' -disablewallet ' , ' -nolisten ' ] + \
[ ' -rpcallowip= ' + x for x in allow_ips ] + \
[ ' -rpcbind= ' + addr for addr in [ ' 127.0.0.1 ' , " %s : %d " % ( rpchost , rpcport ) ] ] # Bind to localhost as well so start_nodes doesn't hang
2017-06-09 22:35:17 +02:00
self . nodes [ 0 ] . rpchost = None
2018-12-03 18:15:07 +01:00
self . start_nodes ( [ node_args ] )
2017-02-22 11:29:43 +01:00
# connect to node through non-loopback interface
2019-07-31 20:11:32 +02:00
node = get_rpc_proxy ( rpc_url ( self . nodes [ 0 ] . datadir , 0 , self . chain , " %s : %d " % ( rpchost , rpcport ) ) , 0 , coveragedir = self . options . coveragedir )
2017-02-22 11:29:43 +01:00
node . getnetworkinfo ( )
2017-03-24 04:56:31 +01:00
self . stop_nodes ( )
2016-07-25 00:31:05 +02:00
def run_test ( self ) :
# due to OS-specific network stats queries, this test works only on Linux
2018-02-22 15:43:26 +01:00
if sum ( [ self . options . run_ipv4 , self . options . run_ipv6 , self . options . run_nonloopback ] ) > 1 :
raise AssertionError ( " Only one of --ipv4, --ipv6 and --nonloopback can be set " )
self . log . info ( " Check for linux " )
2017-03-22 15:29:39 +01:00
if not sys . platform . startswith ( ' linux ' ) :
2018-02-22 15:43:26 +01:00
raise SkipTest ( " This test can only be run on linux. " )
self . log . info ( " Check for ipv6 " )
have_ipv6 = test_ipv6_local ( )
2018-11-22 22:57:07 +01:00
if not have_ipv6 and not ( self . options . run_ipv4 or self . options . run_nonloopback ) :
2018-02-22 15:43:26 +01:00
raise SkipTest ( " This test requires ipv6 support. " )
self . log . info ( " Check for non-loopback interface " )
self . non_loopback_ip = None
2016-07-25 00:31:05 +02:00
for name , ip in all_interfaces ( ) :
if ip != ' 127.0.0.1 ' :
2018-02-22 15:43:26 +01:00
self . non_loopback_ip = ip
2016-07-25 00:31:05 +02:00
break
2018-02-22 15:43:26 +01:00
if self . non_loopback_ip is None and self . options . run_nonloopback :
raise SkipTest ( " This test requires a non-loopback ip address. " )
self . defaultport = rpc_port ( 0 )
if not self . options . run_nonloopback :
self . _run_loopback_tests ( )
if not self . options . run_ipv4 and not self . options . run_ipv6 :
self . _run_nonloopback_tests ( )
def _run_loopback_tests ( self ) :
if self . options . run_ipv4 :
# check only IPv4 localhost (explicit)
self . run_bind_test ( [ ' 127.0.0.1 ' ] , ' 127.0.0.1 ' , [ ' 127.0.0.1 ' ] ,
[ ( ' 127.0.0.1 ' , self . defaultport ) ] )
# check only IPv4 localhost (explicit) with alternative port
self . run_bind_test ( [ ' 127.0.0.1 ' ] , ' 127.0.0.1:32171 ' , [ ' 127.0.0.1:32171 ' ] ,
[ ( ' 127.0.0.1 ' , 32171 ) ] )
# check only IPv4 localhost (explicit) with multiple alternative ports on same host
self . run_bind_test ( [ ' 127.0.0.1 ' ] , ' 127.0.0.1:32171 ' , [ ' 127.0.0.1:32171 ' , ' 127.0.0.1:32172 ' ] ,
[ ( ' 127.0.0.1 ' , 32171 ) , ( ' 127.0.0.1 ' , 32172 ) ] )
else :
# check default without rpcallowip (IPv4 and IPv6 localhost)
self . run_bind_test ( None , ' 127.0.0.1 ' , [ ] ,
[ ( ' 127.0.0.1 ' , self . defaultport ) , ( ' ::1 ' , self . defaultport ) ] )
2018-12-03 18:15:07 +01:00
# check default with rpcallowip (IPv4 and IPv6 localhost)
2018-02-22 15:43:26 +01:00
self . run_bind_test ( [ ' 127.0.0.1 ' ] , ' 127.0.0.1 ' , [ ] ,
2018-12-03 18:15:07 +01:00
[ ( ' 127.0.0.1 ' , self . defaultport ) , ( ' ::1 ' , self . defaultport ) ] )
2018-02-22 15:43:26 +01:00
# check only IPv6 localhost (explicit)
self . run_bind_test ( [ ' [::1] ' ] , ' [::1] ' , [ ' [::1] ' ] ,
[ ( ' ::1 ' , self . defaultport ) ] )
# check both IPv4 and IPv6 localhost (explicit)
self . run_bind_test ( [ ' 127.0.0.1 ' ] , ' 127.0.0.1 ' , [ ' 127.0.0.1 ' , ' [::1] ' ] ,
[ ( ' 127.0.0.1 ' , self . defaultport ) , ( ' ::1 ' , self . defaultport ) ] )
def _run_nonloopback_tests ( self ) :
self . log . info ( " Using interface %s for testing " % self . non_loopback_ip )
2016-07-25 00:31:05 +02:00
# check only non-loopback interface
2018-02-22 15:43:26 +01:00
self . run_bind_test ( [ self . non_loopback_ip ] , self . non_loopback_ip , [ self . non_loopback_ip ] ,
[ ( self . non_loopback_ip , self . defaultport ) ] )
2016-07-25 00:31:05 +02:00
# Check that with invalid rpcallowip, we are denied
2018-02-22 15:43:26 +01:00
self . run_allowip_test ( [ self . non_loopback_ip ] , self . non_loopback_ip , self . defaultport )
assert_raises_rpc_error ( - 342 , " non-JSON HTTP response with ' 403 Forbidden ' from server " , self . run_allowip_test , [ ' 1.1.1.1 ' ] , self . non_loopback_ip , self . defaultport )
2014-04-07 17:29:36 +02:00
if __name__ == ' __main__ ' :
2016-08-19 22:41:09 +02:00
RPCBindTest ( ) . main ( )