Browse Source

gossipwith: simple tool to snarf gossip from a node.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
json-streaming
Rusty Russell 6 years ago
committed by Christian Decker
parent
commit
0925daa087
  1. 1
      devtools/.gitignore
  2. 11
      devtools/Makefile
  3. 220
      devtools/gossipwith.c
  4. 25
      tests/test_gossip.py

1
devtools/.gitignore

@ -2,3 +2,4 @@ bolt11-cli
decodemsg
onion
dump-gossipstore
gossipwith

11
devtools/Makefile

@ -1,6 +1,7 @@
DEVTOOLS_SRC := devtools/gen_print_wire.c devtools/gen_print_onion_wire.c devtools/print_wire.c
DEVTOOLS_OBJS := $(DEVTOOLS_SRC:.c=.o)
DEVTOOLS_TOOL_SRC := devtools/bolt11-cli.c devtools/decodemsg.c devtools/onion.c devtools/dump-gossipstore.c
DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith
DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c)
DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o)
DEVTOOLS_COMMON_OBJS := \
@ -15,7 +16,7 @@ DEVTOOLS_COMMON_OBJS := \
common/version.o \
common/wireaddr.o
devtools-all: devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore
devtools-all: $(DEVTOOLS)
devtools/gen_print_wire.h: $(WIRE_GEN) wire/gen_peer_wire_csv
$(WIRE_GEN) --bolt --printwire --header $@ wire_type < wire/gen_peer_wire_csv > $@
@ -40,12 +41,16 @@ devtools/onion.c: ccan/config.h
devtools/onion: $(DEVTOOLS_OBJS) $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/onion.o common/sphinx.o
devtools/gossipwith: $(DEVTOOLS_OBJS) $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/gen_peer_wire.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o common/crypto_sync.o
$(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS): wire/wire.h devtools/gen_print_wire.h devtools/gen_print_onion_wire.h
devtools/gen_print_wire.o: devtools/gen_print_wire.h wire/gen_peer_wire.h devtools/print_wire.h
devtools/gen_print_onion_wire.o: devtools/gen_print_onion_wire.h devtools/print_wire.h
devtools/bolt11-cli: $(DEVTOOLS_OBJS) $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o
# Make sure these depend on everything.
ALL_PROGRAMS += devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore
ALL_PROGRAMS += $(DEVTOOLS)
ALL_OBJS += $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS)
check-source: $(DEVTOOLS_SRC:%=check-src-include-order/%) $(DEVTOOLS_TOOLS_SRC:%=check-src-include-order/%)

220
devtools/gossipwith.c

