From 2860ad55df90901e2da3eda95a018fd6a7e285d6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 12:11:50 +0930 Subject: [PATCH 01/14] tests/utils.py: remove unused connect routine. It's broken anyway. Signed-off-by: Rusty Russell --- tests/utils.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 25e5c88c9349..4cd7665ff8cb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -319,44 +319,6 @@ def __init__(self, daemon, rpc, btc, executor, may_fail=False, may_reconnect=Fal self.may_fail = may_fail self.may_reconnect = may_reconnect - # Use batch if you're doing more than one async. - def connect(self, remote_node, capacity, async=False): - # Collect necessary information - addr = self.rpc.newaddr()['address'] - txid = self.bitcoin.rpc.sendtoaddress(addr, capacity) - tx = self.bitcoin.rpc.gettransaction(txid) - start_size = self.bitcoin.rpc.getmempoolinfo()['size'] - - def call_connect(): - try: - self.rpc.connect('127.0.0.1', remote_node.daemon.port, tx['hex'], async=False) - except Exception: - pass - t = threading.Thread(target=call_connect) - t.daemon = True - t.start() - - def wait_connected(): - # Up to 10 seconds to get tx into mempool. - start_time = time.time() - while self.bitcoin.rpc.getmempoolinfo()['size'] == start_size: - if time.time() > start_time + 10: - raise TimeoutError('No new transactions in mempool') - time.sleep(0.1) - - self.bitcoin.generate_block(1) - - # fut.result(timeout=5) - - # Now wait for confirmation - self.daemon.wait_for_log(" to CHANNELD_NORMAL|STATE_NORMAL") - remote_node.daemon.wait_for_log(" to CHANNELD_NORMAL|STATE_NORMAL") - - if async: - return self.executor.submit(wait_connected) - else: - return wait_connected() - def openchannel(self, remote_node, capacity, addrtype="p2sh-segwit"): addr, wallettxid = self.fundwallet(capacity, addrtype) fundingtx = self.rpc.fundchannel(remote_node.info['id'], capacity) From d431e917eaa4dbfab0d68febe24fcb0337fdd5d8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:14:39 +0930 Subject: [PATCH 02/14] tests/test_lightningd.py: move port into node. We're about to change the JSONRPC, so let's put an explicit 'port' into our node class. We initialize it at startup time: in future I hope to use ephemeral ports to make our tests more easily parallelizable. Suggested-by: @cdecker Signed-off-by: Rusty Russell --- tests/test_lightningd.py | 212 +++++++++++++++++++-------------------- tests/utils.py | 6 +- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 0444fb894c10..189673f37ef4 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -208,7 +208,7 @@ def get_node(self, disconnect=None, options=None, may_fail=False, may_reconnect= ] try: - node.daemon.start() + node.start() except Exception: node.daemon.stop() raise @@ -329,7 +329,7 @@ def tearDown(self): class LightningDTests(BaseLightningDTests): def connect(self, may_reconnect=False): l1, l2 = self.node_factory.get_nodes(2, opts={'may_reconnect': may_reconnect}) - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert ret['id'] == l2.info['id'] @@ -639,10 +639,10 @@ def test_connect(self): l2.daemon.wait_for_log('hand_back_peer {}: now local again'.format(l1.info['id'])) # Reconnect should be a noop - ret = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) assert ret['id'] == l2.info['id'] - ret = l2.rpc.connect(l1.info['id'], host='localhost', port=l1.info['port']) + ret = l2.rpc.connect(l1.info['id'], host='localhost', port=l1.port) assert ret['id'] == l1.info['id'] # Should still only have one peer! @@ -662,7 +662,7 @@ def test_connect(self): # Should get reasonable error if wrong key for peer. self.assertRaisesRegex(ValueError, "Cryptographic handshake: ", - l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.info['port']) + l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port) def test_connect_by_gossip(self): """Test connecting to an unknown peer using node gossip @@ -671,7 +671,7 @@ def test_connect_by_gossip(self): l2 = self.node_factory.get_node() l3 = self.node_factory.get_node(options={"ipaddr": "127.0.0.1"}) - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) # Nodes are gossiped only if they have channels chanid = self.fund_channel(l2, l3, 10**6) @@ -681,7 +681,7 @@ def test_connect_by_gossip(self): l2.daemon.wait_for_logs(['Received node_announcement for node {}'.format(l3.info['id'])]) # Let l1 learn of l3 by node gossip - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_logs(['Received node_announcement for node {}'.format(l3.info['id'])]) # Have l1 connect to l3 without explicit host and port. @@ -695,31 +695,30 @@ def test_connect_standard_addr(self): l3 = self.node_factory.get_node() # node@host - ret = l1.rpc.connect("{}@{}".format(l2.info['id'], 'localhost'), port=l2.info['port']) + ret = l1.rpc.connect("{}@{}".format(l2.info['id'], 'localhost'), port=l2.port) assert ret['id'] == l2.info['id'] # node@host:port - ret = l1.rpc.connect("{}@localhost:{}".format(l3.info['id'], l3.info['port'])) + ret = l1.rpc.connect("{}@localhost:{}".format(l3.info['id'], l3.port)) assert ret['id'] == l3.info['id'] # node@[ipv6]:port --- not supported by our CI - # ret = l1.rpc.connect("{}@[::1]:{}".format(l3.info['id'], l3.info['port'])) + # ret = l1.rpc.connect("{}@[::1]:{}".format(l3.info['id'], l3.port)) # assert ret['id'] == l3.info['id'] def test_reconnect_channel_peers(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) - l2.stop() - l2.daemon.start() + l2.restart() # Should reconnect. wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'][0]['connected']) wait_for(lambda: l2.rpc.listpeers(l1.info['id'])['peers'][0]['connected']) # Connect command should succeed. - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Stop l2 and wait for l1 to notice. l2.stop() @@ -728,18 +727,18 @@ def test_reconnect_channel_peers(self): # Now should fail. self.assertRaisesRegex(ValueError, "Connection refused", - l1.rpc.connect, l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect, l2.info['id'], 'localhost', l2.port) # Wait for exponential backoff to give us a 2 second window. l1.daemon.wait_for_log('...will try again in 2 seconds') # It should now succeed when it restarts. - l2.daemon.start() + l2.start() # Multiples should be fine! - fut1 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.info['port']) - fut2 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.info['port']) - fut3 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.info['port']) + fut1 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.port) + fut2 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.port) + fut3 = self.executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.port) fut1.result(10) fut2.result(10) fut3.result(10) @@ -1246,7 +1245,7 @@ def test_report_routing_failure(self): # l1-l2-l3-l4. def fund_from_to_payer(lsrc, ldst, lpayer): - lsrc.rpc.connect(ldst.info['id'], 'localhost', ldst.info['port']) + lsrc.rpc.connect(ldst.info['id'], 'localhost', ldst.port) c = self.fund_channel(lsrc, ldst, 10000000) self.wait_for_routes(lpayer, [c]) @@ -1276,7 +1275,7 @@ def test_bad_opening(self): # l1 asks for a too-long locktime l1 = self.node_factory.get_node(options={'locktime-blocks': 100}) l2 = self.node_factory.get_node(options={'max-locktime-blocks': 99}) - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert ret['id'] == l2.info['id'] @@ -1387,7 +1386,7 @@ def test_closing_while_disconnected(self): l1.rpc.close, chan, False, 0) l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN') - l2.daemon.start() + l2.start() l1.daemon.wait_for_log(' to CLOSINGD_SIGEXCHANGE') l2.daemon.wait_for_log(' to CLOSINGD_SIGEXCHANGE') @@ -1416,7 +1415,7 @@ def test_db_upgrade(self): l1.db_manip("CREATE TABLE version (version INTEGER);") l1.db_manip("INSERT INTO version VALUES (1);") - l1.daemon.start() + l1.start() upgrades = l1.db_query("SELECT * from db_upgrades;") assert len(upgrades) == 1 assert(upgrades[0]['upgrade_from'] == 1) @@ -1465,7 +1464,7 @@ def test_closing_different_fees(self): }) p.feerate = feerate p.amount = amount - l1.rpc.connect(p.info['id'], 'localhost', p.info['port']) + l1.rpc.connect(p.info['id'], 'localhost', p.port) peers.append(p) for p in peers: @@ -1584,7 +1583,7 @@ def test_onchain_first_commit(self): # Make locktime different, as we once had them reversed! l2 = self.node_factory.get_node(options={'locktime-blocks': 10}) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.give_funds(l1, 10**6 + 1000000) @@ -1671,7 +1670,7 @@ def test_onchaind_replay(self): l2 = self.node_factory.get_node(options=options) btc = l1.bitcoin - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchaind_replay', 'desc')['payment_hash'] @@ -1722,7 +1721,7 @@ def test_onchain_dust_out(self): l1 = self.node_factory.get_node(disconnect=disconnects) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) # Must be dust! @@ -1767,8 +1766,7 @@ def test_onchain_dust_out(self): l2.daemon.wait_for_log('onchaind complete, forgetting peer') # Restart l1, it should not crash! - l1.stop() - l1.daemon.start() + l1.restart() # Now, 100 blocks and l1 should be done. bitcoind.generate_block(6) @@ -1785,10 +1783,10 @@ def test_onchain_timeout(self): l1 = self.node_factory.get_node(disconnect=disconnects) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] @@ -1856,8 +1854,8 @@ def test_onchain_middleman(self): l3 = self.node_factory.get_node() # l2 connects to both, so l1 can't reconnect and thus l2 drops to chain - l2.rpc.connect(l1.info['id'], 'localhost', l1.info['port']) - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) self.fund_channel(l2, l1, 10**6) c23 = self.fund_channel(l2, l3, 10**6) @@ -1929,7 +1927,7 @@ def test_penalty_inhtlc(self): l1 = self.node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], may_fail=True) l2 = self.node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit']) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) # Now, this will get stuck due to l1 commit being disabled.. @@ -1992,7 +1990,7 @@ def test_penalty_outhtlc(self): l1 = self.node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED*3-nocommit'], may_fail=True) l2 = self.node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED*3-nocommit']) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) # Move some across to l2. @@ -2059,7 +2057,7 @@ def test_onchain_feechange(self): l2 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] @@ -2093,7 +2091,7 @@ def test_onchain_feechange(self): l1.stop() l1.daemon.cmd_line.append('--override-fee-rates=20000/9000/2000') - l1.daemon.start() + l1.start() # We recognize different proposal as ours. l1.daemon.wait_for_log('Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US') @@ -2133,7 +2131,7 @@ def test_onchain_all_dust(self): l1 = self.node_factory.get_node(options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node(disconnect=disconnects) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] @@ -2239,7 +2237,7 @@ def test_permfail_new_commit(self): l1 = self.node_factory.get_node(options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node(disconnect=disconnects) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) # This will fail at l2's end. @@ -2275,7 +2273,7 @@ def test_permfail_htlc_in(self): l1 = self.node_factory.get_node(options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node(disconnect=disconnects) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) # This will fail at l2's end. @@ -2317,7 +2315,7 @@ def test_permfail_htlc_out(self): l1 = self.node_factory.get_node(options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node(disconnect=disconnects) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l2, l1, 10**6) # This will fail at l2's end. @@ -2440,7 +2438,7 @@ def test_gossip_weirdalias(self): assert l2.daemon.is_in_log('Server started with public key .* alias {}' .format(normal_name)) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l2, l1, 10**6) bitcoind.rpc.generate(6) @@ -2468,9 +2466,9 @@ def test_gossip_persistence(self): l3 = self.node_factory.get_node(options=opts) l4 = self.node_factory.get_node(options=opts) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) - l3.rpc.connect(l4.info['id'], 'localhost', l4.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l3.rpc.connect(l4.info['id'], 'localhost', l4.port) self.fund_channel(l1, l2, 10**6) self.fund_channel(l2, l3, 10**6) @@ -2581,11 +2579,11 @@ def test_routing_gossip_reconnect(self): may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) l3 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.openchannel(l2, 20000) # Now open new channels and everybody should sync - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.openchannel(l3, 20000) # Settle the gossip @@ -2597,8 +2595,8 @@ def test_second_channel(self): l2 = self.node_factory.get_node() l3 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) - l1.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.connect(l3.info['id'], 'localhost', l3.port) self.fund_channel(l1, l2, 10**6) self.fund_channel(l1, l3, 10**6) @@ -2609,7 +2607,7 @@ def test_routing_gossip(self): for i in range(len(nodes) - 1): src, dst = nodes[i], nodes[i + 1] - src.rpc.connect(dst.info['id'], 'localhost', dst.info['port']) + src.rpc.connect(dst.info['id'], 'localhost', dst.port) src.openchannel(dst, 20000) # Allow announce messages. @@ -2648,7 +2646,7 @@ def test_forward(self): # Connect 1 -> 2 -> 3. l1, l2 = self.connect() l3 = self.node_factory.get_node() - ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.port) assert ret['id'] == l3.info['id'] @@ -2744,13 +2742,13 @@ def test_forward_different_fees_and_cltv(self): l2 = self.node_factory.get_node(options={'cltv-delta': 20, 'fee-base': 200, 'fee-per-satoshi': 2000}) l3 = self.node_factory.get_node(options={'cltv-delta': 30, 'cltv-final': 9, 'fee-base': 300, 'fee-per-satoshi': 3000}) - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert ret['id'] == l2.info['id'] l1.daemon.wait_for_log('Handing back peer .* to master') l2.daemon.wait_for_log('Handing back peer .* to master') - ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.port) assert ret['id'] == l3.info['id'] l2.daemon.wait_for_log('Handing back peer .* to master') @@ -2848,13 +2846,13 @@ def test_forward_pad_fees_and_cltv(self): l2 = self.node_factory.get_node(options={'cltv-delta': 20, 'fee-base': 200, 'fee-per-satoshi': 2000}) l3 = self.node_factory.get_node(options={'cltv-delta': 30, 'cltv-final': 9, 'fee-base': 300, 'fee-per-satoshi': 3000}) - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert ret['id'] == l2.info['id'] l1.daemon.wait_for_log('Handing back peer .* to master') l2.daemon.wait_for_log('Handing back peer .* to master') - ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.port) assert ret['id'] == l3.info['id'] l2.daemon.wait_for_log('Handing back peer .* to master') @@ -2893,7 +2891,7 @@ def test_htlc_sig_persistence(self): l1 = self.node_factory.get_node(options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node(disconnect=['+WIRE_COMMITMENT_SIGNED']) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) f = self.executor.submit(self.pay, l1, l2, 31337000) l1.daemon.wait_for_log(r'HTLC out 0 RCVD_ADD_ACK_COMMIT->SENT_ADD_ACK_REVOCATION') @@ -2909,7 +2907,7 @@ def test_htlc_sig_persistence(self): l2.rpc.dev_fail(l1.info['id']) l2.stop() l1.bitcoin.rpc.generate(1) - l1.daemon.start() + l1.start() assert l1.daemon.is_in_log(r'Loaded 1 HTLC signatures from DB') l1.daemon.wait_for_logs([ @@ -2938,7 +2936,7 @@ def test_htlc_out_timeout(self): options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chanid = self.fund_channel(l1, l2, 10**6) # Wait for route propagation. @@ -2994,7 +2992,7 @@ def test_htlc_in_timeout(self): options={'dev-no-reconnect': None}) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chanid = self.fund_channel(l1, l2, 10**6) self.wait_for_routes(l1, [chanid]) @@ -3044,12 +3042,12 @@ def test_disconnect(self): l2 = self.node_factory.get_node() self.assertRaises(ValueError, l1.rpc.connect, - l2.info['id'], 'localhost', l2.info['port']) + l2.info['id'], 'localhost', l2.port) self.assertRaises(ValueError, l1.rpc.connect, - l2.info['id'], 'localhost', l2.info['port']) + l2.info['id'], 'localhost', l2.port) self.assertRaises(ValueError, l1.rpc.connect, - l2.info['id'], 'localhost', l2.info['port']) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l2.info['id'], 'localhost', l2.port) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Should have 3 connect fails. for d in disconnects: @@ -3074,12 +3072,12 @@ def test_disconnect_funder(self): self.give_funds(l1, 2000000) for d in disconnects: - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) assert l1.rpc.getpeer(l2.info['id']) is None # This one will succeed. - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 20000) # Should still only have one peer! @@ -3098,12 +3096,12 @@ def test_disconnect_fundee(self): self.give_funds(l1, 2000000) for d in disconnects: - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) assert l1.rpc.getpeer(l2.info['id']) is None # This one will succeed. - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 20000) # Should still only have one peer! @@ -3120,7 +3118,7 @@ def test_disconnect_half_signed(self): self.give_funds(l1, 2000000) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) # Fundee remembers, funder doesn't. @@ -3137,7 +3135,7 @@ def test_reconnect_signed(self): self.give_funds(l1, 2000000) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 20000) # They haven't forgotten each other. @@ -3160,7 +3158,7 @@ def test_reconnect_openingd(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.give_funds(l1, 2000000) @@ -3169,7 +3167,7 @@ def test_reconnect_openingd(self): assert l1.rpc.getpeer(l2.info['id']) is None # Reconnect. - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # We should get a message about reconnecting, but order unsynced. l2.daemon.wait_for_logs(['gossipd.*reconnect for active peer', @@ -3191,7 +3189,7 @@ def test_reconnect_normal(self): l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) @@ -3205,7 +3203,7 @@ def test_reconnect_sender_add1(self): l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) @@ -3235,7 +3233,7 @@ def test_reconnect_sender_add(self): l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) @@ -3262,7 +3260,7 @@ def test_reconnect_receiver_add(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) @@ -3293,7 +3291,7 @@ def test_reconnect_receiver_fulfill(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 10**6) @@ -3315,7 +3313,7 @@ def test_shutdown_reconnect(self): l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chan = self.fund_channel(l1, l2, 10**6) self.pay(l1, l2, 200000000) @@ -3342,7 +3340,7 @@ def test_shutdown_awaiting_lockin(self): l1 = self.node_factory.get_node() l2 = self.node_factory.get_node(options={'anchor-confirms': 3}) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.give_funds(l1, 10**6 + 1000000) chanid = l1.rpc.fundchannel(l2.info['id'], 10**6)['channel_id'] @@ -3381,7 +3379,7 @@ def test_closing_negotiation_reconnect(self): '+WIRE_CLOSING_SIGNED'] l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chan = self.fund_channel(l1, l2, 10**6) self.pay(l1, l2, 200000000) @@ -3411,7 +3409,7 @@ def test_bech32_funding(self): l2 = self.node_factory.get_node(random_hsm=True) # connect - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # fund a bech32 address and then open a channel with it res = l1.openchannel(l2, 20000, 'bech32') @@ -3592,7 +3590,7 @@ def test_funding_fail(self): l1 = self.node_factory.get_node(random_hsm=True) max_locktime = 3 * 6 * 24 l2 = self.node_factory.get_node(options={'locktime-blocks': max_locktime + 1}) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) funds = 1000000 @@ -3612,7 +3610,7 @@ def test_funding_fail(self): # Restart l2 without ridiculous locktime. del l2.daemon.opts['locktime-blocks'] l2.restart() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # We don't have enough left to cover fees if we try to spend it all. self.assertRaisesRegex(ValueError, r'Cannot afford funding transaction', @@ -3629,7 +3627,7 @@ def test_funding_toolarge(self): """Try to create a giant channel""" l1 = self.node_factory.get_node() l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Send funds. amount = 2**24 @@ -3654,7 +3652,7 @@ def test_lockin_between_restart(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(options={'anchor-confirms': 3}, may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.give_funds(l1, 10**6 + 1000000) l1.rpc.fundchannel(l2.info['id'], 10**6)['tx'] @@ -3666,7 +3664,7 @@ def test_lockin_between_restart(self): bitcoind.generate_block(120) # Restart - l1.daemon.start() + l1.start() # All should be good. l1.daemon.wait_for_log(' to CHANNELD_NORMAL') @@ -3687,7 +3685,7 @@ def test_funding_while_offline(self): bitcoind.generate_block(120) # Restart - l1.daemon.start() + l1.start() sync_blockheight([l1]) assert len(l1.rpc.listfunds()['outputs']) == 1 @@ -3729,7 +3727,7 @@ def test_channel_persistence(self): l1 = self.node_factory.get_node(may_reconnect=True) l2 = self.node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Neither node should have a channel open, they are just connected for n in (l1, l2): @@ -3760,7 +3758,7 @@ def test_channel_persistence(self): wait_for(lambda: 'connected' not in l1.rpc.listpeers()['peers'][0]['channels'][0]) # Now restart l2 and it should reload peers/channels from the DB - l2.daemon.start() + l2.start() wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 1) # Wait for the restored HTLC to finish @@ -3776,8 +3774,7 @@ def test_channel_persistence(self): assert l2.rpc.listpeers()['peers'][0]['channels'][0]['msatoshi_to_us'] == 20000 # Finally restart l1, and make sure it remembers - l1.stop() - l1.daemon.start() + l1.restart() assert l1.rpc.listpeers()['peers'][0]['channels'][0]['msatoshi_to_us'] == 99980000 # Now make sure l1 is watching for unilateral closes @@ -3796,7 +3793,7 @@ def test_payment_success_persistence(self): options={'dev-no-reconnect': None}, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chanid = self.fund_channel(l1, l2, 100000) @@ -3815,7 +3812,7 @@ def test_payment_success_persistence(self): del l1.daemon.opts['dev-disconnect'] # Should reconnect, and sort the payment out. - l1.daemon.start() + l1.start() wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending') @@ -3836,7 +3833,7 @@ def test_payment_failed_persistence(self): options={'dev-no-reconnect': None}, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 100000) @@ -3859,7 +3856,7 @@ def test_payment_failed_persistence(self): time.sleep(3) # Should reconnect, and fail the payment - l1.daemon.start() + l1.start() wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending') @@ -3875,7 +3872,7 @@ def test_payment_duplicate_uncommitted(self): l1 = self.node_factory.get_node(disconnect=['=WIRE_UPDATE_ADD_HTLC-nocommit']) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) self.fund_channel(l1, l2, 100000) @@ -3908,8 +3905,8 @@ def test_gossip_badsig(self): l3 = self.node_factory.get_node() # l2 connects to both, so l1 can't reconnect and thus l2 drops to chain - l2.rpc.connect(l1.info['id'], 'localhost', l1.info['port']) - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) self.fund_channel(l2, l1, 10**6) self.fund_channel(l2, l3, 10**6) @@ -4041,8 +4038,7 @@ def test_channel_reenable(self): wait_for(lambda: [c['active'] for c in l1.rpc.listchannels()['channels']] == [True, True]) # Restart l2, will cause l1 to reconnect - l2.stop() - l2.daemon.start() + l2.restart() # Now they should sync and re-establish again l1.daemon.wait_for_logs(['Received channel_update for channel \\d+:1:1.1.', @@ -4139,7 +4135,7 @@ def test_fee_limits(self): # Try with node which sets --ignore-fee-limits l3 = self.node_factory.get_node(options={'ignore-fee-limits': 'true'}) - l1.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l1.rpc.connect(l3.info['id'], 'localhost', l3.port) chan = self.fund_channel(l1, l3, 10**6) @@ -4163,7 +4159,7 @@ def test_update_fee_reconnect(self): disconnects = ['+WIRE_COMMITMENT_SIGNED'] l1 = self.node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = self.node_factory.get_node(may_reconnect=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chan = self.fund_channel(l1, l2, 10**6) @@ -4202,7 +4198,7 @@ def test_io_logging(self): l1 = self.node_factory.get_node(options={'debug-subdaemon-io': 'channeld', 'log-level': 'io'}) l2 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Fundchannel manually so we get channeld pid. self.give_funds(l1, 10**6 + 1000000) @@ -4292,7 +4288,7 @@ def test_listconfigs(self): # See utils.py assert configs['bitcoin-datadir'] == bitcoind.bitcoin_dir assert configs['lightning-dir'] == l1.daemon.lightning_dir - assert configs['port'] == l1.info['port'] + assert configs['port'] == l1.port assert configs['allow-deprecated-apis'] is False assert configs['override-fee-rates'] == '15000/7500/1000' assert configs['network'] == 'regtest' @@ -4311,7 +4307,7 @@ def test_multiple_channels(self): for i in range(3): # FIXME: we shouldn't disconnect on close? - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert ret['id'] == l2.info['id'] l1.daemon.wait_for_log('Handing back peer .* to master') @@ -4424,7 +4420,7 @@ def test_forget_channel(self): l1 = self.node_factory.get_node() l2 = self.node_factory.get_node() self.give_funds(l1, 10**6) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 10**5) assert len(l1.rpc.listpeers()['peers']) == 1 @@ -4517,8 +4513,8 @@ def test_disconnectpeer(self): l1 = self.node_factory.get_node() l2 = self.node_factory.get_node() l3 = self.node_factory.get_node() - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) - l1.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.connect(l3.info['id'], 'localhost', l3.port) # Gossiping assert l1.rpc.getpeer(l2.info['id'])['state'] == "GOSSIPING" @@ -4575,7 +4571,7 @@ def test_rescan(self): l1.daemon.opts['rescan'] = -500000 l1.stop() btc.rpc.generate(4) - l1.daemon.start() + l1.start() l1.daemon.wait_for_log(r'Adding block 105') assert not l1.daemon.is_in_log(r'Adding block 102') diff --git a/tests/utils.py b/tests/utils.py index 4cd7665ff8cb..ce508a53a544 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -367,6 +367,10 @@ def db_manip(self, query): c.close() db.close() + def start(self): + self.daemon.start() + self.port = self.rpc.getinfo()['port'] + def stop(self, timeout=10): """ Attempt to do a clean shutdown, but kill if it hangs """ @@ -403,7 +407,7 @@ def restart(self, timeout=10, clean=True): else: self.daemon.stop() - self.daemon.start() + self.start() def fund_channel(self, l2, amount): From cb26692a1406ef5c8d6f82b5e084166282fae801 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:14:40 +0930 Subject: [PATCH 03/14] lightningd: make explicit listen and reconnect flags. We set no_reconnect with --offline, but that doesn't work if !DEVELOPER. Make the flag positive, and non-DEVELOPER mode for gossipd. We also don't override portnum with --offline, but have an explicit 'listen' flag. Signed-off-by: Rusty Russell --- gossipd/gossip.c | 13 ++++++------- gossipd/gossip_wire.csv | 3 +-- lightningd/gossip_control.c | 10 +++------- lightningd/jsonrpc.c | 2 +- lightningd/lightningd.c | 3 ++- lightningd/lightningd.h | 9 ++++++--- lightningd/opening_control.c | 7 ------- lightningd/options.c | 21 ++++++++++++++------- 8 files changed, 33 insertions(+), 35 deletions(-) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index d5e9e41b1740..977a74e17bab 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -132,8 +132,8 @@ struct daemon { /* To make sure our node_announcement timestamps increase */ u32 last_announce_timestamp; - /* Only matters if DEVELOPER defined */ - bool no_reconnect; + /* Automatically reconnect. */ + bool reconnect; }; /* Peers we're trying to reach. */ @@ -1631,7 +1631,7 @@ static struct io_plan *gossip_init(struct daemon_conn *master, daemon, msg, &daemon->broadcast_interval, &chain_hash, &daemon->id, &daemon->globalfeatures, &daemon->localfeatures, &daemon->wireaddrs, daemon->rgb, - daemon->alias, &update_channel_interval, &daemon->no_reconnect)) { + daemon->alias, &update_channel_interval, &daemon->reconnect)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } /* Prune time is twice update time */ @@ -1955,12 +1955,11 @@ static void retry_important(struct important_peerid *imp) /* In case we've come off a timer, don't leave dangling pointer */ imp->reconnect_timer = NULL; -#if DEVELOPER - /* With --dev-no-reconnect, we only want explicit + /* With --dev-no-reconnect or --offline, we only want explicit * connects */ - if (imp->daemon->no_reconnect) + if (!imp->daemon->reconnect) return; -#endif + try_reach_peer(imp->daemon, &imp->id, false); } diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 60e879f14f3a..e530c341d40b 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -16,8 +16,7 @@ gossipctl_init,,wireaddrs,num_wireaddrs*struct wireaddr gossipctl_init,,rgb,3*u8 gossipctl_init,,alias,32*u8 gossipctl_init,,update_channel_interval,u32 -# DEVELOPER only -gossipctl_init,,no_reconnect,bool +gossipctl_init,,reconnect,bool # Activate the gossip daemon, so others can connect. gossipctl_activate,3025 diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 09a3227686cd..1a19bc3691d2 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -183,11 +183,6 @@ void gossip_init(struct lightningd *ld) u8 *msg; int hsmfd; u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; -#if DEVELOPER - bool no_reconnect = ld->no_reconnect; -#else - bool no_reconnect = false; -#endif msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); if (!wire_sync_write(ld->hsm_fd, msg)) @@ -212,7 +207,7 @@ void gossip_init(struct lightningd *ld) &get_chainparams(ld)->genesis_blockhash, &ld->id, get_offered_global_features(tmpctx), get_offered_local_features(tmpctx), ld->wireaddrs, ld->rgb, - ld->alias, ld->config.channel_update_interval, no_reconnect); + ld->alias, ld->config.channel_update_interval, ld->reconnect); subd_send_msg(ld->gossip, msg); } @@ -227,7 +222,8 @@ static void gossip_activate_done(struct subd *gossip UNUSED, void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, ld->portnum); + const u8 *msg = towire_gossipctl_activate(NULL, + ld->listen ? ld->portnum : 0); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 02a109377468..d3f050aeb759 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -149,7 +149,7 @@ static void json_getinfo(struct command *cmd, json_object_start(response, NULL); json_add_pubkey(response, "id", &cmd->ld->id); - if (cmd->ld->portnum) { + if (cmd->ld->listen && cmd->ld->portnum) { json_add_num(response, "port", cmd->ld->portnum); json_array_start(response, "address"); for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 4861d9149e58..3da3507a885a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -51,7 +51,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_debug_subdaemon = NULL; ld->dev_disconnect_fd = -1; ld->dev_subdaemon_fail = false; - ld->no_reconnect = false; if (getenv("LIGHTNINGD_DEV_MEMLEAK")) memleak_init(ld, backtrace_state); @@ -72,6 +71,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->close_commands); ld->wireaddrs = tal_arr(ld, struct wireaddr, 0); ld->portnum = DEFAULT_PORT; + ld->listen = true; + ld->reconnect = true; timers_init(&ld->timers, time_mono()); ld->topology = new_topology(ld, ld->log); ld->debug_subdaemon_io = NULL; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index b17c50df7d8e..2709a9ff18a9 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -111,6 +111,12 @@ struct lightningd { /* Port we're listening on */ u16 portnum; + /* Do we want to reconnect to other peers? */ + bool reconnect; + + /* Do we want to listen for other peers? */ + bool listen; + /* Addresses to announce to the network (tal_count()) */ struct wireaddr *wireaddrs; @@ -160,9 +166,6 @@ struct lightningd { /* May be useful for non-developers debugging in the field */ char *debug_subdaemon_io; - /* Disable automatic reconnects */ - bool no_reconnect; - /* Initial autocleaninvoice settings. */ u64 ini_autocleaninvoice_cycle; u64 ini_autocleaninvoice_expiredby; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 46aea9725d5d..a6030f118662 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -257,13 +257,6 @@ void tell_gossipd_peer_is_important(struct lightningd *ld, { u8 *msg; -#if DEVELOPER - /* Don't schedule an attempt if we disabled reconnections with - * the `--dev-no-reconnect` flag */ - if (ld->no_reconnect) - return; -#endif /* DEVELOPER */ - /* Tell gossipd we need to keep connection to this peer */ msg = towire_gossipctl_peer_important(NULL, &channel->peer->id, true); subd_send_msg(ld->gossip, take(msg)); diff --git a/lightningd/options.c b/lightningd/options.c index 12a973e1efeb..9bab94b250f6 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -259,8 +259,9 @@ static char *opt_set_fee_rates(const char *arg, struct chain_topology *topo) static char *opt_set_offline(struct lightningd *ld) { - ld->portnum = 0; - ld->no_reconnect = true; + ld->reconnect = false; + ld->listen = false; + return NULL; } @@ -360,8 +361,8 @@ static void config_register_opts(struct lightningd *ld) #if DEVELOPER static void dev_register_opts(struct lightningd *ld) { - opt_register_noarg("--dev-no-reconnect", opt_set_bool, - &ld->no_reconnect, + opt_register_noarg("--dev-no-reconnect", opt_set_invbool, + &ld->reconnect, "Disable automatic reconnect attempts"); opt_register_noarg("--dev-fail-on-subdaemon-fail", opt_set_bool, &ld->dev_subdaemon_fail, opt_hidden); @@ -767,11 +768,11 @@ void handle_opts(struct lightningd *ld, int argc, char *argv[]) check_config(ld); - if (ld->portnum && tal_count(ld->wireaddrs) == 0) + if (ld->portnum && ld->listen && tal_count(ld->wireaddrs) == 0) guess_addresses(ld); else log_debug(ld->log, "Not guessing addresses: %s", - ld->portnum ? "manually set" : "port set to zero"); + (ld->portnum && ld->listen) ? "manually set" : "port set to zero"); } /* FIXME: This is a hack! Expose somehow in ccan/opt.*/ @@ -804,12 +805,18 @@ static void add_config(struct lightningd *ld, /* These two show up as --network= */ || opt->cb == (void *)opt_set_testnet || opt->cb == (void *)opt_set_mainnet - || opt->cb == (void *)opt_set_offline /* will show up as port=0 and --no-reconnect */ || opt->cb == (void *)test_daemons_and_exit) { /* These are not important */ } else if (opt->cb == (void *)opt_set_bool) { const bool *b = opt->u.carg; answer = tal_fmt(name0, "%s", *b ? "true" : "false"); + } else if (opt->cb == (void *)opt_set_invbool) { + const bool *b = opt->u.carg; + answer = tal_fmt(name0, "%s", !*b ? "true" : "false"); + } else if (opt->cb == (void *)opt_set_offline) { + answer = tal_fmt(name0, "%s", + (!ld->reconnect && !ld->listen) + ? "true" : "false"); } else { /* Insert more decodes here! */ abort(); From c303b82e0b937f338b8a9d05c9ac392b351bc339 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:23:21 +0930 Subject: [PATCH 04/14] lightningd: deprecate --ipaddr in favor of --addr. We're going to add sockets, and later onion addresses, so the current name is bad. Signed-off-by: Rusty Russell --- lightningd/options.c | 26 +++++++++++++++++++------- tests/test_lightningd.py | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index 9bab94b250f6..0ee33951a5f4 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -138,7 +138,7 @@ static char *opt_set_s32(const char *arg, s32 *u) return NULL; } -static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) +static char *opt_add_addr(const char *arg, struct lightningd *ld) { size_t n = tal_count(ld->wireaddrs); char const *err_msg; @@ -155,6 +155,14 @@ static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) } +static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) +{ + log_broken(ld->log, "--ipaddr has been deprecated, use --addr"); + if (!deprecated_apis) + return "--ipaddr is deprecated"; + return opt_add_addr(arg, ld); +} + static void opt_show_u64(char buf[OPT_SHOW_LEN], const u64 *u) { snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, *u); @@ -328,10 +336,12 @@ static void config_register_opts(struct lightningd *ld) &ld->config.fee_per_satoshi, "Microsatoshi fee for every satoshi in HTLC"); opt_register_arg("--ipaddr", opt_add_ipaddr, NULL, + ld, opt_hidden); + opt_register_arg("--addr", opt_add_addr, NULL, ld, - "Set the IP address (v4 or v6) to announce to the network for incoming connections"); + "Set the IP address (v4 or v6) to listen on and announce to the network for incoming connections"); opt_register_noarg("--offline", opt_set_offline, ld, - "Start in offline-mode (do not automatically reconnect and do not accept incoming connections"); + "Start in offline-mode (do not automatically reconnect and do not accept incoming connections)"); opt_register_early_arg("--network", opt_set_network, opt_show_network, ld, @@ -341,10 +351,10 @@ static void config_register_opts(struct lightningd *ld) "Alias for --network=testnet"); opt_register_early_noarg("--mainnet", opt_set_mainnet, ld, "Alias for --network=bitcoin"); - opt_register_arg("--allow-deprecated-apis", - opt_set_bool_arg, opt_show_bool, - &deprecated_apis, - "Enable deprecated options, JSONRPC commands, fields, etc."); + opt_register_early_arg("--allow-deprecated-apis", + opt_set_bool_arg, opt_show_bool, + &deprecated_apis, + "Enable deprecated options, JSONRPC commands, fields, etc."); opt_register_arg("--debug-subdaemon-io", opt_set_charp, NULL, &ld->debug_subdaemon_io, "Enable full peer IO logging in subdaemons ending in this string (can also send SIGUSR1 to toggle)"); @@ -861,6 +871,8 @@ static void add_config(struct lightningd *ld, topo->override_fee_rate[1], topo->override_fee_rate[2]); } else if (opt->cb_arg == (void *)opt_add_ipaddr) { + /* Covered by opt_add_addr below */ + } else if (opt->cb_arg == (void *)opt_add_addr) { /* This is a bit weird, we can have multiple args */ for (size_t i = 0; i < tal_count(ld->wireaddrs); i++) { json_add_string(response, diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 189673f37ef4..94f4b32ef707 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -669,7 +669,7 @@ def test_connect_by_gossip(self): """ l1 = self.node_factory.get_node() l2 = self.node_factory.get_node() - l3 = self.node_factory.get_node(options={"ipaddr": "127.0.0.1"}) + l3 = self.node_factory.get_node(options={"addr": "127.0.0.1"}) l2.rpc.connect(l3.info['id'], 'localhost', l3.port) From dc38938611cdcc7ebbbc03a57de2bcd7744d8987 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:58:12 +0930 Subject: [PATCH 05/14] Clean up network options. It's become clear that our network options are insufficient, with the coming addition of Tor and unix domain support. Currently: 1. We always bind to local IPv4 and IPv6 sockets, unless --port=0, --offline, or any address is specified explicitly. If they're routable, we announce. 2. --addr is used to announce, but not to control binding. After this change: 1. --port is deprecated. 2. --addr controls what we bind to and announce. 3. --bind-addr/--announce-addr can be used to control one and not the other. 4. Unless --autolisten=0, we add local IPv4 & IPv6 port 9735 (and announce if they are routable). 5. --offline still overrides listening (though announcing is still the same). This means we can bind to as many ports/interfaces as we want, and for special effects we can announce different things (eg. we're sitting behind a port forward or a proxy). What remains to implement is semi-automatic binding: we should be able to say '--addr=0.0.0.0:9999' and have the address resolve at bind time, or even '--addr=0.0.0.0:0' and have the port autoresolve too (you could determine what it was from 'lightning-cli getinfo'. Signed-off-by: Rusty Russell --- common/wireaddr.c | 11 ++++ common/wireaddr.h | 12 ++++ gossipd/gossip.c | 123 ++++++++++++++++++------------------ gossipd/gossip_wire.csv | 5 +- lightningd/gossip_control.c | 6 +- lightningd/jsonrpc.c | 27 ++++++-- lightningd/lightningd.c | 2 + lightningd/lightningd.h | 7 +- lightningd/netaddress.c | 36 +++++++---- lightningd/options.c | 98 ++++++++++++++++++++++------ tests/test_gossip.py | 4 +- tests/test_lightningd.py | 1 - tests/utils.py | 5 +- 13 files changed, 228 insertions(+), 109 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 71062ab48475..1a621027e3e9 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -42,6 +42,17 @@ void towire_wireaddr(u8 **pptr, const struct wireaddr *addr) towire_u16(pptr, addr->port); } +enum addr_listen_announce fromwire_addr_listen_announce(const u8 **cursor, + size_t *max) +{ + return fromwire_u8(cursor, max); +} + +void towire_addr_listen_announce(u8 **pptr, enum addr_listen_announce ala) +{ + towire_u8(pptr, ala); +} + char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) { char addrstr[INET6_ADDRSTRLEN]; diff --git a/common/wireaddr.h b/common/wireaddr.h index ade052295c40..32a67417a851 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -37,10 +37,22 @@ struct wireaddr { u16 port; }; +/* We use wireaddr to tell gossipd both what to listen on, and what to + * announce */ +enum addr_listen_announce { + ADDR_LISTEN = (1 << 0), + ADDR_ANNOUNCE = (1 << 1), + ADDR_LISTEN_AND_ANNOUNCE = ADDR_LISTEN|ADDR_ANNOUNCE +}; + /* Inserts a single ADDR_TYPE_PADDING if addr is NULL */ void towire_wireaddr(u8 **pptr, const struct wireaddr *addr); bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr); +enum addr_listen_announce fromwire_addr_listen_announce(const u8 **cursor, + size_t *max); +void towire_addr_listen_announce(u8 **pptr, enum addr_listen_announce ala); + bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, const char **err_msg); char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 977a74e17bab..9aa33b2237f1 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -128,6 +128,7 @@ struct daemon { u8 alias[33]; u8 rgb[3]; struct wireaddr *wireaddrs; + enum addr_listen_announce *listen_announce; /* To make sure our node_announcement timestamps increase */ u32 last_announce_timestamp; @@ -563,8 +564,10 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, sig = tal(ctx, secp256k1_ecdsa_signature); memset(sig, 0, sizeof(*sig)); } - for (i = 0; i < tal_count(daemon->wireaddrs); i++) - towire_wireaddr(&addresses, daemon->wireaddrs+i); + for (i = 0; i < tal_count(daemon->wireaddrs); i++) { + if (daemon->listen_announce[i] & ADDR_ANNOUNCE) + towire_wireaddr(&addresses, daemon->wireaddrs+i); + } announcement = towire_node_announcement(ctx, sig, features, timestamp, @@ -1552,69 +1555,63 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon init_new_peer, daemon); } -static void setup_listeners(struct daemon *daemon, u16 portnum) +static void setup_listeners(struct daemon *daemon) { + bool had_ipv6_wildcard = false; struct sockaddr_in addr; struct sockaddr_in6 addr6; - socklen_t len; - int fd1, fd2; - - if (!portnum) { - status_info("Zero portnum, not listening for incoming"); - return; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(portnum); - - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; - addr6.sin6_addr = in6addr_any; - addr6.sin6_port = htons(portnum); - - /* IPv6, since on Linux that (usually) binds to IPv4 too. */ - fd1 = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), true); - if (fd1 >= 0) { - struct sockaddr_in6 in6 = {}; + int fd; - len = sizeof(in6); - if (getsockname(fd1, (void *)&in6, &len) != 0) { - status_broken("Failed get IPv6 sockname: %s", - strerror(errno)); - close_noerr(fd1); - fd1 = -1; - } else { - addr.sin_port = in6.sin6_port; - assert(portnum == ntohs(addr.sin_port)); - status_trace("Creating IPv6 listener on port %u", - portnum); - io_new_listener(daemon, fd1, connection_in, daemon); - } - } + for (size_t i = 0; i < tal_count(daemon->wireaddrs); i++) { + if (!(daemon->listen_announce[i] & ADDR_LISTEN)) + continue; - /* Just in case, aim for the same port... */ - fd2 = make_listen_fd(AF_INET, &addr, sizeof(addr), false); - if (fd2 >= 0) { - len = sizeof(addr); - if (getsockname(fd2, (void *)&addr, &len) != 0) { - status_broken("Failed get IPv4 sockname: %s", - strerror(errno)); - close_noerr(fd2); - fd2 = -1; - } else { - assert(portnum == ntohs(addr.sin_port)); - status_trace("Creating IPv4 listener on port %u", - portnum); - io_new_listener(daemon, fd2, connection_in, daemon); + switch (daemon->wireaddrs[i].type) { + case ADDR_TYPE_IPV4: + addr.sin_family = AF_INET; + addr.sin_port = htons(daemon->wireaddrs[i].port); + assert(daemon->wireaddrs[i].addrlen + == sizeof(addr.sin_addr)); + memcpy(&addr.sin_addr, + daemon->wireaddrs[i].addr, + sizeof(addr.sin_addr)); + /* We might fail if IPv6 bound to port first */ + fd = make_listen_fd(AF_INET, &addr, sizeof(addr), + !had_ipv6_wildcard); + if (fd >= 0) { + status_trace("Created IPv4 listener on port %u", + daemon->wireaddrs[i].port); + io_new_listener(daemon, fd, connection_in, + daemon); + } + continue; + case ADDR_TYPE_IPV6: + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(daemon->wireaddrs[i].port); + assert(daemon->wireaddrs[i].addrlen + == sizeof(addr6.sin6_addr)); + memcpy(&addr6.sin6_addr, daemon->wireaddrs[i].addr, + sizeof(addr6.sin6_addr)); + if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) + had_ipv6_wildcard = true; + fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), + true); + if (fd >= 0) { + status_trace("Created IPv6 listener on port %u", + daemon->wireaddrs[i].port); + io_new_listener(daemon, fd, connection_in, + daemon); + } + continue; + case ADDR_TYPE_PADDING: + break; } - } - - if (fd1 < 0 && fd2 < 0) + /* Shouldn't happen. */ status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Could not bind to a network address on port %u", - portnum); + "Invalid listener address type %u", + daemon->wireaddrs[i].type); + } } /* Parse an incoming gossip init message and assign config variables @@ -1630,7 +1627,8 @@ static struct io_plan *gossip_init(struct daemon_conn *master, if (!fromwire_gossipctl_init( daemon, msg, &daemon->broadcast_interval, &chain_hash, &daemon->id, &daemon->globalfeatures, - &daemon->localfeatures, &daemon->wireaddrs, daemon->rgb, + &daemon->localfeatures, &daemon->wireaddrs, + &daemon->listen_announce, daemon->rgb, daemon->alias, &update_channel_interval, &daemon->reconnect)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } @@ -1652,12 +1650,13 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, struct daemon *daemon, const u8 *msg) { - u16 port; + bool listen; - if (!fromwire_gossipctl_activate(msg, &port)) + if (!fromwire_gossipctl_activate(msg, &listen)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); - setup_listeners(daemon, port); + if (listen) + setup_listeners(daemon); /* OK, we're ready! */ daemon_conn_send(&daemon->master, diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index e530c341d40b..870878b79a88 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -13,6 +13,7 @@ gossipctl_init,,lflen,u16 gossipctl_init,,lfeatures,lflen*u8 gossipctl_init,,num_wireaddrs,u16 gossipctl_init,,wireaddrs,num_wireaddrs*struct wireaddr +gossipctl_init,,listen_announce,num_wireaddrs*enum addr_listen_announce gossipctl_init,,rgb,3*u8 gossipctl_init,,alias,32*u8 gossipctl_init,,update_channel_interval,u32 @@ -20,8 +21,8 @@ gossipctl_init,,reconnect,bool # Activate the gossip daemon, so others can connect. gossipctl_activate,3025 -# If non-zero, port to listen on. -gossipctl_activate,,port,u16 +# Do we listen? +gossipctl_activate,,listen,bool # Gossipd->master, I am ready. gossipctl_activate_reply,3125 diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 1a19bc3691d2..37ae6393c443 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -206,7 +206,8 @@ void gossip_init(struct lightningd *ld) tmpctx, ld->config.broadcast_interval, &get_chainparams(ld)->genesis_blockhash, &ld->id, get_offered_global_features(tmpctx), - get_offered_local_features(tmpctx), ld->wireaddrs, ld->rgb, + get_offered_local_features(tmpctx), ld->wireaddrs, + ld->listen_announce, ld->rgb, ld->alias, ld->config.channel_update_interval, ld->reconnect); subd_send_msg(ld->gossip, msg); } @@ -222,8 +223,7 @@ static void gossip_activate_done(struct subd *gossip UNUSED, void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, - ld->listen ? ld->portnum : 0); + const u8 *msg = towire_gossipctl_activate(NULL, ld->listen); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index d3f050aeb759..7ed0709cf570 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -149,12 +149,31 @@ static void json_getinfo(struct command *cmd, json_object_start(response, NULL); json_add_pubkey(response, "id", &cmd->ld->id); - if (cmd->ld->listen && cmd->ld->portnum) { - json_add_num(response, "port", cmd->ld->portnum); + if (cmd->ld->listen) { + bool have_listen_no_announce = false; + if (deprecated_apis) + json_add_num(response, "port", cmd->ld->portnum); + + /* These are the addresses we're announcing */ json_array_start(response, "address"); - for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) - json_add_address(response, NULL, cmd->ld->wireaddrs+i); + for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { + if (cmd->ld->listen_announce[i] & ADDR_ANNOUNCE) + json_add_address(response, NULL, + cmd->ld->wireaddrs+i); + else + have_listen_no_announce = true; + } json_array_end(response); + + if (have_listen_no_announce) { + json_array_start(response, "listen-only"); + for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { + if (cmd->ld->listen_announce[i] == ADDR_LISTEN) + json_add_address(response, NULL, + cmd->ld->wireaddrs+i); + } + json_array_end(response); + } } json_add_string(response, "version", version()); json_add_num(response, "blockheight", get_block_height(cmd->ld->topology)); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 3da3507a885a..fefc5c97609e 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -70,8 +70,10 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->sendpay_commands); list_head_init(&ld->close_commands); ld->wireaddrs = tal_arr(ld, struct wireaddr, 0); + ld->listen_announce = tal_arr(ld, enum addr_listen_announce, 0); ld->portnum = DEFAULT_PORT; ld->listen = true; + ld->autolisten = true; ld->reconnect = true; timers_init(&ld->timers, time_mono()); ld->topology = new_topology(ld, ld->log); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 2709a9ff18a9..e60f25e82343 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -117,8 +117,13 @@ struct lightningd { /* Do we want to listen for other peers? */ bool listen; - /* Addresses to announce to the network (tal_count()) */ + /* Do we want to guess addresses to listen and announce? */ + bool autolisten; + + /* Addresses to bind/announce to the network (tal_count()) */ struct wireaddr *wireaddrs; + /* And the bitset for each, whether to listen, announce or both */ + enum addr_listen_announce *listen_announce; /* Bearer of all my secrets. */ int hsm_fd; diff --git a/lightningd/netaddress.c b/lightningd/netaddress.c index 4231a6977ef2..987c20e6656b 100644 --- a/lightningd/netaddress.c +++ b/lightningd/netaddress.c @@ -228,10 +228,13 @@ static bool get_local_sockname(struct lightningd *ld, return true; } -/* Return a wireaddr without port filled in */ -static bool guess_one_address(struct lightningd *ld, - struct wireaddr *addr, u16 portnum, - enum wire_addr_type type) +/* Return 0 if not available, or whether it's listenable-only or announceable. + * If it's listenable only, will set wireaddr to all-zero address for universal + * binding. */ +static enum addr_listen_announce guess_one_address(struct lightningd *ld, + struct wireaddr *addr, + u16 portnum, + enum wire_addr_type type) { addr->type = type; addr->port = portnum; @@ -245,7 +248,7 @@ static bool guess_one_address(struct lightningd *ld, sin.sin_addr.s_addr = 0x08080808; sin.sin_family = AF_INET; if (!get_local_sockname(ld, AF_INET, &sin, sizeof(sin))) - return false; + return 0; addr->addrlen = sizeof(sin.sin_addr); memcpy(addr->addr, &sin.sin_addr, addr->addrlen); break; @@ -260,25 +263,27 @@ static bool guess_one_address(struct lightningd *ld, sin6.sin6_family = AF_INET6; memcpy(sin6.sin6_addr.s6_addr, pchGoogle, sizeof(pchGoogle)); if (!get_local_sockname(ld, AF_INET6, &sin6, sizeof(sin6))) - return false; + return 0; addr->addrlen = sizeof(sin6.sin6_addr); memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); break; } case ADDR_TYPE_PADDING: log_debug(ld->log, "Padding address, ignoring"); - return false; + return 0; } if (!IsRoutable(addr)) { log_debug(ld->log, "Address %s is not routable", type_to_string(tmpctx, struct wireaddr, addr)); - return false; + /* This corresponds to INADDR_ANY or in6addr_any */ + memset(addr->addr, 0, addr->addrlen); + return ADDR_LISTEN; } log_debug(ld->log, "Public address %s", type_to_string(tmpctx, struct wireaddr, addr)); - return true; + return ADDR_LISTEN_AND_ANNOUNCE; } void guess_addresses(struct lightningd *ld) @@ -289,10 +294,19 @@ void guess_addresses(struct lightningd *ld) /* We allocate an extra, then remove if it's not needed. */ tal_resize(&ld->wireaddrs, n+1); - if (guess_one_address(ld, &ld->wireaddrs[n], ld->portnum, ADDR_TYPE_IPV4)) { + tal_resize(&ld->listen_announce, n+1); + /* We do IPv6 first: on Linux, that binds to IPv4 too. */ + ld->listen_announce[n] = guess_one_address(ld, &ld->wireaddrs[n], + ld->portnum, ADDR_TYPE_IPV6); + if (ld->listen_announce[n] != 0) { n++; tal_resize(&ld->wireaddrs, n+1); + tal_resize(&ld->listen_announce, n+1); } - if (!guess_one_address(ld, &ld->wireaddrs[n], ld->portnum, ADDR_TYPE_IPV6)) + ld->listen_announce[n] = guess_one_address(ld, &ld->wireaddrs[n], + ld->portnum, ADDR_TYPE_IPV4); + if (ld->listen_announce[n] == 0) { tal_resize(&ld->wireaddrs, n); + tal_resize(&ld->listen_announce, n); + } } diff --git a/lightningd/options.c b/lightningd/options.c index 0ee33951a5f4..6a907ec1f505 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -102,11 +102,15 @@ static char *opt_set_u32(const char *arg, u32 *u) return NULL; } -static char *opt_set_u16(const char *arg, u16 *u) +static char *opt_set_port(const char *arg, struct lightningd *ld) { char *endp; unsigned long l; + log_broken(ld->log, "--port has been deprecated, use --autolisten=0 or --addr=:"); + if (!deprecated_apis) + return "--port is deprecated"; + assert(arg != NULL); /* This is how the manpage says to do it. Yech. */ @@ -114,9 +118,13 @@ static char *opt_set_u16(const char *arg, u16 *u) l = strtoul(arg, &endp, 0); if (*endp || !arg[0]) return tal_fmt(NULL, "'%s' is not a number", arg); - *u = l; - if (errno || *u != l) + ld->portnum = l; + if (errno || ld->portnum != l) return tal_fmt(NULL, "'%s' is out of range", arg); + + if (ld->portnum == 0) + ld->autolisten = false; + return NULL; } @@ -138,7 +146,9 @@ static char *opt_set_s32(const char *arg, s32 *u) return NULL; } -static char *opt_add_addr(const char *arg, struct lightningd *ld) +static char *opt_add_addr_withtype(const char *arg, + struct lightningd *ld, + enum addr_listen_announce ala) { size_t n = tal_count(ld->wireaddrs); char const *err_msg; @@ -146,6 +156,8 @@ static char *opt_add_addr(const char *arg, struct lightningd *ld) assert(arg != NULL); tal_resize(&ld->wireaddrs, n+1); + tal_resize(&ld->listen_announce, n+1); + ld->listen_announce[n] = ala; if (!parse_wireaddr(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { return tal_fmt(NULL, "Unable to parse IP address '%s': %s", arg, err_msg); @@ -155,6 +167,21 @@ static char *opt_add_addr(const char *arg, struct lightningd *ld) } +static char *opt_add_addr(const char *arg, struct lightningd *ld) +{ + return opt_add_addr_withtype(arg, ld, ADDR_LISTEN_AND_ANNOUNCE); +} + +static char *opt_add_bind_addr(const char *arg, struct lightningd *ld) +{ + return opt_add_addr_withtype(arg, ld, ADDR_LISTEN); +} + +static char *opt_add_announce_addr(const char *arg, struct lightningd *ld) +{ + return opt_add_addr_withtype(arg, ld, ADDR_ANNOUNCE); +} + static char *opt_add_ipaddr(const char *arg, struct lightningd *ld) { log_broken(ld->log, "--ipaddr has been deprecated, use --addr"); @@ -177,11 +204,6 @@ static void opt_show_s32(char buf[OPT_SHOW_LEN], const s32 *u) snprintf(buf, OPT_SHOW_LEN, "%"PRIi32, *u); } -static void opt_show_u16(char buf[OPT_SHOW_LEN], const u16 *u) -{ - snprintf(buf, OPT_SHOW_LEN, "%u", *u); -} - static char *opt_set_network(const char *arg, struct lightningd *ld) { assert(arg != NULL); @@ -339,9 +361,19 @@ static void config_register_opts(struct lightningd *ld) ld, opt_hidden); opt_register_arg("--addr", opt_add_addr, NULL, ld, - "Set the IP address (v4 or v6) to listen on and announce to the network for incoming connections"); + "Set an IP address (v4 or v6) to listen on and announce to the network for incoming connections"); + opt_register_arg("--bind-addr", opt_add_bind_addr, NULL, + ld, + "Set an IP address (v4 or v6) to listen on, but not announce"); + opt_register_arg("--announce-addr", opt_add_announce_addr, NULL, + ld, + "Set an IP address (v4 or v6) to announce, but not listen on"); + opt_register_noarg("--offline", opt_set_offline, ld, "Start in offline-mode (do not automatically reconnect and do not accept incoming connections)"); + opt_register_arg("--autolisten", opt_set_bool_arg, opt_show_bool, + &ld->autolisten, + "If true, listen on default port and announce if it seems to be a public interface"); opt_register_early_arg("--network", opt_set_network, opt_show_network, ld, @@ -649,8 +681,8 @@ void register_opts(struct lightningd *ld) /* --port needs to be an early arg to force it being parsed * before --ipaddr which may depend on it */ - opt_register_early_arg("--port", opt_set_u16, opt_show_u16, &ld->portnum, - "Port to bind to (0 means don't listen)"); + opt_register_early_arg("--port", opt_set_port, NULL, ld, + opt_hidden); opt_register_arg("--bitcoin-datadir", opt_set_talstr, NULL, &ld->topology->bitcoind->datadir, "-datadir arg for bitcoin-cli"); @@ -778,11 +810,11 @@ void handle_opts(struct lightningd *ld, int argc, char *argv[]) check_config(ld); - if (ld->portnum && ld->listen && tal_count(ld->wireaddrs) == 0) + if (ld->listen && ld->autolisten) guess_addresses(ld); else log_debug(ld->log, "Not guessing addresses: %s", - (ld->portnum && ld->listen) ? "manually set" : "port set to zero"); + !ld->listen ? "--offline" : "--autolisten=0"); } /* FIXME: This is a hack! Expose somehow in ccan/opt.*/ @@ -801,6 +833,21 @@ static const char *next_name(const char *names, unsigned *len) return first_name(names + 1, len); } +static void json_add_opt_addrs(struct json_result *response, + const char *name0, + const struct wireaddr *wireaddrs, + const enum addr_listen_announce *listen_announce, + enum addr_listen_announce ala) +{ + for (size_t i = 0; i < tal_count(wireaddrs); i++) { + if (listen_announce[i] != ala) + continue; + json_add_string(response, + name0, + fmt_wireaddr(name0, wireaddrs+i)); + } +} + static void add_config(struct lightningd *ld, struct json_result *response, const struct opt_table *opt, @@ -870,16 +917,25 @@ static void add_config(struct lightningd *ld, topo->override_fee_rate[0], topo->override_fee_rate[1], topo->override_fee_rate[2]); + } else if (opt->cb_arg == (void *)opt_set_port) { + if (!deprecated_apis) + answer = tal_fmt(name0, "%u", ld->portnum); } else if (opt->cb_arg == (void *)opt_add_ipaddr) { /* Covered by opt_add_addr below */ } else if (opt->cb_arg == (void *)opt_add_addr) { - /* This is a bit weird, we can have multiple args */ - for (size_t i = 0; i < tal_count(ld->wireaddrs); i++) { - json_add_string(response, - name0, - fmt_wireaddr(name0, - ld->wireaddrs+i)); - } + json_add_opt_addrs(response, name0, + ld->wireaddrs, ld->listen_announce, + ADDR_LISTEN_AND_ANNOUNCE); + return; + } else if (opt->cb_arg == (void *)opt_add_bind_addr) { + json_add_opt_addrs(response, name0, + ld->wireaddrs, ld->listen_announce, + ADDR_LISTEN); + return; + } else if (opt->cb_arg == (void *)opt_add_announce_addr) { + json_add_opt_addrs(response, name0, + ld->wireaddrs, ld->listen_announce, + ADDR_ANNOUNCE); return; #if DEVELOPER } else if (strstarts(name, "dev-")) { diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 963cc183cd90..e595d863d71e 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -16,8 +16,8 @@ def test_gossip_pruning(node_factory, bitcoind): opts = {'channel-update-interval': 5} l1, l2, l3 = node_factory.get_nodes(3, opts) - l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) - l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port']) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) scid1 = l1.fund_channel(l2, 10**6) scid2 = l2.fund_channel(l3, 10**6) diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 94f4b32ef707..bdf4d738abc0 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -4288,7 +4288,6 @@ def test_listconfigs(self): # See utils.py assert configs['bitcoin-datadir'] == bitcoind.bitcoin_dir assert configs['lightning-dir'] == l1.daemon.lightning_dir - assert configs['port'] == l1.port assert configs['allow-deprecated-apis'] is False assert configs['override-fee-rates'] == '15000/7500/1000' assert configs['network'] == 'regtest' diff --git a/tests/utils.py b/tests/utils.py index ce508a53a544..5ddba846e673 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -261,7 +261,7 @@ def __init__(self, lightning_dir, bitcoin_dir, port=9735, random_hsm=False, node opts = { 'bitcoin-datadir': bitcoin_dir, 'lightning-dir': lightning_dir, - 'port': port, + 'addr': '127.0.0.1:{}'.format(port), 'allow-deprecated-apis': 'false', 'override-fee-rates': '15000/7500/1000', 'network': 'regtest', @@ -369,7 +369,8 @@ def db_manip(self, query): def start(self): self.daemon.start() - self.port = self.rpc.getinfo()['port'] + # This shortcut is sufficient for our simple tests. + self.port = self.rpc.getinfo()['address'][0]['port'] def stop(self, timeout=10): """ Attempt to do a clean shutdown, but kill if it hangs From 7b1d1af87e7650d42f99800ed7e723b4a81bdcd9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:12 +0930 Subject: [PATCH 06/14] lightningd: still bind to local ports even if address not public. Now we only bind to addresses in our wireaddrs array, we would not autobind to local sockets if they couldn't reach google's nameserver. That's clearly wrong: we should only not bind if there's a protocol issue (eg. no IPv6 support). Signed-off-by: Rusty Russell --- lightningd/netaddress.c | 42 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lightningd/netaddress.c b/lightningd/netaddress.c index 987c20e6656b..3f80a908c1c3 100644 --- a/lightningd/netaddress.c +++ b/lightningd/netaddress.c @@ -200,32 +200,35 @@ static bool IsRoutable(const struct wireaddr *addr) /* Trick I learned from Harald Welte: create UDP socket, connect() and * then query address. */ -static bool get_local_sockname(struct lightningd *ld, - int af, void *saddr, socklen_t saddrlen) +/* Returns 0 if protocol completely unsupported, ADDR_LISTEN if we + * can't reach addr, ADDR_LISTEN_AND_ANNOUNCE if we can (and fill saddr). */ +static enum addr_listen_announce get_local_sockname(struct lightningd *ld, + int af, void *saddr, + socklen_t saddrlen) { int fd = socket(af, SOCK_DGRAM, 0); if (fd < 0) { log_debug(ld->log, "Failed to create %u socket: %s", af, strerror(errno)); - return false; + return 0; } if (connect(fd, saddr, saddrlen) != 0) { log_debug(ld->log, "Failed to connect %u socket: %s", af, strerror(errno)); close(fd); - return false; + return ADDR_LISTEN; } if (getsockname(fd, saddr, &saddrlen) != 0) { log_debug(ld->log, "Failed to get %u socket name: %s", af, strerror(errno)); close(fd); - return false; + return ADDR_LISTEN; } close(fd); - return true; + return ADDR_LISTEN_AND_ANNOUNCE; } /* Return 0 if not available, or whether it's listenable-only or announceable. @@ -236,6 +239,8 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, u16 portnum, enum wire_addr_type type) { + enum addr_listen_announce ret; + addr->type = type; addr->port = portnum; @@ -247,8 +252,7 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, /* 8.8.8.8 */ sin.sin_addr.s_addr = 0x08080808; sin.sin_family = AF_INET; - if (!get_local_sockname(ld, AF_INET, &sin, sizeof(sin))) - return 0; + ret = get_local_sockname(ld, AF_INET, &sin, sizeof(sin)); addr->addrlen = sizeof(sin.sin_addr); memcpy(addr->addr, &sin.sin_addr, addr->addrlen); break; @@ -262,8 +266,7 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, sin6.sin6_port = htons(53); sin6.sin6_family = AF_INET6; memcpy(sin6.sin6_addr.s6_addr, pchGoogle, sizeof(pchGoogle)); - if (!get_local_sockname(ld, AF_INET6, &sin6, sizeof(sin6))) - return 0; + ret = get_local_sockname(ld, AF_INET6, &sin6, sizeof(sin6)); addr->addrlen = sizeof(sin6.sin6_addr); memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); break; @@ -273,17 +276,24 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, return 0; } - if (!IsRoutable(addr)) { + if (ret == 0) + return ret; + + /* If we can reach it, but resulting address is unroutable, listen only */ + if (ret == ADDR_LISTEN_AND_ANNOUNCE && !IsRoutable(addr)) { log_debug(ld->log, "Address %s is not routable", type_to_string(tmpctx, struct wireaddr, addr)); + ret = ADDR_LISTEN; + } + + if (ret == ADDR_LISTEN) { /* This corresponds to INADDR_ANY or in6addr_any */ memset(addr->addr, 0, addr->addrlen); - return ADDR_LISTEN; + } else { + log_debug(ld->log, "Public address %s", + type_to_string(tmpctx, struct wireaddr, addr)); } - - log_debug(ld->log, "Public address %s", - type_to_string(tmpctx, struct wireaddr, addr)); - return ADDR_LISTEN_AND_ANNOUNCE; + return ret; } void guess_addresses(struct lightningd *ld) From 695324286e43cc5e91fef9d2318597c246423d42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:21 +0930 Subject: [PATCH 07/14] wireaddr: helpers to convert to/from IPv4/v6 addresses. Signed-off-by: Rusty Russell --- common/wireaddr.c | 64 +++++++++++++++++++++++++++++++++++------------ common/wireaddr.h | 14 +++++++++++ gossipd/gossip.c | 41 ++++++------------------------ 3 files changed, 70 insertions(+), 49 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 1a621027e3e9..f370bafaf6a1 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -78,6 +78,50 @@ char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) } REGISTER_TYPE_TO_STRING(wireaddr, fmt_wireaddr); +void wireaddr_from_ipv4(struct wireaddr *addr, + const struct in_addr *ip4, + const u16 port) +{ + addr->type = ADDR_TYPE_IPV4; + addr->addrlen = sizeof(*ip4); + addr->port = port; + memset(addr->addr, 0, sizeof(addr->addr)); + memcpy(addr->addr, ip4, addr->addrlen); +} + +void wireaddr_from_ipv6(struct wireaddr *addr, + const struct in6_addr *ip6, + const u16 port) +{ + addr->type = ADDR_TYPE_IPV6; + addr->addrlen = sizeof(*ip6); + addr->port = port; + memset(addr->addr, 0, sizeof(addr->addr)); + memcpy(&addr->addr, ip6, addr->addrlen); +} + +bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4) +{ + if (addr->type != ADDR_TYPE_IPV4) + return false; + s4->sin_family = AF_INET; + s4->sin_port = htons(addr->port); + assert(addr->addrlen == sizeof(s4->sin_addr)); + memcpy(&s4->sin_addr, addr->addr, sizeof(s4->sin_addr)); + return true; +} + +bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6) +{ + if (addr->type != ADDR_TYPE_IPV6) + return false; + s6->sin6_family = AF_INET6; + s6->sin6_port = htons(addr->port); + assert(addr->addrlen == sizeof(s6->sin6_addr)); + memcpy(&s6->sin6_addr, addr->addr, sizeof(s6->sin6_addr)); + return true; +} + /* Valid forms: * * [anything]: @@ -148,18 +192,12 @@ bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname, } /* Use only the first found address */ if (addrinfo->ai_family == AF_INET) { - addr->type = ADDR_TYPE_IPV4; - addr->addrlen = 4; - addr->port = port; sa4 = (struct sockaddr_in *) addrinfo->ai_addr; - memcpy(&addr->addr, &sa4->sin_addr, addr->addrlen); + wireaddr_from_ipv4(addr, &sa4->sin_addr, port); res = true; } else if (addrinfo->ai_family == AF_INET6) { - addr->type = ADDR_TYPE_IPV6; - addr->addrlen = 16; - addr->port = port; sa6 = (struct sockaddr_in6 *) addrinfo->ai_addr; - memcpy(&addr->addr, &sa6->sin6_addr, addr->addrlen); + wireaddr_from_ipv6(addr, &sa6->sin6_addr, port); res = true; } @@ -193,16 +231,10 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, memset(&addr->addr, 0, sizeof(addr->addr)); if (inet_pton(AF_INET, ip, &v4) == 1) { - addr->type = ADDR_TYPE_IPV4; - addr->addrlen = 4; - addr->port = port; - memcpy(&addr->addr, &v4, addr->addrlen); + wireaddr_from_ipv4(addr, &v4, port); res = true; } else if (inet_pton(AF_INET6, ip, &v6) == 1) { - addr->type = ADDR_TYPE_IPV6; - addr->addrlen = 16; - addr->port = port; - memcpy(&addr->addr, &v6, addr->addrlen); + wireaddr_from_ipv6(addr, &v6, port); res = true; } diff --git a/common/wireaddr.h b/common/wireaddr.h index 32a67417a851..e639b34823b5 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -6,6 +6,11 @@ #include #include +struct in6_addr; +struct in_addr; +struct sockaddr_in6; +struct sockaddr_in; + /* BOLT #7: * * The following `address descriptor` types are defined: @@ -60,4 +65,13 @@ char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname, const u16 port, const char **err_msg); +void wireaddr_from_ipv4(struct wireaddr *addr, + const struct in_addr *ip4, + const u16 port); +void wireaddr_from_ipv6(struct wireaddr *addr, + const struct in6_addr *ip6, + const u16 port); +bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4); +bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); + #endif /* LIGHTNING_COMMON_WIREADDR_H */ diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 9aa33b2237f1..8dd2807227f5 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1532,18 +1532,10 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon if (s.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (void *)&s; - addr.type = ADDR_TYPE_IPV6; - addr.addrlen = sizeof(s6->sin6_addr); - BUILD_ASSERT(sizeof(s6->sin6_addr) <= sizeof(addr.addr)); - memcpy(addr.addr, &s6->sin6_addr, addr.addrlen); - addr.port = ntohs(s6->sin6_port); + wireaddr_from_ipv6(&addr, &s6->sin6_addr, ntohs(s6->sin6_port)); } else if (s.ss_family == AF_INET) { struct sockaddr_in *s4 = (void *)&s; - addr.type = ADDR_TYPE_IPV4; - addr.addrlen = sizeof(s4->sin_addr); - BUILD_ASSERT(sizeof(s4->sin_addr) <= sizeof(addr.addr)); - memcpy(addr.addr, &s4->sin_addr, addr.addrlen); - addr.port = ntohs(s4->sin_port); + wireaddr_from_ipv4(&addr, &s4->sin_addr, ntohs(s4->sin_port)); } else { status_broken("Unknown socket type %i for incoming conn", s.ss_family); @@ -1568,13 +1560,7 @@ static void setup_listeners(struct daemon *daemon) switch (daemon->wireaddrs[i].type) { case ADDR_TYPE_IPV4: - addr.sin_family = AF_INET; - addr.sin_port = htons(daemon->wireaddrs[i].port); - assert(daemon->wireaddrs[i].addrlen - == sizeof(addr.sin_addr)); - memcpy(&addr.sin_addr, - daemon->wireaddrs[i].addr, - sizeof(addr.sin_addr)); + wireaddr_to_ipv4(&daemon->wireaddrs[i], &addr); /* We might fail if IPv6 bound to port first */ fd = make_listen_fd(AF_INET, &addr, sizeof(addr), !had_ipv6_wildcard); @@ -1586,13 +1572,7 @@ static void setup_listeners(struct daemon *daemon) } continue; case ADDR_TYPE_IPV6: - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(daemon->wireaddrs[i].port); - assert(daemon->wireaddrs[i].addrlen - == sizeof(addr6.sin6_addr)); - memcpy(&addr6.sin6_addr, daemon->wireaddrs[i].addr, - sizeof(addr6.sin6_addr)); + wireaddr_to_ipv6(&daemon->wireaddrs[i], &addr6); if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) had_ipv6_wildcard = true; fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), @@ -1771,19 +1751,14 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) switch (reach->addr.type) { case ADDR_TYPE_IPV4: - ai.ai_family = AF_INET; - sin.sin_family = AF_INET; - sin.sin_port = htons(reach->addr.port); - memcpy(&sin.sin_addr, reach->addr.addr, sizeof(sin.sin_addr)); + wireaddr_to_ipv4(&reach->addr, &sin); + ai.ai_family = sin.sin_family; ai.ai_addrlen = sizeof(sin); ai.ai_addr = (struct sockaddr *)&sin; break; case ADDR_TYPE_IPV6: - ai.ai_family = AF_INET6; - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(reach->addr.port); - memcpy(&sin6.sin6_addr, reach->addr.addr, sizeof(sin6.sin6_addr)); + wireaddr_to_ipv6(&reach->addr, &sin6); + ai.ai_family = sin6.sin6_family; ai.ai_addrlen = sizeof(sin6); ai.ai_addr = (struct sockaddr *)&sin6; break; From 5b33adf673aecf511ec94aae4b3a247359ec503f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:21 +0930 Subject: [PATCH 08/14] gossipd: take over address determination, from master. It does all the other address handling, do this too. It also proves useful as we clean up wildcard address handling. Signed-off-by: Rusty Russell --- gossipd/Makefile | 1 + gossipd/gossip.c | 16 +++++-- gossipd/gossip_wire.csv | 8 +++- {lightningd => gossipd}/netaddress.c | 70 ++++++++++++++-------------- gossipd/netaddress.h | 10 ++++ lightningd/Makefile | 1 - lightningd/gossip_control.c | 16 ++++++- lightningd/netaddress.h | 11 ----- lightningd/options.c | 7 --- lightningd/peer_control.c | 1 - 10 files changed, 80 insertions(+), 61 deletions(-) rename {lightningd => gossipd}/netaddress.c (82%) create mode 100644 gossipd/netaddress.h delete mode 100644 lightningd/netaddress.h diff --git a/gossipd/Makefile b/gossipd/Makefile index 8d9274d4000a..adecacc0a717 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -16,6 +16,7 @@ LIGHTNINGD_GOSSIP_HEADERS := gossipd/gen_gossip_wire.h \ gossipd/gen_gossip_store.h \ gossipd/gossip_store.h \ gossipd/handshake.h \ + gossipd/netaddress.h \ gossipd/routing.h \ gossipd/broadcast.h LIGHTNINGD_GOSSIP_SRC := gossipd/gossip.c \ diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 8dd2807227f5..1f05943607b5 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1631,16 +1632,25 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, const u8 *msg) { bool listen; + bool guess_addrs; + u16 port; - if (!fromwire_gossipctl_activate(msg, &listen)) + if (!fromwire_gossipctl_activate(msg, &listen, &guess_addrs, &port)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); - if (listen) + if (listen) { + if (guess_addrs) + guess_addresses(&daemon->wireaddrs, + &daemon->listen_announce, + port); setup_listeners(daemon); + } /* OK, we're ready! */ daemon_conn_send(&daemon->master, - take(towire_gossipctl_activate_reply(NULL))); + take(towire_gossipctl_activate_reply(NULL, + daemon->wireaddrs, + daemon->listen_announce))); return daemon_conn_read_next(master->conn, master); } diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 870878b79a88..8f781d3fd6d1 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -23,9 +23,15 @@ gossipctl_init,,reconnect,bool gossipctl_activate,3025 # Do we listen? gossipctl_activate,,listen,bool +gossipctl_activate,,guess_addresses,bool +# FIXME: Hack for deprecated --port option. +gossipctl_activate,,port,u16 -# Gossipd->master, I am ready. +# Gossipd->master, I am ready, here are the final addresses. gossipctl_activate_reply,3125 +gossipctl_activate_reply,,num_wireaddrs,u16 +gossipctl_activate_reply,,wireaddrs,num_wireaddrs*struct wireaddr +gossipctl_activate_reply,,listen_announce,num_wireaddrs*enum addr_listen_announce # Master -> gossipd: Optional hint for where to find peer. gossipctl_peer_addrhint,3014 diff --git a/lightningd/netaddress.c b/gossipd/netaddress.c similarity index 82% rename from lightningd/netaddress.c rename to gossipd/netaddress.c index 3f80a908c1c3..7d23ed0127e6 100644 --- a/lightningd/netaddress.c +++ b/gossipd/netaddress.c @@ -1,10 +1,10 @@ #include #include +#include +#include #include #include -#include -#include -#include +#include #include #include #include @@ -202,27 +202,26 @@ static bool IsRoutable(const struct wireaddr *addr) * then query address. */ /* Returns 0 if protocol completely unsupported, ADDR_LISTEN if we * can't reach addr, ADDR_LISTEN_AND_ANNOUNCE if we can (and fill saddr). */ -static enum addr_listen_announce get_local_sockname(struct lightningd *ld, - int af, void *saddr, +static enum addr_listen_announce get_local_sockname(int af, void *saddr, socklen_t saddrlen) { int fd = socket(af, SOCK_DGRAM, 0); if (fd < 0) { - log_debug(ld->log, "Failed to create %u socket: %s", - af, strerror(errno)); + status_trace("Failed to create %u socket: %s", + af, strerror(errno)); return 0; } if (connect(fd, saddr, saddrlen) != 0) { - log_debug(ld->log, "Failed to connect %u socket: %s", - af, strerror(errno)); + status_trace("Failed to connect %u socket: %s", + af, strerror(errno)); close(fd); return ADDR_LISTEN; } if (getsockname(fd, saddr, &saddrlen) != 0) { - log_debug(ld->log, "Failed to get %u socket name: %s", - af, strerror(errno)); + status_trace("Failed to get %u socket name: %s", + af, strerror(errno)); close(fd); return ADDR_LISTEN; } @@ -234,8 +233,7 @@ static enum addr_listen_announce get_local_sockname(struct lightningd *ld, /* Return 0 if not available, or whether it's listenable-only or announceable. * If it's listenable only, will set wireaddr to all-zero address for universal * binding. */ -static enum addr_listen_announce guess_one_address(struct lightningd *ld, - struct wireaddr *addr, +static enum addr_listen_announce guess_one_address(struct wireaddr *addr, u16 portnum, enum wire_addr_type type) { @@ -252,7 +250,7 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, /* 8.8.8.8 */ sin.sin_addr.s_addr = 0x08080808; sin.sin_family = AF_INET; - ret = get_local_sockname(ld, AF_INET, &sin, sizeof(sin)); + ret = get_local_sockname(AF_INET, &sin, sizeof(sin)); addr->addrlen = sizeof(sin.sin_addr); memcpy(addr->addr, &sin.sin_addr, addr->addrlen); break; @@ -266,13 +264,13 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, sin6.sin6_port = htons(53); sin6.sin6_family = AF_INET6; memcpy(sin6.sin6_addr.s6_addr, pchGoogle, sizeof(pchGoogle)); - ret = get_local_sockname(ld, AF_INET6, &sin6, sizeof(sin6)); + ret = get_local_sockname(AF_INET6, &sin6, sizeof(sin6)); addr->addrlen = sizeof(sin6.sin6_addr); memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); break; } case ADDR_TYPE_PADDING: - log_debug(ld->log, "Padding address, ignoring"); + status_trace("Padding address, ignoring"); return 0; } @@ -281,8 +279,8 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, /* If we can reach it, but resulting address is unroutable, listen only */ if (ret == ADDR_LISTEN_AND_ANNOUNCE && !IsRoutable(addr)) { - log_debug(ld->log, "Address %s is not routable", - type_to_string(tmpctx, struct wireaddr, addr)); + status_trace("Address %s is not routable", + type_to_string(tmpctx, struct wireaddr, addr)); ret = ADDR_LISTEN; } @@ -290,33 +288,35 @@ static enum addr_listen_announce guess_one_address(struct lightningd *ld, /* This corresponds to INADDR_ANY or in6addr_any */ memset(addr->addr, 0, addr->addrlen); } else { - log_debug(ld->log, "Public address %s", - type_to_string(tmpctx, struct wireaddr, addr)); + status_trace("Public address %s", + type_to_string(tmpctx, struct wireaddr, addr)); } return ret; } -void guess_addresses(struct lightningd *ld) +void guess_addresses(struct wireaddr **wireaddrs, + enum addr_listen_announce **listen_announce, + u16 portnum) { - size_t n = tal_count(ld->wireaddrs); + size_t n = tal_count(*wireaddrs); - log_debug(ld->log, "Trying to guess public addresses..."); + status_trace("Trying to guess public addresses..."); /* We allocate an extra, then remove if it's not needed. */ - tal_resize(&ld->wireaddrs, n+1); - tal_resize(&ld->listen_announce, n+1); + tal_resize(wireaddrs, n+1); + tal_resize(listen_announce, n+1); /* We do IPv6 first: on Linux, that binds to IPv4 too. */ - ld->listen_announce[n] = guess_one_address(ld, &ld->wireaddrs[n], - ld->portnum, ADDR_TYPE_IPV6); - if (ld->listen_announce[n] != 0) { + (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n], + portnum, ADDR_TYPE_IPV6); + if ((*listen_announce)[n] != 0) { n++; - tal_resize(&ld->wireaddrs, n+1); - tal_resize(&ld->listen_announce, n+1); + tal_resize(wireaddrs, n+1); + tal_resize(listen_announce, n+1); } - ld->listen_announce[n] = guess_one_address(ld, &ld->wireaddrs[n], - ld->portnum, ADDR_TYPE_IPV4); - if (ld->listen_announce[n] == 0) { - tal_resize(&ld->wireaddrs, n); - tal_resize(&ld->listen_announce, n); + (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n], + portnum, ADDR_TYPE_IPV4); + if ((*listen_announce)[n] == 0) { + tal_resize(wireaddrs, n); + tal_resize(listen_announce, n); } } diff --git a/gossipd/netaddress.h b/gossipd/netaddress.h new file mode 100644 index 000000000000..68fd2d689dc1 --- /dev/null +++ b/gossipd/netaddress.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_GOSSIPD_NETADDRESS_H +#define LIGHTNING_GOSSIPD_NETADDRESS_H +#include "config.h" +#include + +void guess_addresses(struct wireaddr **wireaddrs, + enum addr_listen_announce **listen_announce, + u16 portnum); + +#endif /* LIGHTNING_GOSSIPD_NETADDRESS_H */ diff --git a/lightningd/Makefile b/lightningd/Makefile index 0615a70a3539..d925c4c0d9fe 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -66,7 +66,6 @@ LIGHTNINGD_SRC := \ lightningd/lightningd.c \ lightningd/log.c \ lightningd/log_status.c \ - lightningd/netaddress.c \ lightningd/onchain_control.c \ lightningd/opening_control.c \ lightningd/opt_time.c \ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 37ae6393c443..aa96e498527e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -213,17 +213,29 @@ void gossip_init(struct lightningd *ld) } static void gossip_activate_done(struct subd *gossip UNUSED, - const u8 *reply UNUSED, + const u8 *reply, const int *fds UNUSED, void *unused UNUSED) { + struct lightningd *ld = gossip->ld; + + /* Reply gives us the actual wireaddrs we're using */ + tal_free(ld->wireaddrs); + tal_free(ld->listen_announce); + if (!fromwire_gossipctl_activate_reply(gossip->ld, reply, + &ld->wireaddrs, + &ld->listen_announce)) + fatal("Bad gossipctl_activate_reply: %s", + tal_hex(reply, reply)); + /* Break out of loop, so we can begin */ io_break(gossip); } void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, ld->listen); + const u8 *msg = towire_gossipctl_activate(NULL, ld->listen, ld->autolisten, + ld->portnum); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/netaddress.h b/lightningd/netaddress.h deleted file mode 100644 index a2428a9e7487..000000000000 --- a/lightningd/netaddress.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef LIGHTNING_LIGHTNINGD_NETADDRESS_H -#define LIGHTNING_LIGHTNINGD_NETADDRESS_H -#include "config.h" -#include - -struct lightningd; - -void guess_addresses(struct lightningd *ld); - - -#endif /* LIGHTNING_LIGHTNINGD_NETADDRESS_H */ diff --git a/lightningd/options.c b/lightningd/options.c index 6a907ec1f505..f6e27f4dba29 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -809,12 +808,6 @@ void handle_opts(struct lightningd *ld, int argc, char *argv[]) errx(1, "no arguments accepted"); check_config(ld); - - if (ld->listen && ld->autolisten) - guess_addresses(ld); - else - log_debug(ld->log, "Not guessing addresses: %s", - !ld->listen ? "--offline" : "--autolisten=0"); } /* FIXME: This is a hack! Expose somehow in ccan/opt.*/ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3730409dc218..3af37ce3bad7 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include From 864b95d299a897d260e0836c0ba765f76892de67 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:21 +0930 Subject: [PATCH 09/14] wireaddr_internal: new type for where we can also use a local socket. This was something @icota implemented, but it fits logically into this cleanup series. We create a new type which is the internal generalization of a wireaddr (which is defined by the spec), and add a case here for a socket name. Based-on-the-true-story-by: @icota Signed-off-by: Rusty Russell --- common/test/run-ip_port_parsing.c | 15 ++++++ common/type_to_string.h | 1 + common/wireaddr.c | 88 +++++++++++++++++++++++++++++++ common/wireaddr.h | 27 ++++++++++ 4 files changed, 131 insertions(+) diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index fc8eae762270..d99811c161a2 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -8,21 +8,36 @@ /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } /* Generated stub for fromwire_u16 */ u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } /* Generated stub for towire_u16 */ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) { fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(void) diff --git a/common/type_to_string.h b/common/type_to_string.h index bee4cbd677f9..83c485ed9431 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -20,6 +20,7 @@ union printable_types { const struct preimage *preimage; const struct channel_oneside *channel_oneside; const struct wireaddr *wireaddr; + const struct wireaddr_internal *wireaddr_internal; const secp256k1_pubkey *secp256k1_pubkey; const struct channel_id *channel_id; const struct short_channel_id *short_channel_id; diff --git a/common/wireaddr.c b/common/wireaddr.c index f370bafaf6a1..5b454d7f0f78 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include /* Returns false if we didn't parse it, and *cursor == NULL if malformed. */ @@ -53,6 +55,40 @@ void towire_addr_listen_announce(u8 **pptr, enum addr_listen_announce ala) towire_u8(pptr, ala); } +void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr) +{ + towire_u8(pptr, addr->itype); + switch (addr->itype) { + case ADDR_INTERNAL_SOCKNAME: + towire_u8_array(pptr, (const u8 *)addr->u.sockname, + sizeof(addr->u.sockname)); + return; + case ADDR_INTERNAL_WIREADDR: + towire_wireaddr(pptr, &addr->u.wireaddr); + return; + } + abort(); +} + +bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, + struct wireaddr_internal *addr) +{ + addr->itype = fromwire_u8(cursor, max); + switch (addr->itype) { + case ADDR_INTERNAL_SOCKNAME: + fromwire_u8_array(cursor, max, (u8 *)addr->u.sockname, + sizeof(addr->u.sockname)); + /* Must be NUL terminated */ + if (!memchr(addr->u.sockname, 0, sizeof(addr->u.sockname))) + fromwire_fail(cursor, max); + return *cursor != NULL; + case ADDR_INTERNAL_WIREADDR: + return fromwire_wireaddr(cursor, max, &addr->u.wireaddr); + } + fromwire_fail(cursor, max); + return false; +} + char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) { char addrstr[INET6_ADDRSTRLEN]; @@ -122,6 +158,19 @@ bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6) return true; } +char *fmt_wireaddr_internal(const tal_t *ctx, + const struct wireaddr_internal *a) +{ + switch (a->itype) { + case ADDR_INTERNAL_SOCKNAME: + return tal_fmt(ctx, "%s", a->u.sockname); + case ADDR_INTERNAL_WIREADDR: + return fmt_wireaddr(ctx, &a->u.wireaddr); + } + abort(); +} +REGISTER_TYPE_TO_STRING(wireaddr_internal, fmt_wireaddr_internal); + /* Valid forms: * * [anything]: @@ -247,3 +296,42 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, *err_msg = "Error parsing hostname"; return res; } + +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg) +{ + /* Addresses starting with '/' are local socket paths */ + if (arg[0] == '/') { + addr->itype = ADDR_INTERNAL_SOCKNAME; + + /* Check if the path is too long */ + if (strlen(arg) >= sizeof(addr->u.sockname)) { + if (err_msg) + *err_msg = "Socket name too long"; + return false; + } + strcpy(addr->u.sockname, arg); + return true; + } + + addr->itype = ADDR_INTERNAL_WIREADDR; + return parse_wireaddr(arg, &addr->u.wireaddr, port, err_msg); +} + +void wireaddr_from_sockname(struct wireaddr_internal *addr, + const char *sockname) +{ + addr->itype = ADDR_INTERNAL_SOCKNAME; + memset(addr->u.sockname, 0, sizeof(addr->u.sockname)); + strncpy(addr->u.sockname, sockname, sizeof(addr->u.sockname)-1); +} + +bool wireaddr_to_sockname(const struct wireaddr_internal *addr, + struct sockaddr_un *sun) +{ + if (addr->itype != ADDR_INTERNAL_SOCKNAME) + return false; + sun->sun_family = AF_LOCAL; + BUILD_ASSERT(sizeof(sun->sun_path) == sizeof(addr->u.sockname)); + memcpy(sun->sun_path, addr->u.sockname, sizeof(addr->u.sockname)); + return true; +} diff --git a/common/wireaddr.h b/common/wireaddr.h index e639b34823b5..3b202f9aed92 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -10,6 +10,7 @@ struct in6_addr; struct in_addr; struct sockaddr_in6; struct sockaddr_in; +struct sockaddr_un; /* BOLT #7: * @@ -74,4 +75,30 @@ void wireaddr_from_ipv6(struct wireaddr *addr, bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4); bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); +enum wireaddr_internal_type { + ADDR_INTERNAL_SOCKNAME, + ADDR_INTERNAL_WIREADDR, +}; + +/* For internal use, where we can also supply a local socket, wildcard. */ +struct wireaddr_internal { + enum wireaddr_internal_type itype; + union { + struct wireaddr wireaddr; + char sockname[108]; + } u; +}; +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg); + +void towire_wireaddr_internal(u8 **pptr, + const struct wireaddr_internal *addr); +bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, + struct wireaddr_internal *addr); +char *fmt_wireaddr_internal(const tal_t *ctx, + const struct wireaddr_internal *a); + +void wireaddr_from_sockname(struct wireaddr_internal *addr, + const char *sockname); +bool wireaddr_to_sockname(const struct wireaddr_internal *addr, + struct sockaddr_un *sun); #endif /* LIGHTNING_COMMON_WIREADDR_H */ From d2707a47816300f6466fa1cbae52d555326ad179 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:21 +0930 Subject: [PATCH 10/14] gossipd/lightningd: use wireaddr_internal. This replacement is a little menial, but it explicitly catches all the places where we allow a local socket. The actual implementation of opening a AF_UNIX socket is almost hidden in the patch. The detection of "valid address" is now more complex: p->addr.itype != ADDR_INTERNAL_WIREADDR || p->addr.u.wireaddr.type != ADDR_TYPE_PADDING But most places we do this, we should audit: I'm pretty sure we can't get an invalid address any more from gossipd (they may be in db, but we should fix that too). Closes: #1323 Signed-off-by: Rusty Russell --- gossipd/gossip.c | 205 +++++++++++++++++---------- gossipd/gossip_wire.csv | 14 +- gossipd/handshake.c | 16 +-- gossipd/handshake.h | 14 +- gossipd/netaddress.c | 10 +- gossipd/netaddress.h | 3 +- gossipd/test/run-initiator-success.c | 7 +- gossipd/test/run-responder-success.c | 7 +- lightningd/connect_control.c | 13 +- lightningd/gossip_control.c | 2 +- lightningd/json.c | 18 +++ lightningd/json.h | 6 +- lightningd/jsonrpc.c | 8 +- lightningd/lightningd.c | 2 +- lightningd/lightningd.h | 2 +- lightningd/opening_control.c | 10 +- lightningd/opening_control.h | 6 +- lightningd/options.c | 8 +- lightningd/peer_control.c | 26 ++-- lightningd/peer_control.h | 6 +- wallet/test/run-wallet.c | 10 +- wallet/wallet.c | 9 +- 22 files changed, 249 insertions(+), 153 deletions(-) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 1f05943607b5..5e5883485864 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -128,7 +129,7 @@ struct daemon { u8 alias[33]; u8 rgb[3]; - struct wireaddr *wireaddrs; + struct wireaddr_internal *wireaddrs; enum addr_listen_announce *listen_announce; /* To make sure our node_announcement timestamps increase */ @@ -149,7 +150,7 @@ struct reaching { struct pubkey id; /* FIXME: Support multiple address. */ - struct wireaddr addr; + struct wireaddr_internal addr; /* Whether connect command is waiting for the result. */ bool master_needs_response; @@ -192,7 +193,7 @@ struct peer { struct pubkey id; /* Where it's connected to. */ - struct wireaddr addr; + struct wireaddr_internal addr; /* Feature bitmaps. */ u8 *gfeatures, *lfeatures; @@ -214,7 +215,7 @@ struct addrhint { struct pubkey id; /* FIXME: use array... */ - struct wireaddr addr; + struct wireaddr_internal addr; }; /* FIXME: Reorder */ @@ -309,7 +310,7 @@ new_local_peer_state(struct peer *peer, const struct crypto_state *cs) static struct peer *new_peer(const tal_t *ctx, struct daemon *daemon, const struct pubkey *their_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs) { struct peer *peer = tal(ctx, struct peer); @@ -522,7 +523,7 @@ static struct io_plan *read_init(struct io_conn *conn, struct peer *peer) * we have the features. */ static struct io_plan *init_new_peer(struct io_conn *conn, const struct pubkey *their_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, struct daemon *daemon) { @@ -566,8 +567,12 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, memset(sig, 0, sizeof(*sig)); } for (i = 0; i < tal_count(daemon->wireaddrs); i++) { - if (daemon->listen_announce[i] & ADDR_ANNOUNCE) - towire_wireaddr(&addresses, daemon->wireaddrs+i); + if (!(daemon->listen_announce[i] & ADDR_ANNOUNCE)) + continue; + /* You can only announce wiretypes! */ + assert(daemon->wireaddrs[i].itype == ADDR_INTERNAL_WIREADDR); + towire_wireaddr(&addresses, + &daemon->wireaddrs[i].u.wireaddr); } announcement = @@ -1521,7 +1526,7 @@ static void gossip_refresh_network(struct daemon *daemon) static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon) { - struct wireaddr addr; + struct wireaddr_internal addr; struct sockaddr_storage s = {}; socklen_t len = sizeof(s); @@ -1533,10 +1538,18 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon if (s.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (void *)&s; - wireaddr_from_ipv6(&addr, &s6->sin6_addr, ntohs(s6->sin6_port)); + addr.itype = ADDR_INTERNAL_WIREADDR; + wireaddr_from_ipv6(&addr.u.wireaddr, + &s6->sin6_addr, ntohs(s6->sin6_port)); } else if (s.ss_family == AF_INET) { struct sockaddr_in *s4 = (void *)&s; - wireaddr_from_ipv4(&addr, &s4->sin_addr, ntohs(s4->sin_port)); + addr.itype = ADDR_INTERNAL_WIREADDR; + wireaddr_from_ipv4(&addr.u.wireaddr, + &s4->sin_addr, ntohs(s4->sin_port)); + } else if (s.ss_family == AF_UNIX) { + struct sockaddr_un *sun = (void *)&s; + addr.itype = ADDR_INTERNAL_SOCKNAME; + memcpy(addr.u.sockname, sun->sun_path, sizeof(sun->sun_path)); } else { status_broken("Unknown socket type %i for incoming conn", s.ss_family); @@ -1548,50 +1561,78 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon init_new_peer, daemon); } -static void setup_listeners(struct daemon *daemon) +/* Returns true if it was an IPv6 wildcard (as inserted by guess_addresses) */ +static bool handle_wireaddr_listen(struct daemon *daemon, + const struct wireaddr *wireaddr, + bool had_ipv6_wildcard) { - bool had_ipv6_wildcard = false; + int fd; struct sockaddr_in addr; struct sockaddr_in6 addr6; + + switch (wireaddr->type) { + case ADDR_TYPE_IPV4: + wireaddr_to_ipv4(wireaddr, &addr); + /* We might fail if IPv6 bound to port first */ + fd = make_listen_fd(AF_INET, &addr, sizeof(addr), + !had_ipv6_wildcard); + if (fd >= 0) { + status_trace("Created IPv4 listener on port %u", + wireaddr->port); + io_new_listener(daemon, fd, connection_in, daemon); + } + return false; + case ADDR_TYPE_IPV6: + wireaddr_to_ipv6(wireaddr, &addr6); + if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) + had_ipv6_wildcard = true; + else + had_ipv6_wildcard = false; + fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), true); + if (fd >= 0) { + status_trace("Created IPv6 listener on port %u", + wireaddr->port); + io_new_listener(daemon, fd, connection_in, daemon); + } + return had_ipv6_wildcard; + case ADDR_TYPE_PADDING: + break; + } + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Invalid listener wireaddress type %u", wireaddr->type); +} + +static void setup_listeners(struct daemon *daemon) +{ + bool had_ipv6_wildcard = false; + struct sockaddr_un addrun; int fd; for (size_t i = 0; i < tal_count(daemon->wireaddrs); i++) { if (!(daemon->listen_announce[i] & ADDR_LISTEN)) continue; - switch (daemon->wireaddrs[i].type) { - case ADDR_TYPE_IPV4: - wireaddr_to_ipv4(&daemon->wireaddrs[i], &addr); - /* We might fail if IPv6 bound to port first */ - fd = make_listen_fd(AF_INET, &addr, sizeof(addr), - !had_ipv6_wildcard); - if (fd >= 0) { - status_trace("Created IPv4 listener on port %u", - daemon->wireaddrs[i].port); - io_new_listener(daemon, fd, connection_in, - daemon); - } + switch (daemon->wireaddrs[i].itype) { + case ADDR_INTERNAL_SOCKNAME: + addrun.sun_family = AF_UNIX; + memcpy(addrun.sun_path, daemon->wireaddrs[i].u.sockname, + sizeof(addrun.sun_path)); + fd = make_listen_fd(AF_INET, &addrun, sizeof(addrun), + false); + status_trace("Created socket listener on file %s", + addrun.sun_path); + io_new_listener(daemon, fd, connection_in, daemon); continue; - case ADDR_TYPE_IPV6: - wireaddr_to_ipv6(&daemon->wireaddrs[i], &addr6); - if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) - had_ipv6_wildcard = true; - fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), - true); - if (fd >= 0) { - status_trace("Created IPv6 listener on port %u", - daemon->wireaddrs[i].port); - io_new_listener(daemon, fd, connection_in, - daemon); - } + case ADDR_INTERNAL_WIREADDR: + had_ipv6_wildcard = handle_wireaddr_listen( + daemon, &daemon->wireaddrs[i].u.wireaddr, + had_ipv6_wildcard); continue; - case ADDR_TYPE_PADDING: - break; } /* Shouldn't happen. */ status_failed(STATUS_FAIL_INTERNAL_ERROR, "Invalid listener address type %u", - daemon->wireaddrs[i].type); + daemon->wireaddrs[i].itype); } } @@ -1686,7 +1727,7 @@ static struct io_plan *resolve_channel_req(struct io_conn *conn, static struct io_plan *handshake_out_success(struct io_conn *conn, const struct pubkey *id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, struct reaching *reach) { @@ -1751,6 +1792,7 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) struct addrinfo ai; struct sockaddr_in sin; struct sockaddr_in6 sin6; + struct sockaddr_un sun; /* FIXME: make generic */ ai.ai_flags = 0; @@ -1759,24 +1801,32 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) ai.ai_canonname = NULL; ai.ai_next = NULL; - switch (reach->addr.type) { - case ADDR_TYPE_IPV4: - wireaddr_to_ipv4(&reach->addr, &sin); - ai.ai_family = sin.sin_family; + switch (reach->addr.itype) { + case ADDR_INTERNAL_SOCKNAME: + wireaddr_to_sockname(&reach->addr, &sun); + ai.ai_family = sun.sun_family; ai.ai_addrlen = sizeof(sin); - ai.ai_addr = (struct sockaddr *)&sin; - break; - case ADDR_TYPE_IPV6: - wireaddr_to_ipv6(&reach->addr, &sin6); - ai.ai_family = sin6.sin6_family; - ai.ai_addrlen = sizeof(sin6); - ai.ai_addr = (struct sockaddr *)&sin6; + ai.ai_addr = (struct sockaddr *)&sun; break; - case ADDR_TYPE_PADDING: - /* Shouldn't happen. */ - return io_close(conn); + case ADDR_INTERNAL_WIREADDR: + switch (reach->addr.u.wireaddr.type) { + case ADDR_TYPE_IPV4: + wireaddr_to_ipv4(&reach->addr.u.wireaddr, &sin); + ai.ai_family = sin.sin_family; + ai.ai_addrlen = sizeof(sin); + ai.ai_addr = (struct sockaddr *)&sin; + break; + case ADDR_TYPE_IPV6: + wireaddr_to_ipv6(&reach->addr.u.wireaddr, &sin6); + ai.ai_family = sin6.sin6_family; + ai.ai_addrlen = sizeof(sin6); + ai.ai_addr = (struct sockaddr *)&sin6; + break; + case ADDR_TYPE_PADDING: + /* Shouldn't happen. */ + return io_close(conn); + } } - io_set_finish(conn, connect_failed, reach); return io_connect(conn, &ai, connection_out, reach); } @@ -1797,12 +1847,14 @@ seed_resolve_addr(const tal_t *ctx, const struct pubkey *id, const u16 port) status_trace("Resolving %s", addr); a = tal(ctx, struct addrhint); - if (!wireaddr_from_hostname(&a->addr, addr, port, NULL)) { + a->addr.itype = ADDR_INTERNAL_WIREADDR; + if (!wireaddr_from_hostname(&a->addr.u.wireaddr, addr, port, NULL)) { status_trace("Could not resolve %s", addr); return tal_free(a); } else { status_trace("Resolved %s to %s", addr, - type_to_string(ctx, struct wireaddr, &a->addr)); + type_to_string(ctx, struct wireaddr, + &a->addr.u.wireaddr)); return a; } } @@ -1830,7 +1882,8 @@ gossip_resolve_addr(const tal_t *ctx, * we should copy all addresses. * For now getting first address should be fine. */ a = tal(ctx, struct addrhint); - a->addr = node->addresses[0]; + a->addr.itype = ADDR_INTERNAL_WIREADDR; + a->addr.u.wireaddr = node->addresses[0]; return a; } @@ -1839,7 +1892,7 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, bool master_needs_response) { struct addrhint *a; - int fd; + int fd, af; struct reaching *reach; u8 *msg; struct peer *peer = find_peer(daemon, id); @@ -1892,23 +1945,33 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, } /* Might not even be able to create eg. IPv6 sockets */ - switch (a->addr.type) { - case ADDR_TYPE_IPV4: - fd = socket(AF_INET, SOCK_STREAM, 0); + af = -1; + switch (a->addr.itype) { + case ADDR_INTERNAL_SOCKNAME: + af = AF_LOCAL; break; - case ADDR_TYPE_IPV6: - fd = socket(AF_INET6, SOCK_STREAM, 0); - break; - default: + case ADDR_INTERNAL_WIREADDR: + switch (a->addr.u.wireaddr.type) { + case ADDR_TYPE_IPV4: + af = AF_INET; + break; + case ADDR_TYPE_IPV6: + af = AF_INET6; + break; + case ADDR_TYPE_PADDING: + break; + } + } + if (af == -1) { fd = -1; errno = EPROTONOSUPPORT; - break; - } + } else + fd = socket(af, SOCK_STREAM, 0); if (fd < 0) { char *err = tal_fmt(tmpctx, "Can't open %i socket for %s (%s), giving up", - a->addr.type, + af, type_to_string(tmpctx, struct pubkey, id), strerror(errno)); status_debug("%s", err); @@ -2051,7 +2114,7 @@ static struct io_plan *get_peers(struct io_conn *conn, struct peer *peer; size_t n = 0; struct pubkey *id = tal_arr(conn, struct pubkey, n); - struct wireaddr *wireaddr = tal_arr(conn, struct wireaddr, n); + struct wireaddr_internal *wireaddr = tal_arr(conn, struct wireaddr_internal, n); const struct gossip_getnodes_entry **nodes; struct pubkey *specific_id = NULL; struct node_map_iter it; diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 8f781d3fd6d1..c87a8a2e745d 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -12,7 +12,7 @@ gossipctl_init,,gfeatures,gflen*u8 gossipctl_init,,lflen,u16 gossipctl_init,,lfeatures,lflen*u8 gossipctl_init,,num_wireaddrs,u16 -gossipctl_init,,wireaddrs,num_wireaddrs*struct wireaddr +gossipctl_init,,wireaddrs,num_wireaddrs*struct wireaddr_internal gossipctl_init,,listen_announce,num_wireaddrs*enum addr_listen_announce gossipctl_init,,rgb,3*u8 gossipctl_init,,alias,32*u8 @@ -30,13 +30,13 @@ gossipctl_activate,,port,u16 # Gossipd->master, I am ready, here are the final addresses. gossipctl_activate_reply,3125 gossipctl_activate_reply,,num_wireaddrs,u16 -gossipctl_activate_reply,,wireaddrs,num_wireaddrs*struct wireaddr +gossipctl_activate_reply,,wireaddrs,num_wireaddrs*struct wireaddr_internal gossipctl_activate_reply,,listen_announce,num_wireaddrs*enum addr_listen_announce # Master -> gossipd: Optional hint for where to find peer. gossipctl_peer_addrhint,3014 gossipctl_peer_addrhint,,id,struct pubkey -gossipctl_peer_addrhint,,addr,struct wireaddr +gossipctl_peer_addrhint,,addr,struct wireaddr_internal # Master -> gossipd: connect to a peer. gossipctl_connect_to_peer,3001 @@ -59,7 +59,7 @@ gossipctl_peer_important,,important,bool # Gossipd -> master: we got a peer. Two fds: peer and gossip gossip_peer_connected,3002 gossip_peer_connected,,id,struct pubkey -gossip_peer_connected,,addr,struct wireaddr +gossip_peer_connected,,addr,struct wireaddr_internal gossip_peer_connected,,crypto_state,struct crypto_state gossip_peer_connected,,gflen,u16 gossip_peer_connected,,gfeatures,gflen*u8 @@ -69,7 +69,7 @@ gossip_peer_connected,,lfeatures,lflen*u8 # Gossipd -> master: peer sent non-gossip packet. Two fds: peer and gossip gossip_peer_nongossip,3003 gossip_peer_nongossip,,id,struct pubkey -gossip_peer_nongossip,,addr,struct wireaddr +gossip_peer_nongossip,,addr,struct wireaddr_internal gossip_peer_nongossip,,crypto_state,struct crypto_state gossip_peer_nongossip,,gflen,u16 gossip_peer_nongossip,,gfeatures,gflen*u8 @@ -84,7 +84,7 @@ gossipctl_release_peer,,id,struct pubkey # Gossipd -> master: reply to gossip_release_peer. Two fds: peer and gossip. gossipctl_release_peer_reply,3104 -gossipctl_release_peer_reply,,addr,struct wireaddr +gossipctl_release_peer_reply,,addr,struct wireaddr_internal gossipctl_release_peer_reply,,crypto_state,struct crypto_state gossipctl_release_peer_reply,,gflen,u16 gossipctl_release_peer_reply,,gfeatures,gflen*u8 @@ -168,7 +168,7 @@ gossip_getpeers_request,,id,num*struct pubkey gossip_getpeers_reply,3111 gossip_getpeers_reply,,num,u16 gossip_getpeers_reply,,id,num*struct pubkey -gossip_getpeers_reply,,addr,num*struct wireaddr +gossip_getpeers_reply,,addr,num*struct wireaddr_internal gossip_getpeers_reply,,numnodes,u16 gossip_getpeers_reply,,nodes,numnodes*struct gossip_getnodes_entry diff --git a/gossipd/handshake.c b/gossipd/handshake.c index 26dbf1c9cd40..5c9e893c3dc3 100644 --- a/gossipd/handshake.c +++ b/gossipd/handshake.c @@ -170,7 +170,7 @@ struct handshake { struct act_three act3; /* Where is connection from/to */ - struct wireaddr addr; + struct wireaddr_internal addr; /* Who we are */ struct pubkey my_id; @@ -183,7 +183,7 @@ struct handshake { /* Function to call once handshake complete. */ struct io_plan *(*cb)(struct io_conn *conn, const struct pubkey *their_id, - const struct wireaddr *wireaddr, + const struct wireaddr_internal *wireaddr, const struct crypto_state *cs, void *cbarg); void *cbarg; @@ -353,12 +353,12 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn, struct crypto_state cs; struct io_plan *(*cb)(struct io_conn *conn, const struct pubkey *their_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, void *cbarg); void *cbarg; struct pubkey their_id; - struct wireaddr addr; + struct wireaddr_internal addr; /* BOLT #8: * @@ -965,10 +965,10 @@ static struct io_plan *act_one_responder(struct io_conn *conn, struct io_plan *responder_handshake_(struct io_conn *conn, const struct pubkey *my_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, - const struct wireaddr *, + const struct wireaddr_internal *, const struct crypto_state *, void *cbarg), void *cbarg) @@ -987,10 +987,10 @@ struct io_plan *responder_handshake_(struct io_conn *conn, struct io_plan *initiator_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct pubkey *their_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, - const struct wireaddr *, + const struct wireaddr_internal *, const struct crypto_state *, void *cbarg), void *cbarg) diff --git a/gossipd/handshake.h b/gossipd/handshake.h index fea357fccdc4..6bc07db3275b 100644 --- a/gossipd/handshake.h +++ b/gossipd/handshake.h @@ -5,7 +5,7 @@ struct crypto_state; struct io_conn; -struct wireaddr; +struct wireaddr_internal; struct pubkey; #define initiator_handshake(conn, my_id, their_id, addr, cb, cbarg) \ @@ -14,7 +14,7 @@ struct pubkey; (cb), (cbarg), \ struct io_conn *, \ const struct pubkey *, \ - const struct wireaddr *, \ + const struct wireaddr_internal *, \ const struct crypto_state *), \ (cbarg)) @@ -22,10 +22,10 @@ struct pubkey; struct io_plan *initiator_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct pubkey *their_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, - const struct wireaddr *, + const struct wireaddr_internal *, const struct crypto_state *, void *cbarg), void *cbarg); @@ -37,16 +37,16 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, (cb), (cbarg), \ struct io_conn *, \ const struct pubkey *, \ - const struct wireaddr *, \ + const struct wireaddr_internal *, \ const struct crypto_state *), \ (cbarg)) struct io_plan *responder_handshake_(struct io_conn *conn, const struct pubkey *my_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, - const struct wireaddr *, + const struct wireaddr_internal *, const struct crypto_state *, void *cbarg), void *cbarg); diff --git a/gossipd/netaddress.c b/gossipd/netaddress.c index 7d23ed0127e6..a7939e970a61 100644 --- a/gossipd/netaddress.c +++ b/gossipd/netaddress.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -294,7 +293,7 @@ static enum addr_listen_announce guess_one_address(struct wireaddr *addr, return ret; } -void guess_addresses(struct wireaddr **wireaddrs, +void guess_addresses(struct wireaddr_internal **wireaddrs, enum addr_listen_announce **listen_announce, u16 portnum) { @@ -305,15 +304,18 @@ void guess_addresses(struct wireaddr **wireaddrs, /* We allocate an extra, then remove if it's not needed. */ tal_resize(wireaddrs, n+1); tal_resize(listen_announce, n+1); + + (*wireaddrs)[n].itype = ADDR_INTERNAL_WIREADDR; /* We do IPv6 first: on Linux, that binds to IPv4 too. */ - (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n], + (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n].u.wireaddr, portnum, ADDR_TYPE_IPV6); if ((*listen_announce)[n] != 0) { n++; tal_resize(wireaddrs, n+1); tal_resize(listen_announce, n+1); + (*wireaddrs)[n].itype = ADDR_INTERNAL_WIREADDR; } - (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n], + (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n].u.wireaddr, portnum, ADDR_TYPE_IPV4); if ((*listen_announce)[n] == 0) { tal_resize(wireaddrs, n); diff --git a/gossipd/netaddress.h b/gossipd/netaddress.h index 68fd2d689dc1..58a3fa25ddc4 100644 --- a/gossipd/netaddress.h +++ b/gossipd/netaddress.h @@ -2,8 +2,9 @@ #define LIGHTNING_GOSSIPD_NETADDRESS_H #include "config.h" #include +#include -void guess_addresses(struct wireaddr **wireaddrs, +void guess_addresses(struct wireaddr_internal **wireaddrs, enum addr_listen_announce **listen_announce, u16 portnum); diff --git a/gossipd/test/run-initiator-success.c b/gossipd/test/run-initiator-success.c index 3932deeacad0..2efeb4e7d753 100644 --- a/gossipd/test/run-initiator-success.c +++ b/gossipd/test/run-initiator-success.c @@ -176,7 +176,7 @@ static struct io_plan *test_read(struct io_conn *conn, static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them, - const struct wireaddr *addr UNUSED, + const struct wireaddr_internal *addr UNUSED, const struct crypto_state *cs, void *unused UNUSED) { @@ -201,7 +201,7 @@ int main(void) { setup_locale(); - struct wireaddr dummy; + struct wireaddr_internal dummy; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -223,7 +223,8 @@ int main(void) e_priv = privkey("1212121212121212121212121212121212121212121212121212121212121212"); e_pub = pubkey("036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7"); - dummy.addrlen = 0; + dummy.itype = ADDR_INTERNAL_WIREADDR; + dummy.u.wireaddr.addrlen = 0; initiator_handshake((void *)tmpctx, &ls_pub, &rs_pub, &dummy, success, NULL); /* Should not exit! */ abort(); diff --git a/gossipd/test/run-responder-success.c b/gossipd/test/run-responder-success.c index a65c4c96f159..2130edc1187f 100644 --- a/gossipd/test/run-responder-success.c +++ b/gossipd/test/run-responder-success.c @@ -175,7 +175,7 @@ static struct io_plan *test_read(struct io_conn *conn, static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them UNUSED, - const struct wireaddr *addr UNUSED, + const struct wireaddr_internal *addr UNUSED, const struct crypto_state *cs, void *unused UNUSED) { @@ -198,7 +198,7 @@ int main(void) { setup_locale(); - struct wireaddr dummy; + struct wireaddr_internal dummy; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -218,7 +218,8 @@ int main(void) e_priv = privkey("2222222222222222222222222222222222222222222222222222222222222222"); e_pub = pubkey("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27"); - dummy.addrlen = 0; + dummy.itype = ADDR_INTERNAL_WIREADDR; + dummy.u.wireaddr.addrlen = 0; responder_handshake((void *)tmpctx, &ls_pub, &dummy, success, NULL); /* Should not exit! */ abort(); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 3fa915e3ccd4..05354922d27e 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -85,7 +85,7 @@ static void json_connect(struct command *cmd, char *atptr; char *ataddr = NULL; const char *name; - struct wireaddr addr; + struct wireaddr_internal addr; u8 *msg; const char *err_msg; @@ -139,22 +139,21 @@ static void json_connect(struct command *cmd, /* Was there parseable host name? */ if (name) { + u32 port; /* Is there a port? */ if (porttok) { - u32 port; - if (!json_tok_number(buffer, porttok, &port)) { + if (!json_tok_number(buffer, porttok, &port) || !port) { command_fail(cmd, "Port %.*s not valid", porttok->end - porttok->start, buffer + porttok->start); return; } - addr.port = port; } else { - addr.port = DEFAULT_PORT; + port = DEFAULT_PORT; } - if (!parse_wireaddr(name, &addr, addr.port, &err_msg) || !addr.port) { + if (!parse_wireaddr_internal(name, &addr, port, &err_msg)) { command_fail(cmd, "Host %s:%u not valid: %s", - name, addr.port, err_msg ? err_msg : "port is 0"); + name, port, err_msg ? err_msg : "port is 0"); return; } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index aa96e498527e..8010622dc83e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -35,7 +35,7 @@ static void peer_nongossip(struct subd *gossip, const u8 *msg, { struct pubkey id; struct crypto_state cs; - struct wireaddr addr; + struct wireaddr_internal addr; u8 *gfeatures, *lfeatures, *in_pkt; if (!fromwire_gossip_peer_nongossip(msg, msg, diff --git a/lightningd/json.c b/lightningd/json.c index 05ab73bb3fc3..35b5c3c03b47 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -1,6 +1,7 @@ #include "json.h" #include #include +#include #include #include #include @@ -142,3 +143,20 @@ void json_add_address(struct json_result *response, const char *fieldname, json_object_end(response); } +void json_add_address_internal(struct json_result *response, + const char *fieldname, + const struct wireaddr_internal *addr) +{ + switch (addr->itype) { + case ADDR_INTERNAL_SOCKNAME: + json_object_start(response, fieldname); + json_add_string(response, "type", "local socket"); + json_add_string(response, "socket", addr->u.sockname); + json_object_end(response); + return; + case ADDR_INTERNAL_WIREADDR: + json_add_address(response, fieldname, &addr->u.wireaddr); + return; + } + abort(); +} diff --git a/lightningd/json.h b/lightningd/json.h index 2e4230d3b4bb..29b629884a82 100644 --- a/lightningd/json.h +++ b/lightningd/json.h @@ -19,6 +19,7 @@ struct route_hop; struct short_channel_id; struct wallet_payment; struct wireaddr; +struct wireaddr_internal; /* Output a route array. */ void json_add_route(struct json_result *r, char const *n, @@ -58,5 +59,8 @@ bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, void json_add_address(struct json_result *response, const char *fieldname, const struct wireaddr *addr); - +/* JSON serialize a network address for a node. */ +void json_add_address_internal(struct json_result *response, + const char *fieldname, + const struct wireaddr_internal *addr); #endif /* LIGHTNING_LIGHTNINGD_JSON_H */ diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 7ed0709cf570..660de3fc8111 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -158,8 +158,8 @@ static void json_getinfo(struct command *cmd, json_array_start(response, "address"); for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { if (cmd->ld->listen_announce[i] & ADDR_ANNOUNCE) - json_add_address(response, NULL, - cmd->ld->wireaddrs+i); + json_add_address_internal(response, NULL, + cmd->ld->wireaddrs+i); else have_listen_no_announce = true; } @@ -169,8 +169,8 @@ static void json_getinfo(struct command *cmd, json_array_start(response, "listen-only"); for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { if (cmd->ld->listen_announce[i] == ADDR_LISTEN) - json_add_address(response, NULL, - cmd->ld->wireaddrs+i); + json_add_address_internal(response, NULL, + cmd->ld->wireaddrs+i); } json_array_end(response); } diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index fefc5c97609e..15b94251ba66 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -69,7 +69,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->waitsendpay_commands); list_head_init(&ld->sendpay_commands); list_head_init(&ld->close_commands); - ld->wireaddrs = tal_arr(ld, struct wireaddr, 0); + ld->wireaddrs = tal_arr(ld, struct wireaddr_internal, 0); ld->listen_announce = tal_arr(ld, enum addr_listen_announce, 0); ld->portnum = DEFAULT_PORT; ld->listen = true; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index e60f25e82343..bab518a4e896 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -121,7 +121,7 @@ struct lightningd { bool autolisten; /* Addresses to bind/announce to the network (tal_count()) */ - struct wireaddr *wireaddrs; + struct wireaddr_internal *wireaddrs; /* And the bitset for each, whether to listen, announce or both */ enum addr_listen_announce *listen_announce; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a6030f118662..77f101fd5244 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -586,7 +586,7 @@ static struct uncommitted_channel * new_uncommitted_channel(struct lightningd *ld, struct funding_channel *fc, const struct pubkey *peer_id, - const struct wireaddr *addr) + const struct wireaddr_internal *addr) { struct uncommitted_channel *uc = tal(ld, struct uncommitted_channel); char *idname; @@ -671,7 +671,7 @@ static void channel_config(struct lightningd *ld, u8 *peer_accept_channel(const tal_t *ctx, struct lightningd *ld, const struct pubkey *peer_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures UNUSED, const u8 *lfeatures UNUSED, int peer_fd, int gossip_fd, @@ -745,7 +745,7 @@ u8 *peer_accept_channel(const tal_t *ctx, static void peer_offer_channel(struct lightningd *ld, struct funding_channel *fc, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures UNUSED, const u8 *lfeatures UNUSED, int peer_fd, int gossip_fd) @@ -826,7 +826,7 @@ static void gossip_peer_released(struct subd *gossip, struct lightningd *ld = gossip->ld; struct crypto_state cs; u8 *gfeatures, *lfeatures; - struct wireaddr addr; + struct wireaddr_internal addr; struct channel *c; struct uncommitted_channel *uc; @@ -869,7 +869,7 @@ static void gossip_peer_released(struct subd *gossip, * released. */ bool handle_opening_channel(struct lightningd *ld, const struct pubkey *id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures, const u8 *lfeatures, int peer_fd, int gossip_fd) diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index bbd2781af30e..eca131decc8a 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -9,7 +9,7 @@ struct json_result; struct lightningd; struct pubkey; struct uncommitted_channel; -struct wireaddr; +struct wireaddr_internal; void json_add_uncommitted_channel(struct json_result *response, const struct uncommitted_channel *uc); @@ -21,7 +21,7 @@ void json_add_uncommitted_channel(struct json_result *response, u8 *peer_accept_channel(const tal_t *ctx, struct lightningd *ld, const struct pubkey *peer_id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures, const u8 *lfeatures, int peer_fd, int gossip_fd, @@ -32,7 +32,7 @@ u8 *peer_accept_channel(const tal_t *ctx, * so we could open a channel? Returns true if it took over. */ bool handle_opening_channel(struct lightningd *ld, const struct pubkey *id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures, const u8 *lfeatures, int peer_fd, int gossip_fd); diff --git a/lightningd/options.c b/lightningd/options.c index f6e27f4dba29..7c32c9dfdf59 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -158,8 +158,8 @@ static char *opt_add_addr_withtype(const char *arg, tal_resize(&ld->listen_announce, n+1); ld->listen_announce[n] = ala; - if (!parse_wireaddr(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { - return tal_fmt(NULL, "Unable to parse IP address '%s': %s", arg, err_msg); + if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { + return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } return NULL; @@ -828,7 +828,7 @@ static const char *next_name(const char *names, unsigned *len) static void json_add_opt_addrs(struct json_result *response, const char *name0, - const struct wireaddr *wireaddrs, + const struct wireaddr_internal *wireaddrs, const enum addr_listen_announce *listen_announce, enum addr_listen_announce ala) { @@ -837,7 +837,7 @@ static void json_add_opt_addrs(struct json_result *response, continue; json_add_string(response, name0, - fmt_wireaddr(name0, wireaddrs+i)); + fmt_wireaddr_internal(name0, wireaddrs+i)); } } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3af37ce3bad7..77b1ad0870b2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -77,7 +77,7 @@ static void copy_to_parent_log(const char *prefix, struct peer *new_peer(struct lightningd *ld, u64 dbid, const struct pubkey *id, - const struct wireaddr *addr) + const struct wireaddr_internal *addr) { /* We are owned by our channels, and freed manually by destroy_channel */ struct peer *peer = tal(NULL, struct peer); @@ -86,10 +86,13 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->dbid = dbid; peer->id = *id; peer->uncommitted_channel = NULL; + /* FIXME: This is always set, right? */ if (addr) peer->addr = *addr; - else - peer->addr.type = ADDR_TYPE_PADDING; + else { + peer->addr.itype = ADDR_INTERNAL_WIREADDR; + peer->addr.u.wireaddr.type = ADDR_TYPE_PADDING; + } list_head_init(&peer->channels); peer->direction = get_channel_direction(&peer->ld->id, &peer->id); @@ -423,7 +426,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, u8 *global_features; u8 *local_features; struct channel *channel; - struct wireaddr addr; + struct wireaddr_internal addr; struct uncommitted_channel *uc; if (!fromwire_gossip_peer_connected(msg, msg, @@ -543,7 +546,7 @@ static struct channel *channel_by_channel_id(struct peer *peer, /* We only get here IF we weren't trying to connect to it. */ void peer_sent_nongossip(struct lightningd *ld, const struct pubkey *id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures, const u8 *lfeatures, @@ -745,7 +748,7 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, { /* This is a little sneaky... */ struct pubkey *ids; - struct wireaddr *addrs; + struct wireaddr_internal *addrs; struct gossip_getnodes_entry **nodes; struct json_result *response = new_json_result(gpa->cmd); struct peer *p; @@ -780,10 +783,11 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, if (connected) { json_array_start(response, "netaddr"); - if (p->addr.type != ADDR_TYPE_PADDING) + if (p->addr.itype != ADDR_INTERNAL_WIREADDR + || p->addr.u.wireaddr.type != ADDR_TYPE_PADDING) json_add_string(response, NULL, type_to_string(response, - struct wireaddr, + struct wireaddr_internal, &p->addr)); json_array_end(response); } @@ -921,9 +925,11 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg, json_add_pubkey(response, "id", ids+i); json_add_node_decoration(response, nodes, ids+i); json_array_start(response, "netaddr"); - if (addrs[i].type != ADDR_TYPE_PADDING) + if (addrs[i].itype != ADDR_INTERNAL_WIREADDR + || addrs[i].u.wireaddr.type != ADDR_TYPE_PADDING) json_add_string(response, NULL, - type_to_string(response, struct wireaddr, + type_to_string(response, + struct wireaddr_internal, addrs + i)); json_array_end(response); json_add_bool(response, "connected", true); diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index eb56bcb64440..c87bed3f9754 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -41,7 +41,7 @@ struct peer { struct log_book *log_book; /* Where we connected to, or it connected from. */ - struct wireaddr addr; + struct wireaddr_internal addr; /* If we open a channel our direction will be this */ u8 direction; @@ -56,7 +56,7 @@ struct peer *find_peer_by_dbid(struct lightningd *ld, u64 dbid); struct peer *new_peer(struct lightningd *ld, u64 dbid, const struct pubkey *id, - const struct wireaddr *addr); + const struct wireaddr_internal *addr); /* Also removes from db. */ void delete_peer(struct peer *peer); @@ -78,7 +78,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, void peer_sent_nongossip(struct lightningd *ld, const struct pubkey *id, - const struct wireaddr *addr, + const struct wireaddr_internal *addr, const struct crypto_state *cs, const u8 *gfeatures, const u8 *lfeatures, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 34856bb73c74..1ed45842f10b 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -81,10 +81,10 @@ bool fromwire_gossipctl_peer_disconnect_reply(const void *p UNNEEDED) bool fromwire_gossipctl_peer_disconnect_replyfail(const void *p UNNEEDED, bool *isconnected UNNEEDED) { fprintf(stderr, "fromwire_gossipctl_peer_disconnect_replyfail called!\n"); abort(); } /* Generated stub for fromwire_gossip_getpeers_reply */ -bool fromwire_gossip_getpeers_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey **id UNNEEDED, struct wireaddr **addr UNNEEDED, struct gossip_getnodes_entry ***nodes UNNEEDED) +bool fromwire_gossip_getpeers_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey **id UNNEEDED, struct wireaddr_internal **addr UNNEEDED, struct gossip_getnodes_entry ***nodes UNNEEDED) { fprintf(stderr, "fromwire_gossip_getpeers_reply called!\n"); abort(); } /* Generated stub for fromwire_gossip_peer_connected */ -bool fromwire_gossip_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey *id UNNEEDED, struct wireaddr *addr UNNEEDED, struct crypto_state *crypto_state UNNEEDED, u8 **gfeatures UNNEEDED, u8 **lfeatures UNNEEDED) +bool fromwire_gossip_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct crypto_state *crypto_state UNNEEDED, u8 **gfeatures UNNEEDED, u8 **lfeatures UNNEEDED) { fprintf(stderr, "fromwire_gossip_peer_connected called!\n"); abort(); } /* Generated stub for get_feerate */ u32 get_feerate(const struct chain_topology *topo UNNEEDED, enum feerate feerate UNNEEDED) @@ -98,7 +98,7 @@ u8 *get_offered_local_features(const tal_t *ctx UNNEEDED) /* Generated stub for handle_opening_channel */ bool handle_opening_channel(struct lightningd *ld UNNEEDED, const struct pubkey *id UNNEEDED, - const struct wireaddr *addr UNNEEDED, + const struct wireaddr_internal *addr UNNEEDED, const struct crypto_state *cs UNNEEDED, const u8 *gfeatures UNNEEDED, const u8 *lfeatures UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED) @@ -318,7 +318,7 @@ void outpointfilter_remove(struct outpointfilter *of UNNEEDED, u8 *peer_accept_channel(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED, const struct pubkey *peer_id UNNEEDED, - const struct wireaddr *addr UNNEEDED, + const struct wireaddr_internal *addr UNNEEDED, const struct crypto_state *cs UNNEEDED, const u8 *gfeatures UNNEEDED, const u8 *lfeatures UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED, @@ -378,7 +378,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, u8 *towire_gossipctl_hand_back_peer(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, const struct crypto_state *crypto_state UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_gossipctl_hand_back_peer called!\n"); abort(); } /* Generated stub for towire_gossipctl_peer_addrhint */ -u8 *towire_gossipctl_peer_addrhint(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, const struct wireaddr *addr UNNEEDED) +u8 *towire_gossipctl_peer_addrhint(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "towire_gossipctl_peer_addrhint called!\n"); abort(); } /* Generated stub for towire_gossipctl_peer_disconnect */ u8 *towire_gossipctl_peer_disconnect(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED) diff --git a/wallet/wallet.c b/wallet/wallet.c index e288465c2e29..548fe3f24659 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -493,7 +493,7 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) const unsigned char *addrstr; struct peer *peer; struct pubkey id; - struct wireaddr *addrp, addr; + struct wireaddr_internal *addrp, addr; sqlite3_stmt *stmt = db_query(w->db, @@ -510,7 +510,7 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) addrstr = sqlite3_column_text(stmt, 2); if (addrstr) { addrp = &addr; - if (!parse_wireaddr((const char*)addrstr, addrp, DEFAULT_PORT, NULL)) { + if (!parse_wireaddr_internal((const char*)addrstr, addrp, DEFAULT_PORT, NULL)) { db_stmt_done(stmt); return NULL; } @@ -970,11 +970,12 @@ void wallet_channel_insert(struct wallet *w, struct channel *chan) /* Need to create the peer first */ stmt = db_prepare(w->db, "INSERT INTO peers (node_id, address) VALUES (?, ?);"); sqlite3_bind_pubkey(stmt, 1, &chan->peer->id); - if (chan->peer->addr.type == ADDR_TYPE_PADDING) + if (chan->peer->addr.itype == ADDR_INTERNAL_WIREADDR + && chan->peer->addr.u.wireaddr.type == ADDR_TYPE_PADDING) sqlite3_bind_null(stmt, 2); else sqlite3_bind_text(stmt, 2, - type_to_string(tmpctx, struct wireaddr, &chan->peer->addr), + type_to_string(tmpctx, struct wireaddr_internal, &chan->peer->addr), -1, SQLITE_TRANSIENT); db_exec_prepared(w->db, stmt); chan->peer->dbid = sqlite3_last_insert_rowid(w->db->sql); From 4c297eb4648e34198c03f636bc43055d665929c3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:22 +0930 Subject: [PATCH 11/14] More flexible address wildcards, only add wildcard if nothing else. 1. Add special option where an empty host means 'wildcard for IPv4 and/or IPv6' which means ':1234' can be used to set only the portnum. 2. Only add this protocol wildcard if --autolisten=1 (default) and no other addresses specified. 3. Pass it down to gossipd, so it can handle errors correctly: in most cases, it's fatal not to be able to bind to a port, but for this case, it's OK if we can only bind to one of IPv4/v6 (fatal iff neither). Signed-off-by: Rusty Russell --- common/wireaddr.c | 24 +++++++++++- common/wireaddr.h | 7 +++- gossipd/gossip.c | 76 ++++++++++++++++++++++-------------- gossipd/gossip_wire.csv | 3 -- lightningd/connect_control.c | 3 +- lightningd/gossip_control.c | 18 +++++++-- lightningd/json.c | 6 +++ lightningd/options.c | 3 +- tests/test_lightningd.py | 7 ++++ wallet/wallet.c | 2 +- 10 files changed, 108 insertions(+), 41 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 5b454d7f0f78..82aa689311ca 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -63,6 +63,9 @@ void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr) towire_u8_array(pptr, (const u8 *)addr->u.sockname, sizeof(addr->u.sockname)); return; + case ADDR_INTERNAL_ALLPROTO: + towire_u16(pptr, addr->u.port); + return; case ADDR_INTERNAL_WIREADDR: towire_wireaddr(pptr, &addr->u.wireaddr); return; @@ -82,6 +85,9 @@ bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, if (!memchr(addr->u.sockname, 0, sizeof(addr->u.sockname))) fromwire_fail(cursor, max); return *cursor != NULL; + case ADDR_INTERNAL_ALLPROTO: + addr->u.port = fromwire_u16(cursor, max); + return *cursor != NULL; case ADDR_INTERNAL_WIREADDR: return fromwire_wireaddr(cursor, max, &addr->u.wireaddr); } @@ -164,6 +170,8 @@ char *fmt_wireaddr_internal(const tal_t *ctx, switch (a->itype) { case ADDR_INTERNAL_SOCKNAME: return tal_fmt(ctx, "%s", a->u.sockname); + case ADDR_INTERNAL_ALLPROTO: + return tal_fmt(ctx, ":%u", a->u.port); case ADDR_INTERNAL_WIREADDR: return fmt_wireaddr(ctx, &a->u.wireaddr); } @@ -297,8 +305,12 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, return res; } -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg) +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, + u16 port, bool wildcard_ok, const char **err_msg) { + u16 wildport; + char *ip; + /* Addresses starting with '/' are local socket paths */ if (arg[0] == '/') { addr->itype = ADDR_INTERNAL_SOCKNAME; @@ -313,6 +325,16 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u1 return true; } + /* An empty string means IPv4 and IPv6 (which under Linux by default + * means just IPv6, and IPv4 gets autobound). */ + if (wildcard_ok + && separate_address_and_port(tmpctx, arg, &ip, &wildport) + && streq(ip, "")) { + addr->itype = ADDR_INTERNAL_ALLPROTO; + addr->u.port = wildport; + return true; + } + addr->itype = ADDR_INTERNAL_WIREADDR; return parse_wireaddr(arg, &addr->u.wireaddr, port, err_msg); } diff --git a/common/wireaddr.h b/common/wireaddr.h index 3b202f9aed92..19a3f0905446 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -77,6 +77,7 @@ bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); enum wireaddr_internal_type { ADDR_INTERNAL_SOCKNAME, + ADDR_INTERNAL_ALLPROTO, ADDR_INTERNAL_WIREADDR, }; @@ -84,11 +85,15 @@ enum wireaddr_internal_type { struct wireaddr_internal { enum wireaddr_internal_type itype; union { + /* ADDR_INTERNAL_SOCKNAME */ struct wireaddr wireaddr; + /* ADDR_INTERNAL_ALLPROTO */ + u16 port; + /* ADDR_INTERNAL_WIREADDR */ char sockname[108]; } u; }; -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, const char **err_msg); +bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, const char **err_msg); void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr); diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 5e5883485864..a84efcd939a6 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -570,7 +570,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, if (!(daemon->listen_announce[i] & ADDR_ANNOUNCE)) continue; /* You can only announce wiretypes! */ - assert(daemon->wireaddrs[i].itype == ADDR_INTERNAL_WIREADDR); + if (daemon->wireaddrs[i].itype != ADDR_INTERNAL_WIREADDR) + continue; towire_wireaddr(&addresses, &daemon->wireaddrs[i].u.wireaddr); } @@ -1389,10 +1390,14 @@ static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, return daemon_conn_read_next(conn, &daemon->master); } -static int make_listen_fd(int domain, void *addr, socklen_t len, bool reportfail) +static int make_listen_fd(int domain, void *addr, socklen_t len, bool mayfail) { int fd = socket(domain, SOCK_STREAM, 0); if (fd < 0) { + if (!mayfail) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to create %u socket: %s", + domain, strerror(errno)); status_trace("Failed to create %u socket: %s", domain, strerror(errno)); return -1; @@ -1407,17 +1412,20 @@ static int make_listen_fd(int domain, void *addr, socklen_t len, bool reportfail strerror(errno)); if (bind(fd, addr, len) != 0) { - if (reportfail) - status_broken("Failed to bind on %u socket: %s", + if (!mayfail) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to bind on %u socket: %s", domain, strerror(errno)); + status_trace("Failed to create %u socket: %s", + domain, strerror(errno)); goto fail; } } if (listen(fd, 5) != 0) { - status_broken("Failed to listen on %u socket: %s", + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Failed to listen on %u socket: %s", domain, strerror(errno)); - goto fail; } return fd; @@ -1561,10 +1569,10 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon init_new_peer, daemon); } -/* Returns true if it was an IPv6 wildcard (as inserted by guess_addresses) */ +/* Return true if it created socket successfully. */ static bool handle_wireaddr_listen(struct daemon *daemon, const struct wireaddr *wireaddr, - bool had_ipv6_wildcard) + bool mayfail) { int fd; struct sockaddr_in addr; @@ -1574,27 +1582,24 @@ static bool handle_wireaddr_listen(struct daemon *daemon, case ADDR_TYPE_IPV4: wireaddr_to_ipv4(wireaddr, &addr); /* We might fail if IPv6 bound to port first */ - fd = make_listen_fd(AF_INET, &addr, sizeof(addr), - !had_ipv6_wildcard); + fd = make_listen_fd(AF_INET, &addr, sizeof(addr), mayfail); if (fd >= 0) { status_trace("Created IPv4 listener on port %u", wireaddr->port); io_new_listener(daemon, fd, connection_in, daemon); + return true; } return false; case ADDR_TYPE_IPV6: wireaddr_to_ipv6(wireaddr, &addr6); - if (memeqzero(&addr6.sin6_addr, sizeof(addr6.sin6_addr))) - had_ipv6_wildcard = true; - else - had_ipv6_wildcard = false; - fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), true); + fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), mayfail); if (fd >= 0) { status_trace("Created IPv6 listener on port %u", wireaddr->port); io_new_listener(daemon, fd, connection_in, daemon); + return true; } - return had_ipv6_wildcard; + return false; case ADDR_TYPE_PADDING: break; } @@ -1604,11 +1609,12 @@ static bool handle_wireaddr_listen(struct daemon *daemon, static void setup_listeners(struct daemon *daemon) { - bool had_ipv6_wildcard = false; struct sockaddr_un addrun; int fd; for (size_t i = 0; i < tal_count(daemon->wireaddrs); i++) { + struct wireaddr wa = daemon->wireaddrs[i].u.wireaddr; + if (!(daemon->listen_announce[i] & ADDR_LISTEN)) continue; @@ -1623,10 +1629,22 @@ static void setup_listeners(struct daemon *daemon) addrun.sun_path); io_new_listener(daemon, fd, connection_in, daemon); continue; + case ADDR_INTERNAL_ALLPROTO: { + bool ipv6_ok; + + memset(wa.addr, 0, sizeof(wa.addr)); + wa.type = ADDR_TYPE_IPV6; + wa.addrlen = 16; + + ipv6_ok = handle_wireaddr_listen(daemon, &wa, true); + wa.type = ADDR_TYPE_IPV4; + wa.addrlen = 4; + /* OK if this fails, as long as one succeeds! */ + handle_wireaddr_listen(daemon, &wa, ipv6_ok); + continue; + } case ADDR_INTERNAL_WIREADDR: - had_ipv6_wildcard = handle_wireaddr_listen( - daemon, &daemon->wireaddrs[i].u.wireaddr, - had_ipv6_wildcard); + handle_wireaddr_listen(daemon, &wa, false); continue; } /* Shouldn't happen. */ @@ -1673,19 +1691,12 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, const u8 *msg) { bool listen; - bool guess_addrs; - u16 port; - if (!fromwire_gossipctl_activate(msg, &listen, &guess_addrs, &port)) + if (!fromwire_gossipctl_activate(msg, &listen)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); - if (listen) { - if (guess_addrs) - guess_addresses(&daemon->wireaddrs, - &daemon->listen_announce, - port); + if (listen) setup_listeners(daemon); - } /* OK, we're ready! */ daemon_conn_send(&daemon->master, @@ -1808,6 +1819,10 @@ static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) ai.ai_addrlen = sizeof(sin); ai.ai_addr = (struct sockaddr *)&sun; break; + case ADDR_INTERNAL_ALLPROTO: + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Can't reach to all protocols"); + break; case ADDR_INTERNAL_WIREADDR: switch (reach->addr.u.wireaddr.type) { case ADDR_TYPE_IPV4: @@ -1950,6 +1965,9 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, case ADDR_INTERNAL_SOCKNAME: af = AF_LOCAL; break; + case ADDR_INTERNAL_ALLPROTO: + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Can't reach ALLPROTO"); case ADDR_INTERNAL_WIREADDR: switch (a->addr.u.wireaddr.type) { case ADDR_TYPE_IPV4: diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index c87a8a2e745d..030c499e88a7 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -23,9 +23,6 @@ gossipctl_init,,reconnect,bool gossipctl_activate,3025 # Do we listen? gossipctl_activate,,listen,bool -gossipctl_activate,,guess_addresses,bool -# FIXME: Hack for deprecated --port option. -gossipctl_activate,,port,u16 # Gossipd->master, I am ready, here are the final addresses. gossipctl_activate_reply,3125 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 05354922d27e..9ee955b38886 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -151,7 +151,8 @@ static void json_connect(struct command *cmd, } else { port = DEFAULT_PORT; } - if (!parse_wireaddr_internal(name, &addr, port, &err_msg)) { + if (!parse_wireaddr_internal(name, &addr, port, false, + &err_msg)) { command_fail(cmd, "Host %s:%u not valid: %s", name, port, err_msg ? err_msg : "port is 0"); return; diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 8010622dc83e..897a58528283 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -183,6 +183,8 @@ void gossip_init(struct lightningd *ld) u8 *msg; int hsmfd; u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; + struct wireaddr_internal *wireaddrs = ld->wireaddrs; + enum addr_listen_announce *listen_announce = ld->listen_announce; msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); if (!wire_sync_write(ld->hsm_fd, msg)) @@ -202,12 +204,21 @@ void gossip_init(struct lightningd *ld) if (!ld->gossip) err(1, "Could not subdaemon gossip"); + /* If no addr specified, hand wildcard to gossipd */ + if (tal_count(wireaddrs) == 0 && ld->autolisten) { + wireaddrs = tal_arrz(tmpctx, struct wireaddr_internal, 1); + listen_announce = tal_arr(tmpctx, enum addr_listen_announce, 1); + wireaddrs->itype = ADDR_INTERNAL_ALLPROTO; + wireaddrs->u.port = ld->portnum; + *listen_announce = ADDR_LISTEN_AND_ANNOUNCE; + } + msg = towire_gossipctl_init( tmpctx, ld->config.broadcast_interval, &get_chainparams(ld)->genesis_blockhash, &ld->id, get_offered_global_features(tmpctx), - get_offered_local_features(tmpctx), ld->wireaddrs, - ld->listen_announce, ld->rgb, + get_offered_local_features(tmpctx), wireaddrs, + listen_announce, ld->rgb, ld->alias, ld->config.channel_update_interval, ld->reconnect); subd_send_msg(ld->gossip, msg); } @@ -234,8 +245,7 @@ static void gossip_activate_done(struct subd *gossip UNUSED, void gossip_activate(struct lightningd *ld) { - const u8 *msg = towire_gossipctl_activate(NULL, ld->listen, ld->autolisten, - ld->portnum); + const u8 *msg = towire_gossipctl_activate(NULL, ld->listen); subd_req(ld->gossip, ld->gossip, take(msg), -1, 0, gossip_activate_done, NULL); diff --git a/lightningd/json.c b/lightningd/json.c index 35b5c3c03b47..b401ca00a935 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -154,6 +154,12 @@ void json_add_address_internal(struct json_result *response, json_add_string(response, "socket", addr->u.sockname); json_object_end(response); return; + case ADDR_INTERNAL_ALLPROTO: + json_object_start(response, fieldname); + json_add_string(response, "type", "any protocol"); + json_add_num(response, "port", addr->u.port); + json_object_end(response); + return; case ADDR_INTERNAL_WIREADDR: json_add_address(response, fieldname, &addr->u.wireaddr); return; diff --git a/lightningd/options.c b/lightningd/options.c index 7c32c9dfdf59..b461d0396b38 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -158,7 +158,8 @@ static char *opt_add_addr_withtype(const char *arg, tal_resize(&ld->listen_announce, n+1); ld->listen_announce[n] = ala; - if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, &err_msg)) { + if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, + true, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index bdf4d738abc0..1385c784d8e4 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -4281,6 +4281,13 @@ def test_pay_disconnect(self): bitcoind.generate_block(1) l1.daemon.wait_for_log('ONCHAIN') + def test_address(self): + l1 = self.node_factory.get_node() + assert len(l1.rpc.getinfo()['address']) == 1 + assert l1.rpc.getinfo()['address'][0]['type'] == 'ipv4' + assert l1.rpc.getinfo()['address'][0]['address'] == '127.0.0.1' + assert int(l1.rpc.getinfo()['address'][0]['port']) == l1.port + def test_listconfigs(self): l1 = self.node_factory.get_node() diff --git a/wallet/wallet.c b/wallet/wallet.c index 548fe3f24659..cead5db5dd52 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -510,7 +510,7 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) addrstr = sqlite3_column_text(stmt, 2); if (addrstr) { addrp = &addr; - if (!parse_wireaddr_internal((const char*)addrstr, addrp, DEFAULT_PORT, NULL)) { + if (!parse_wireaddr_internal((const char*)addrstr, addrp, DEFAULT_PORT, false, NULL)) { db_stmt_done(stmt); return NULL; } From 711949a980f837fee673d3128c058fb96f4830ab Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:22 +0930 Subject: [PATCH 12/14] gossipd: handle wildcard addresses correctly. If we're given a wildcard address, we can't announce it like that: we need to try to turn it into a real address (using guess_address). Then we use that address. As a side-effect of this cleanup, we only announce *any* '--addr' if it's routable. This fix means that our tests have to force '--announce-addr' because otherwise localhost isn't routable. This means that gossipd really controls the addresses now, and breaks them into two arrays: what we bind to, and what we announce. That is now what we return to the master for json_getinfo(), which prints them as 'bindings' and 'addresses' respectively. Signed-off-by: Rusty Russell --- common/wireaddr.c | 13 ++++ common/wireaddr.h | 2 + gossipd/gossip.c | 127 +++++++++++++++++++++++++++--------- gossipd/gossip_wire.csv | 9 +-- gossipd/netaddress.c | 81 ++++------------------- gossipd/netaddress.h | 8 ++- lightningd/gossip_control.c | 11 ++-- lightningd/jsonrpc.c | 25 +++---- lightningd/lightningd.c | 4 +- lightningd/lightningd.h | 12 ++-- lightningd/options.c | 19 +++--- tests/test_lightningd.py | 15 +++-- tests/utils.py | 6 +- 13 files changed, 182 insertions(+), 150 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 82aa689311ca..a5044eba63c7 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,18 @@ bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6) return true; } +bool wireaddr_is_wildcard(const struct wireaddr *addr) +{ + switch (addr->type) { + case ADDR_TYPE_IPV6: + case ADDR_TYPE_IPV4: + return memeqzero(addr->addr, addr->addrlen); + case ADDR_TYPE_PADDING: + return false; + } + abort(); +} + char *fmt_wireaddr_internal(const tal_t *ctx, const struct wireaddr_internal *a) { diff --git a/common/wireaddr.h b/common/wireaddr.h index 19a3f0905446..994774c01268 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -75,6 +75,8 @@ void wireaddr_from_ipv6(struct wireaddr *addr, bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4); bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); +bool wireaddr_is_wildcard(const struct wireaddr *addr); + enum wireaddr_internal_type { ADDR_INTERNAL_SOCKNAME, ADDR_INTERNAL_ALLPROTO, diff --git a/gossipd/gossip.c b/gossipd/gossip.c index a84efcd939a6..beece573e8ff 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -129,8 +129,13 @@ struct daemon { u8 alias[33]; u8 rgb[3]; - struct wireaddr_internal *wireaddrs; - enum addr_listen_announce *listen_announce; + + /* Addresses master told us to use */ + struct wireaddr_internal *proposed_wireaddr; + enum addr_listen_announce *proposed_listen_announce; + + /* What we actually announce. */ + struct wireaddr *announcable; /* To make sure our node_announcement timestamps increase */ u32 last_announce_timestamp; @@ -566,15 +571,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, sig = tal(ctx, secp256k1_ecdsa_signature); memset(sig, 0, sizeof(*sig)); } - for (i = 0; i < tal_count(daemon->wireaddrs); i++) { - if (!(daemon->listen_announce[i] & ADDR_ANNOUNCE)) - continue; - /* You can only announce wiretypes! */ - if (daemon->wireaddrs[i].itype != ADDR_INTERNAL_WIREADDR) - continue; - towire_wireaddr(&addresses, - &daemon->wireaddrs[i].u.wireaddr); - } + for (i = 0; i < tal_count(daemon->announcable); i++) + towire_wireaddr(&addresses, &daemon->announcable[i]); announcement = towire_node_announcement(ctx, sig, features, timestamp, @@ -1607,51 +1605,113 @@ static bool handle_wireaddr_listen(struct daemon *daemon, "Invalid listener wireaddress type %u", wireaddr->type); } -static void setup_listeners(struct daemon *daemon) +/* If it's a wildcard, turns it into a real address pointing to internet */ +static bool public_address(struct wireaddr *wireaddr) +{ + if (wireaddr_is_wildcard(wireaddr)) { + if (!guess_address(wireaddr)) + return false; + } + + return address_routable(wireaddr); +} + +static void add_announcable(struct daemon *daemon, const struct wireaddr *addr) +{ + size_t n = tal_count(daemon->announcable); + tal_resize(&daemon->announcable, n+1); + daemon->announcable[n] = *addr; +} + +static void add_binding(struct wireaddr_internal **binding, + const struct wireaddr_internal *addr) +{ + size_t n = tal_count(*binding); + tal_resize(binding, n+1); + (*binding)[n] = *addr; +} + +/* Initializes daemon->announcable array, returns addresses we bound to. */ +static struct wireaddr_internal *setup_listeners(const tal_t *ctx, + struct daemon *daemon) { struct sockaddr_un addrun; int fd; + struct wireaddr_internal *binding; + + binding = tal_arr(ctx, struct wireaddr_internal, 0); + daemon->announcable = tal_arr(daemon, struct wireaddr, 0); - for (size_t i = 0; i < tal_count(daemon->wireaddrs); i++) { - struct wireaddr wa = daemon->wireaddrs[i].u.wireaddr; + for (size_t i = 0; i < tal_count(daemon->proposed_wireaddr); i++) { + struct wireaddr_internal wa = daemon->proposed_wireaddr[i]; - if (!(daemon->listen_announce[i] & ADDR_LISTEN)) + if (!(daemon->proposed_listen_announce[i] & ADDR_LISTEN)) { + assert(daemon->proposed_listen_announce[i] + & ADDR_ANNOUNCE); + /* You can only announce wiretypes! */ + assert(daemon->proposed_wireaddr[i].itype + == ADDR_INTERNAL_WIREADDR); + add_announcable(daemon, &wa.u.wireaddr); continue; + } - switch (daemon->wireaddrs[i].itype) { + switch (wa.itype) { case ADDR_INTERNAL_SOCKNAME: addrun.sun_family = AF_UNIX; - memcpy(addrun.sun_path, daemon->wireaddrs[i].u.sockname, + memcpy(addrun.sun_path, wa.u.sockname, sizeof(addrun.sun_path)); fd = make_listen_fd(AF_INET, &addrun, sizeof(addrun), false); status_trace("Created socket listener on file %s", addrun.sun_path); io_new_listener(daemon, fd, connection_in, daemon); + /* We don't announce socket names */ + add_binding(&binding, &wa); continue; case ADDR_INTERNAL_ALLPROTO: { bool ipv6_ok; - memset(wa.addr, 0, sizeof(wa.addr)); - wa.type = ADDR_TYPE_IPV6; - wa.addrlen = 16; - - ipv6_ok = handle_wireaddr_listen(daemon, &wa, true); - wa.type = ADDR_TYPE_IPV4; - wa.addrlen = 4; + wa.itype = ADDR_INTERNAL_WIREADDR; + wa.u.wireaddr.port = wa.u.port; + memset(wa.u.wireaddr.addr, 0, + sizeof(wa.u.wireaddr.addr)); + + /* Try both IPv6 and IPv4. */ + wa.u.wireaddr.type = ADDR_TYPE_IPV6; + wa.u.wireaddr.addrlen = 16; + + ipv6_ok = handle_wireaddr_listen(daemon, &wa.u.wireaddr, + true); + if (ipv6_ok) { + add_binding(&binding, &wa); + if (public_address(&wa.u.wireaddr)) + add_announcable(daemon, &wa.u.wireaddr); + } + wa.u.wireaddr.type = ADDR_TYPE_IPV4; + wa.u.wireaddr.addrlen = 4; /* OK if this fails, as long as one succeeds! */ - handle_wireaddr_listen(daemon, &wa, ipv6_ok); + if (handle_wireaddr_listen(daemon, &wa.u.wireaddr, + ipv6_ok)) { + add_binding(&binding, &wa); + if (public_address(&wa.u.wireaddr)) + add_announcable(daemon, &wa.u.wireaddr); + } continue; } case ADDR_INTERNAL_WIREADDR: - handle_wireaddr_listen(daemon, &wa, false); + handle_wireaddr_listen(daemon, &wa.u.wireaddr, false); + add_binding(&binding, &wa); + if (public_address(&wa.u.wireaddr)) + add_announcable(daemon, &wa.u.wireaddr); continue; } /* Shouldn't happen. */ status_failed(STATUS_FAIL_INTERNAL_ERROR, "Invalid listener address type %u", - daemon->wireaddrs[i].itype); + daemon->proposed_wireaddr[i].itype); } + + return binding; } /* Parse an incoming gossip init message and assign config variables @@ -1667,8 +1727,8 @@ static struct io_plan *gossip_init(struct daemon_conn *master, if (!fromwire_gossipctl_init( daemon, msg, &daemon->broadcast_interval, &chain_hash, &daemon->id, &daemon->globalfeatures, - &daemon->localfeatures, &daemon->wireaddrs, - &daemon->listen_announce, daemon->rgb, + &daemon->localfeatures, &daemon->proposed_wireaddr, + &daemon->proposed_listen_announce, daemon->rgb, daemon->alias, &update_channel_interval, &daemon->reconnect)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } @@ -1691,18 +1751,21 @@ static struct io_plan *gossip_activate(struct daemon_conn *master, const u8 *msg) { bool listen; + struct wireaddr_internal *binding; if (!fromwire_gossipctl_activate(msg, &listen)) master_badmsg(WIRE_GOSSIPCTL_ACTIVATE, msg); if (listen) - setup_listeners(daemon); + binding = setup_listeners(tmpctx, daemon); + else + binding = NULL; /* OK, we're ready! */ daemon_conn_send(&daemon->master, take(towire_gossipctl_activate_reply(NULL, - daemon->wireaddrs, - daemon->listen_announce))); + binding, + daemon->announcable))); return daemon_conn_read_next(master->conn, master); } diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 030c499e88a7..c651151159d4 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -24,11 +24,12 @@ gossipctl_activate,3025 # Do we listen? gossipctl_activate,,listen,bool -# Gossipd->master, I am ready, here are the final addresses. +# Gossipd->master, I am ready, here's the addresses I bound, can announce. gossipctl_activate_reply,3125 -gossipctl_activate_reply,,num_wireaddrs,u16 -gossipctl_activate_reply,,wireaddrs,num_wireaddrs*struct wireaddr_internal -gossipctl_activate_reply,,listen_announce,num_wireaddrs*enum addr_listen_announce +gossipctl_activate_reply,,num_bindings,u16 +gossipctl_activate_reply,,bindings,num_bindings*struct wireaddr_internal +gossipctl_activate_reply,,num_announcable,u16 +gossipctl_activate_reply,,announcable,num_announcable*struct wireaddr # Master -> gossipd: Optional hint for where to find peer. gossipctl_peer_addrhint,3014 diff --git a/gossipd/netaddress.c b/gossipd/netaddress.c index a7939e970a61..22cb85df4d3a 100644 --- a/gossipd/netaddress.c +++ b/gossipd/netaddress.c @@ -201,48 +201,39 @@ static bool IsRoutable(const struct wireaddr *addr) * then query address. */ /* Returns 0 if protocol completely unsupported, ADDR_LISTEN if we * can't reach addr, ADDR_LISTEN_AND_ANNOUNCE if we can (and fill saddr). */ -static enum addr_listen_announce get_local_sockname(int af, void *saddr, - socklen_t saddrlen) +static bool get_local_sockname(int af, void *saddr, socklen_t saddrlen) { int fd = socket(af, SOCK_DGRAM, 0); if (fd < 0) { status_trace("Failed to create %u socket: %s", af, strerror(errno)); - return 0; + return false; } if (connect(fd, saddr, saddrlen) != 0) { status_trace("Failed to connect %u socket: %s", af, strerror(errno)); close(fd); - return ADDR_LISTEN; + return false; } if (getsockname(fd, saddr, &saddrlen) != 0) { status_trace("Failed to get %u socket name: %s", af, strerror(errno)); close(fd); - return ADDR_LISTEN; + return false; } close(fd); - return ADDR_LISTEN_AND_ANNOUNCE; + return true; } -/* Return 0 if not available, or whether it's listenable-only or announceable. - * If it's listenable only, will set wireaddr to all-zero address for universal - * binding. */ -static enum addr_listen_announce guess_one_address(struct wireaddr *addr, - u16 portnum, - enum wire_addr_type type) +bool guess_address(struct wireaddr *addr) { - enum addr_listen_announce ret; - - addr->type = type; - addr->port = portnum; + bool ret; /* We point to Google nameservers, works unless you're inside Google :) */ - switch (type) { + switch (addr->type) { case ADDR_TYPE_IPV4: { struct sockaddr_in sin; sin.sin_port = htons(53); @@ -252,7 +243,7 @@ static enum addr_listen_announce guess_one_address(struct wireaddr *addr, ret = get_local_sockname(AF_INET, &sin, sizeof(sin)); addr->addrlen = sizeof(sin.sin_addr); memcpy(addr->addr, &sin.sin_addr, addr->addrlen); - break; + return ret; } case ADDR_TYPE_IPV6: { struct sockaddr_in6 sin6; @@ -266,59 +257,15 @@ static enum addr_listen_announce guess_one_address(struct wireaddr *addr, ret = get_local_sockname(AF_INET6, &sin6, sizeof(sin6)); addr->addrlen = sizeof(sin6.sin6_addr); memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); - break; + return ret; } case ADDR_TYPE_PADDING: - status_trace("Padding address, ignoring"); - return 0; - } - - if (ret == 0) - return ret; - - /* If we can reach it, but resulting address is unroutable, listen only */ - if (ret == ADDR_LISTEN_AND_ANNOUNCE && !IsRoutable(addr)) { - status_trace("Address %s is not routable", - type_to_string(tmpctx, struct wireaddr, addr)); - ret = ADDR_LISTEN; - } - - if (ret == ADDR_LISTEN) { - /* This corresponds to INADDR_ANY or in6addr_any */ - memset(addr->addr, 0, addr->addrlen); - } else { - status_trace("Public address %s", - type_to_string(tmpctx, struct wireaddr, addr)); + break; } - return ret; + abort(); } -void guess_addresses(struct wireaddr_internal **wireaddrs, - enum addr_listen_announce **listen_announce, - u16 portnum) +bool address_routable(const struct wireaddr *wireaddr) { - size_t n = tal_count(*wireaddrs); - - status_trace("Trying to guess public addresses..."); - - /* We allocate an extra, then remove if it's not needed. */ - tal_resize(wireaddrs, n+1); - tal_resize(listen_announce, n+1); - - (*wireaddrs)[n].itype = ADDR_INTERNAL_WIREADDR; - /* We do IPv6 first: on Linux, that binds to IPv4 too. */ - (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n].u.wireaddr, - portnum, ADDR_TYPE_IPV6); - if ((*listen_announce)[n] != 0) { - n++; - tal_resize(wireaddrs, n+1); - tal_resize(listen_announce, n+1); - (*wireaddrs)[n].itype = ADDR_INTERNAL_WIREADDR; - } - (*listen_announce)[n] = guess_one_address(&(*wireaddrs)[n].u.wireaddr, - portnum, ADDR_TYPE_IPV4); - if ((*listen_announce)[n] == 0) { - tal_resize(wireaddrs, n); - tal_resize(listen_announce, n); - } + return IsRoutable(wireaddr); } diff --git a/gossipd/netaddress.h b/gossipd/netaddress.h index 58a3fa25ddc4..664b10ad0fbf 100644 --- a/gossipd/netaddress.h +++ b/gossipd/netaddress.h @@ -4,8 +4,10 @@ #include #include -void guess_addresses(struct wireaddr_internal **wireaddrs, - enum addr_listen_announce **listen_announce, - u16 portnum); +/* Address is a wildcard: try to guess what it looks like to outside world */ +bool guess_address(struct wireaddr *wireaddr); + +/* Is this address public? */ +bool address_routable(const struct wireaddr *wireaddr); #endif /* LIGHTNING_GOSSIPD_NETADDRESS_H */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 897a58528283..c0dfa08d608a 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -183,8 +183,8 @@ void gossip_init(struct lightningd *ld) u8 *msg; int hsmfd; u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; - struct wireaddr_internal *wireaddrs = ld->wireaddrs; - enum addr_listen_announce *listen_announce = ld->listen_announce; + struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; + enum addr_listen_announce *listen_announce = ld->proposed_listen_announce; msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); if (!wire_sync_write(ld->hsm_fd, msg)) @@ -230,12 +230,9 @@ static void gossip_activate_done(struct subd *gossip UNUSED, { struct lightningd *ld = gossip->ld; - /* Reply gives us the actual wireaddrs we're using */ - tal_free(ld->wireaddrs); - tal_free(ld->listen_announce); if (!fromwire_gossipctl_activate_reply(gossip->ld, reply, - &ld->wireaddrs, - &ld->listen_announce)) + &ld->binding, + &ld->announcable)) fatal("Bad gossipctl_activate_reply: %s", tal_hex(reply, reply)); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 660de3fc8111..8fda08270bb1 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -150,30 +150,21 @@ static void json_getinfo(struct command *cmd, json_object_start(response, NULL); json_add_pubkey(response, "id", &cmd->ld->id); if (cmd->ld->listen) { - bool have_listen_no_announce = false; if (deprecated_apis) json_add_num(response, "port", cmd->ld->portnum); /* These are the addresses we're announcing */ json_array_start(response, "address"); - for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { - if (cmd->ld->listen_announce[i] & ADDR_ANNOUNCE) - json_add_address_internal(response, NULL, - cmd->ld->wireaddrs+i); - else - have_listen_no_announce = true; - } + for (size_t i = 0; i < tal_count(cmd->ld->announcable); i++) + json_add_address(response, NULL, cmd->ld->announcable+i); json_array_end(response); - if (have_listen_no_announce) { - json_array_start(response, "listen-only"); - for (size_t i = 0; i < tal_count(cmd->ld->wireaddrs); i++) { - if (cmd->ld->listen_announce[i] == ADDR_LISTEN) - json_add_address_internal(response, NULL, - cmd->ld->wireaddrs+i); - } - json_array_end(response); - } + /* This is what we're actually bound to. */ + json_array_start(response, "binding"); + for (size_t i = 0; i < tal_count(cmd->ld->binding); i++) + json_add_address_internal(response, NULL, + cmd->ld->binding+i); + json_array_end(response); } json_add_string(response, "version", version()); json_add_num(response, "blockheight", get_block_height(cmd->ld->topology)); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 15b94251ba66..2ce639c4e4a3 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -69,8 +69,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->waitsendpay_commands); list_head_init(&ld->sendpay_commands); list_head_init(&ld->close_commands); - ld->wireaddrs = tal_arr(ld, struct wireaddr_internal, 0); - ld->listen_announce = tal_arr(ld, enum addr_listen_announce, 0); + ld->proposed_wireaddr = tal_arr(ld, struct wireaddr_internal, 0); + ld->proposed_listen_announce = tal_arr(ld, enum addr_listen_announce, 0); ld->portnum = DEFAULT_PORT; ld->listen = true; ld->autolisten = true; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index bab518a4e896..311726222290 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -120,10 +120,14 @@ struct lightningd { /* Do we want to guess addresses to listen and announce? */ bool autolisten; - /* Addresses to bind/announce to the network (tal_count()) */ - struct wireaddr_internal *wireaddrs; - /* And the bitset for each, whether to listen, announce or both */ - enum addr_listen_announce *listen_announce; + /* Setup: Addresses to bind/announce to the network (tal_count()) */ + struct wireaddr_internal *proposed_wireaddr; + /* Setup: And the bitset for each, whether to listen, announce or both */ + enum addr_listen_announce *proposed_listen_announce; + + /* Actual bindings and announcables from gossipd */ + struct wireaddr_internal *binding; + struct wireaddr *announcable; /* Bearer of all my secrets. */ int hsm_fd; diff --git a/lightningd/options.c b/lightningd/options.c index b461d0396b38..a9ec6fc9e00d 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -149,16 +149,16 @@ static char *opt_add_addr_withtype(const char *arg, struct lightningd *ld, enum addr_listen_announce ala) { - size_t n = tal_count(ld->wireaddrs); + size_t n = tal_count(ld->proposed_wireaddr); char const *err_msg; assert(arg != NULL); - tal_resize(&ld->wireaddrs, n+1); - tal_resize(&ld->listen_announce, n+1); - ld->listen_announce[n] = ala; + tal_resize(&ld->proposed_wireaddr, n+1); + tal_resize(&ld->proposed_listen_announce, n+1); + ld->proposed_listen_announce[n] = ala; - if (!parse_wireaddr_internal(arg, &ld->wireaddrs[n], ld->portnum, + if (!parse_wireaddr_internal(arg, &ld->proposed_wireaddr[n], ld->portnum, true, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } @@ -918,17 +918,20 @@ static void add_config(struct lightningd *ld, /* Covered by opt_add_addr below */ } else if (opt->cb_arg == (void *)opt_add_addr) { json_add_opt_addrs(response, name0, - ld->wireaddrs, ld->listen_announce, + ld->proposed_wireaddr, + ld->proposed_listen_announce, ADDR_LISTEN_AND_ANNOUNCE); return; } else if (opt->cb_arg == (void *)opt_add_bind_addr) { json_add_opt_addrs(response, name0, - ld->wireaddrs, ld->listen_announce, + ld->proposed_wireaddr, + ld->proposed_listen_announce, ADDR_LISTEN); return; } else if (opt->cb_arg == (void *)opt_add_announce_addr) { json_add_opt_addrs(response, name0, - ld->wireaddrs, ld->listen_announce, + ld->proposed_wireaddr, + ld->proposed_listen_announce, ADDR_ANNOUNCE); return; #if DEVELOPER diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 1385c784d8e4..a0807632ca2f 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -4283,10 +4283,17 @@ def test_pay_disconnect(self): def test_address(self): l1 = self.node_factory.get_node() - assert len(l1.rpc.getinfo()['address']) == 1 - assert l1.rpc.getinfo()['address'][0]['type'] == 'ipv4' - assert l1.rpc.getinfo()['address'][0]['address'] == '127.0.0.1' - assert int(l1.rpc.getinfo()['address'][0]['port']) == l1.port + addr = l1.rpc.getinfo()['address'] + assert len(addr) == 1 + assert addr[0]['type'] == 'ipv4' + assert addr[0]['address'] == '127.0.0.1' + assert int(addr[0]['port']) == l1.port + + bind = l1.rpc.getinfo()['binding'] + assert len(bind) == 1 + assert bind[0]['type'] == 'ipv4' + assert bind[0]['address'] == '127.0.0.1' + assert int(bind[0]['port']) == l1.port def test_listconfigs(self): l1 = self.node_factory.get_node() diff --git a/tests/utils.py b/tests/utils.py index 5ddba846e673..29a9fde2920b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -261,7 +261,9 @@ def __init__(self, lightning_dir, bitcoin_dir, port=9735, random_hsm=False, node opts = { 'bitcoin-datadir': bitcoin_dir, 'lightning-dir': lightning_dir, - 'addr': '127.0.0.1:{}'.format(port), + 'bind-addr': '127.0.0.1:{}'.format(port), + # lightningd won't announce non-routable addresses by default. + 'announce-addr': '127.0.0.1:{}'.format(port), 'allow-deprecated-apis': 'false', 'override-fee-rates': '15000/7500/1000', 'network': 'regtest', @@ -370,7 +372,7 @@ def db_manip(self, query): def start(self): self.daemon.start() # This shortcut is sufficient for our simple tests. - self.port = self.rpc.getinfo()['address'][0]['port'] + self.port = self.rpc.getinfo()['binding'][0]['port'] def stop(self, timeout=10): """ Attempt to do a clean shutdown, but kill if it hangs From 82e1f61792fd544aff4c814e747efe0d69a22f1b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:22 +0930 Subject: [PATCH 13/14] gossipd: don't try to connect to non-routable addresses. Someone could try to announce an internal address, and we might probe it. This breaks tests, so we add '--dev-allow-localhost' for our tests, so we don't eliminate that one. Of course, now we need to skip some more tests in non-developer mode. Signed-off-by: Rusty Russell --- common/test/run-ip_port_parsing.c | 6 ---- gossipd/gossip.c | 41 +++++++++++++++----------- gossipd/gossip_wire.csv | 1 + gossipd/netaddress.c | 4 ++- gossipd/netaddress.h | 3 +- gossipd/routing.c | 4 ++- gossipd/routing.h | 6 +++- gossipd/test/run-bench-find_route.c | 2 +- gossipd/test/run-find_route-specific.c | 2 +- gossipd/test/run-find_route.c | 2 +- lightningd/gossip_control.c | 8 ++++- lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 ++ lightningd/options.c | 3 ++ tests/test_lightningd.py | 12 +++++--- tests/utils.py | 6 ++-- 16 files changed, 66 insertions(+), 38 deletions(-) diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index d99811c161a2..0773e3a3cf3c 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -8,9 +8,6 @@ /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_bool */ -bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } /* Generated stub for fromwire_fail */ const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } @@ -26,9 +23,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } /* Generated stub for towire_u16 */ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) { fprintf(stderr, "towire_u16 called!\n"); abort(); } diff --git a/gossipd/gossip.c b/gossipd/gossip.c index beece573e8ff..4afa2fda38ab 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1606,14 +1606,14 @@ static bool handle_wireaddr_listen(struct daemon *daemon, } /* If it's a wildcard, turns it into a real address pointing to internet */ -static bool public_address(struct wireaddr *wireaddr) +static bool public_address(struct daemon *daemon, struct wireaddr *wireaddr) { if (wireaddr_is_wildcard(wireaddr)) { if (!guess_address(wireaddr)) return false; } - return address_routable(wireaddr); + return address_routable(wireaddr, daemon->rstate->dev_allow_localhost); } static void add_announcable(struct daemon *daemon, const struct wireaddr *addr) @@ -1684,7 +1684,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, true); if (ipv6_ok) { add_binding(&binding, &wa); - if (public_address(&wa.u.wireaddr)) + if (public_address(daemon, &wa.u.wireaddr)) add_announcable(daemon, &wa.u.wireaddr); } wa.u.wireaddr.type = ADDR_TYPE_IPV4; @@ -1693,7 +1693,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, if (handle_wireaddr_listen(daemon, &wa.u.wireaddr, ipv6_ok)) { add_binding(&binding, &wa); - if (public_address(&wa.u.wireaddr)) + if (public_address(daemon, &wa.u.wireaddr)) add_announcable(daemon, &wa.u.wireaddr); } continue; @@ -1701,7 +1701,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, case ADDR_INTERNAL_WIREADDR: handle_wireaddr_listen(daemon, &wa.u.wireaddr, false); add_binding(&binding, &wa); - if (public_address(&wa.u.wireaddr)) + if (public_address(daemon, &wa.u.wireaddr)) add_announcable(daemon, &wa.u.wireaddr); continue; } @@ -1723,18 +1723,21 @@ static struct io_plan *gossip_init(struct daemon_conn *master, { struct bitcoin_blkid chain_hash; u32 update_channel_interval; + bool dev_allow_localhost; if (!fromwire_gossipctl_init( daemon, msg, &daemon->broadcast_interval, &chain_hash, &daemon->id, &daemon->globalfeatures, &daemon->localfeatures, &daemon->proposed_wireaddr, &daemon->proposed_listen_announce, daemon->rgb, - daemon->alias, &update_channel_interval, &daemon->reconnect)) { + daemon->alias, &update_channel_interval, &daemon->reconnect, + &dev_allow_localhost)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } /* Prune time is twice update time */ daemon->rstate = new_routing_state(daemon, &chain_hash, &daemon->id, - update_channel_interval * 2); + update_channel_interval * 2, + dev_allow_localhost); /* Load stored gossip messages */ gossip_store_load(daemon->rstate, daemon->rstate->store); @@ -1944,7 +1947,6 @@ gossip_resolve_addr(const tal_t *ctx, const struct pubkey *id) { struct node *node; - struct addrhint *a; /* Get from routing state. */ node = get_node(rstate, id); @@ -1952,18 +1954,23 @@ gossip_resolve_addr(const tal_t *ctx, /* No matching node? */ if (!node) return NULL; - /* Node has no addresses? */ - if (tal_count(node->addresses) == 0) - return NULL; /* FIXME: When struct addrhint can contain more than one address, - * we should copy all addresses. - * For now getting first address should be fine. */ - a = tal(ctx, struct addrhint); - a->addr.itype = ADDR_INTERNAL_WIREADDR; - a->addr.u.wireaddr = node->addresses[0]; + * we should copy all routable addresses. */ + for (size_t i = 0; i < tal_count(node->addresses); i++) { + struct addrhint *a; + + if (!address_routable(&node->addresses[i], + rstate->dev_allow_localhost)) + continue; - return a; + a = tal(ctx, struct addrhint); + a->addr.itype = ADDR_INTERNAL_WIREADDR; + a->addr.u.wireaddr = node->addresses[i]; + return a; + } + + return NULL; } static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index c651151159d4..d7023b9246ef 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -18,6 +18,7 @@ gossipctl_init,,rgb,3*u8 gossipctl_init,,alias,32*u8 gossipctl_init,,update_channel_interval,u32 gossipctl_init,,reconnect,bool +gossipctl_init,,dev_allow_localhost,bool # Activate the gossip daemon, so others can connect. gossipctl_activate,3025 diff --git a/gossipd/netaddress.c b/gossipd/netaddress.c index 22cb85df4d3a..7d3d4b41f1c2 100644 --- a/gossipd/netaddress.c +++ b/gossipd/netaddress.c @@ -265,7 +265,9 @@ bool guess_address(struct wireaddr *addr) abort(); } -bool address_routable(const struct wireaddr *wireaddr) +bool address_routable(const struct wireaddr *wireaddr, bool allow_localhost) { + if (allow_localhost && IsLocal(wireaddr)) + return true; return IsRoutable(wireaddr); } diff --git a/gossipd/netaddress.h b/gossipd/netaddress.h index 664b10ad0fbf..27ce71853730 100644 --- a/gossipd/netaddress.h +++ b/gossipd/netaddress.h @@ -8,6 +8,7 @@ bool guess_address(struct wireaddr *wireaddr); /* Is this address public? */ -bool address_routable(const struct wireaddr *wireaddr); +bool address_routable(const struct wireaddr *wireaddr, + bool allow_localhost); #endif /* LIGHTNING_GOSSIPD_NETADDRESS_H */ diff --git a/gossipd/routing.c b/gossipd/routing.c index d7930588ebc1..81cc03b84bf0 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -86,7 +86,8 @@ static struct node_map *empty_node_map(const tal_t *ctx) struct routing_state *new_routing_state(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct pubkey *local_id, - u32 prune_timeout) + u32 prune_timeout, + bool dev_allow_localhost) { struct routing_state *rstate = tal(ctx, struct routing_state); rstate->nodes = empty_node_map(rstate); @@ -95,6 +96,7 @@ struct routing_state *new_routing_state(const tal_t *ctx, rstate->local_id = *local_id; rstate->prune_timeout = prune_timeout; rstate->store = gossip_store_new(rstate); + rstate->dev_allow_localhost = dev_allow_localhost; list_head_init(&rstate->pending_cannouncement); uintmap_init(&rstate->chanmap); diff --git a/gossipd/routing.h b/gossipd/routing.h index 6c131207434b..45867b32aaf7 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -162,6 +162,9 @@ struct routing_state { * restarts */ struct gossip_store *store; + /* For testing, we announce and accept localhost */ + bool dev_allow_localhost; + /* A map of channels indexed by short_channel_ids */ UINTMAP(struct chan *) chanmap; }; @@ -183,7 +186,8 @@ struct route_hop { struct routing_state *new_routing_state(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct pubkey *local_id, - u32 prune_timeout); + u32 prune_timeout, + bool dev_allow_localhost); struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 6a7f85c5a919..2b24a12b0d4d 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -219,7 +219,7 @@ int main(int argc, char *argv[]) | SECP256K1_CONTEXT_SIGN); setup_tmpctx(); - rstate = new_routing_state(tmpctx, &zerohash, &me, 0); + rstate = new_routing_state(tmpctx, &zerohash, &me, 0, false); opt_register_noarg("--perfme", opt_set_bool, &perfme, "Run perfme-start and perfme-stop around benchmark"); diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 9a980327a872..c1d5fd1abd06 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -152,7 +152,7 @@ int main(void) strlen("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06"), &c); - rstate = new_routing_state(tmpctx, &zerohash, &a, 0); + rstate = new_routing_state(tmpctx, &zerohash, &a, 0, false); /* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */ nc = get_or_make_connection(rstate, &c, &b, "6990:2:1"); diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 2eefc9eff8e4..e166c04184d1 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -188,7 +188,7 @@ int main(void) memset(&tmp, 'a', sizeof(tmp)); pubkey_from_privkey(&tmp, &a); - rstate = new_routing_state(tmpctx, &zerohash, &a, 0); + rstate = new_routing_state(tmpctx, &zerohash, &a, 0, false); new_node(rstate, &a); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index c0dfa08d608a..ba895748df8c 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -185,6 +185,11 @@ void gossip_init(struct lightningd *ld) u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; enum addr_listen_announce *listen_announce = ld->proposed_listen_announce; + bool allow_localhost = false; +#if DEVELOPER + if (ld->dev_allow_localhost) + allow_localhost = true; +#endif msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); if (!wire_sync_write(ld->hsm_fd, msg)) @@ -219,7 +224,8 @@ void gossip_init(struct lightningd *ld) get_offered_global_features(tmpctx), get_offered_local_features(tmpctx), wireaddrs, listen_announce, ld->rgb, - ld->alias, ld->config.channel_update_interval, ld->reconnect); + ld->alias, ld->config.channel_update_interval, ld->reconnect, + allow_localhost); subd_send_msg(ld->gossip, msg); } diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 2ce639c4e4a3..5a297563bfea 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -51,6 +51,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_debug_subdaemon = NULL; ld->dev_disconnect_fd = -1; ld->dev_subdaemon_fail = false; + ld->dev_allow_localhost = false; if (getenv("LIGHTNINGD_DEV_MEMLEAK")) memleak_init(ld, backtrace_state); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 311726222290..d711a58b6129 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -189,6 +189,9 @@ struct lightningd { /* If we have --dev-fail-on-subdaemon-fail */ bool dev_subdaemon_fail; + /* Allow and accept localhost node_announcement addresses */ + bool dev_allow_localhost; + /* Things we've marked as not leaking. */ const void **notleaks; #endif /* DEVELOPER */ diff --git a/lightningd/options.c b/lightningd/options.c index a9ec6fc9e00d..0f83e1cb1657 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -415,6 +415,9 @@ static void dev_register_opts(struct lightningd *ld) "Time between gossip broadcasts in milliseconds"); opt_register_arg("--dev-disconnect=", opt_subd_dev_disconnect, NULL, ld, "File containing disconnection points"); + opt_register_noarg("--dev-allow-localhost", opt_set_bool, + &ld->dev_allow_localhost, + "Announce and allow announcments for localhost address"); } #endif diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index a0807632ca2f..12db1fd5c181 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -664,6 +664,7 @@ def test_connect(self): "Cryptographic handshake: ", l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port) + @unittest.skipIf(not DEVELOPER, "needs --dev-allow-localhost") def test_connect_by_gossip(self): """Test connecting to an unknown peer using node gossip """ @@ -4284,10 +4285,13 @@ def test_pay_disconnect(self): def test_address(self): l1 = self.node_factory.get_node() addr = l1.rpc.getinfo()['address'] - assert len(addr) == 1 - assert addr[0]['type'] == 'ipv4' - assert addr[0]['address'] == '127.0.0.1' - assert int(addr[0]['port']) == l1.port + if 'dev-allow-localhost' in l1.daemon.opts: + assert len(addr) == 1 + assert addr[0]['type'] == 'ipv4' + assert addr[0]['address'] == '127.0.0.1' + assert int(addr[0]['port']) == l1.port + else: + assert len(addr) == 0 bind = l1.rpc.getinfo()['binding'] assert len(bind) == 1 diff --git a/tests/utils.py b/tests/utils.py index 29a9fde2920b..d4441de13595 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -261,9 +261,7 @@ def __init__(self, lightning_dir, bitcoin_dir, port=9735, random_hsm=False, node opts = { 'bitcoin-datadir': bitcoin_dir, 'lightning-dir': lightning_dir, - 'bind-addr': '127.0.0.1:{}'.format(port), - # lightningd won't announce non-routable addresses by default. - 'announce-addr': '127.0.0.1:{}'.format(port), + 'addr': '127.0.0.1:{}'.format(port), 'allow-deprecated-apis': 'false', 'override-fee-rates': '15000/7500/1000', 'network': 'regtest', @@ -283,6 +281,8 @@ def __init__(self, lightning_dir, bitcoin_dir, port=9735, random_hsm=False, node f.write(seed) if DEVELOPER: self.opts['dev-broadcast-interval'] = 1000 + # lightningd won't announce non-routable addresses by default. + self.opts['dev-allow-localhost'] = None self.prefix = 'lightningd-%d' % (node_id) @property From 8089103b5dbf6ba4b60585926a61b0c5afb90016 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 May 2018 13:59:22 +0930 Subject: [PATCH 14/14] test_lightningd.py: remove gratuitous 'addr' option from test_connect_by_gossip. Signed-off-by: Rusty Russell --- tests/test_lightningd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 12db1fd5c181..c0251352cb87 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -670,7 +670,7 @@ def test_connect_by_gossip(self): """ l1 = self.node_factory.get_node() l2 = self.node_factory.get_node() - l3 = self.node_factory.get_node(options={"addr": "127.0.0.1"}) + l3 = self.node_factory.get_node() l2.rpc.connect(l3.info['id'], 'localhost', l3.port)