@ -0,0 +1,220 @@
/* Simple tool to route gossip from a peer. */
#include <ccan/err/err.h>
#include <ccan/io/io.h>
#include <ccan/opt/opt.h>
#include <ccan/read_write_all/read_write_all.h>
#include <common/crypto_sync.h>
#include <common/dev_disconnect.h>
#include <common/peer_failed.h>
#include <common/status.h>
#include <netdb.h>
#include <secp256k1_ecdh.h>
#include <wire/peer_wire.h>
#define io_write_ simple_write
#define io_read_ simple_read
static struct io_plan *simple_write(struct io_conn *conn,
const void *data, size_t len,
struct io_plan *(*next)(struct io_conn *, void *),
void *arg);
static struct io_plan *simple_read(struct io_conn *conn,
void *data, size_t len,
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg);
#include "../connectd/handshake.c"
/* This makes the handshake prototypes work. */
struct io_conn {
int fd;
};
static struct secret notsosecret;
static bool initial_sync = false;
static unsigned long max_messages = -1UL;
/* Empty stubs to make us compile */
void status_peer_io(enum log_level iodir, const u8 *p)
{
}
void status_fmt(enum log_level level, const char *fmt, ...)
{
}
#if DEVELOPER
void dev_sabotage_fd(int fd)
{
abort();
}
void dev_blackhole_fd(int fd)
{
abort();
}
enum dev_disconnect dev_disconnect(int pkt_type)
{
return DEV_DISCONNECT_NORMAL;
}
#endif
void peer_failed_connection_lost(void)
{
exit(0);
}
bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point)
{
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
notsosecret.data) != 1)
errx(1, "ECDH failed");
return true;
}
/* We don't want to discard *any* messages. */
bool is_unknown_msg_discardable(const u8 *cursor)
{
return false;
}
static struct io_plan *simple_write(struct io_conn *conn,
const void *data, size_t len,
struct io_plan *(*next)(struct io_conn *, void *),
void *arg)
{
if (!write_all(conn->fd, data, len))
err(1, "Writing data");
return next(conn, arg);
}
static struct io_plan *simple_read(struct io_conn *conn,
void *data, size_t len,
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg)
{
if (!read_all(conn->fd, data, len))
err(1, "Reading data");
return next(conn, next_arg);
}
static struct io_plan *handshake_success(struct io_conn *conn,
const struct pubkey *them,
const struct wireaddr_internal *addr,
const struct crypto_state *orig_cs,
void *unused)
{
u8 *msg;
struct crypto_state cs = *orig_cs;
u8 *local_features;
if (initial_sync) {
local_features = tal(conn, u8);
local_features[0] = (1 << 3);
} else
local_features = NULL;
msg = towire_init(NULL, NULL, local_features);
sync_crypto_write(&cs, conn->fd, take(msg));
/* Ignore their init message. */
tal_free(sync_crypto_read(NULL, &cs, conn->fd));
/* Now write out whatever we get. */
while ((msg = sync_crypto_read(NULL, &cs, conn->fd)) != NULL) {
be16 len = cpu_to_be16(tal_bytelen(msg));
if (!write_all(STDOUT_FILENO, &len, sizeof(len))
|| !write_all(STDOUT_FILENO, msg, tal_bytelen(msg)))
err(1, "Writing out msg");
tal_free(msg);
if (--max_messages == 0)
exit(0);
}
err(1, "Reading msg");
}
int main(int argc, char *argv[])
{
struct io_conn *conn = tal(NULL, struct io_conn);
struct wireaddr_internal addr;
int af;
struct pubkey us, them;
const char *err_msg;
const char *at;
struct addrinfo *ai;
setup_locale();
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY |
SECP256K1_CONTEXT_SIGN);
opt_register_noarg("--initial-sync", opt_set_bool, &initial_sync,
"Stream complete gossip history at start");
opt_register_arg("--max-messages", opt_set_ulongval, opt_show_ulongval,
&max_messages,
"Terminate after reading this many messages (> 0)");
opt_register_noarg("--help|-h", opt_usage_and_exit,
"id@addr[:port]\n"
"Connect to a lightning peer and relay gossip messages from it",
"Print this message.");
opt_parse(&argc, argv, opt_log_stderr_exit);
if (argc != 2)
opt_usage_exit_fail("Need an id@addr to connect to");
at = strchr(argv[1], '@');
if (!at)
opt_usage_exit_fail("Need id@addr");
if (!pubkey_from_hexstr(argv[1], at - argv[1], &them))
opt_usage_exit_fail("Invalid id %.*s",
(int)(at - argv[1]), argv[1]);
if (!parse_wireaddr_internal(at+1, &addr, DEFAULT_PORT, NULL,
true, false, &err_msg))
opt_usage_exit_fail("%s '%s'", err_msg, argv[1]);
switch (addr.itype) {
case ADDR_INTERNAL_SOCKNAME:
af = AF_LOCAL;
ai = wireaddr_internal_to_addrinfo(conn, &addr);
break;
case ADDR_INTERNAL_ALLPROTO:
case ADDR_INTERNAL_AUTOTOR:
case ADDR_INTERNAL_FORPROXY:
opt_usage_exit_fail("Don't support proxy use");
case ADDR_INTERNAL_WIREADDR:
switch (addr.u.wireaddr.type) {
case ADDR_TYPE_TOR_V2:
case ADDR_TYPE_TOR_V3:
opt_usage_exit_fail("Don't support proxy use");
break;
case ADDR_TYPE_IPV4:
af = AF_INET;
break;
case ADDR_TYPE_IPV6:
af = AF_INET6;
break;
case ADDR_TYPE_PADDING:
abort();
}
ai = wireaddr_to_addrinfo(tmpctx, &addr.u.wireaddr);
}
conn->fd = socket(af, SOCK_STREAM, 0);
if (conn->fd < 0)
err(1, "Creating socket");
memset(&notsosecret, 0x42, sizeof(notsosecret));
if (!pubkey_from_secret(&notsosecret, &us))
errx(1, "Creating pubkey");
if (connect(conn->fd, ai->ai_addr, ai->ai_addrlen) != 0)
err(1, "Connecting to %s", at+1);
initiator_handshake(conn, &us, &them, &addr, handshake_success, NULL);
exit(0);
}

25
tests/test_gossip.py

@ -1,9 +1,10 @@
from fixtures import * # noqa: F401,F403
from utils import wait_for
from utils import wait_for, TIMEOUT
import json
import logging
import os
import struct
import subprocess
import time
import unittest
@ -839,3 +840,25 @@ def test_gossip_store_load(node_factory):
# May preceed the Started msg waited for in 'start'.
wait_for(lambda: l1.daemon.is_in_log('gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store in 744 bytes'))
assert not l1.daemon.is_in_log('gossip_store.*truncating')
def test_gossipwith(node_factory):
l1, l2 = node_factory.line_graph(2, announce=True)
out = subprocess.run(['devtools/gossipwith',
'--initial-sync',
'--max-messages=5',
'{}@localhost:{}'.format(l1.info['id'], l1.port)],
check=True,
timeout=TIMEOUT, stdout=subprocess.PIPE).stdout
num_msgs = 0
while len(out):
l, t = struct.unpack('>HH', out[0:4])
# channel_announcement node_announcement or channel_update
assert t == 256 or t == 257 or t == 258
out = out[2 + l:]
num_msgs += 1
# one channel announcement, two channel_updates, two node announcements.
assert num_msgs == 5

Loading…
Cancel
Save