diff --git a/lightningd/Makefile b/lightningd/Makefile index 1b2eb9dbb..84f688e30 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -31,6 +31,7 @@ LIGHTNINGD_LIB_HEADERS := $(LIGHTNINGD_LIB_SRC:.c=.h) LIGHTNINGD_SRC := \ lightningd/hsm_control.c \ lightningd/lightningd.c \ + lightningd/peer_control.c \ lightningd/subdaemon.c LIGHTNINGD_OBJS := $(LIGHTNINGD_SRC:.c=.o) @@ -41,9 +42,10 @@ LIGHTNINGD_JSMN_HEADERS := daemon/jsmn/jsmn.h LIGHTNINGD_HEADERS := \ lightningd/hsm_control.h \ lightningd/lightningd.h \ + lightningd/peer_control.h \ lightningd/subdaemon.h -$(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_HEADERS) $(LIGHTNINGD_HSM_CONTROL_HEADERS) $(LIBBASE58_HEADERS) +$(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(CCAN_HEADERS) $(DAEMON_HEADERS) $(LIGHTNINGD_HSM_CONTROL_HEADERS) $(LIGHTNINGD_HANDSHAKE_CONTROL_HEADERS) $(LIBBASE58_HEADERS) include lightningd/hsm/Makefile include lightningd/handshake/Makefile @@ -59,7 +61,7 @@ check-whitespace: $(LIGHTNINGD_SRC:%=check-whitespace/%) $(LIGHTNINGD_HEADERS:%= check-lightningd-makefile: @if [ "`ls lightningd/*.h | grep -v lightningd/gen | tr '\012' ' '`" != "`echo $(LIGHTNINGD_HEADERS) ''`" ]; then echo LIGHTNINGD_HEADERS incorrect; exit 1; fi -lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a +lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIGHTNINGD_HANDSHAKE_CONTROL_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a clean: lightningd-clean diff --git a/lightningd/handshake/test/Makefile b/lightningd/handshake/test/Makefile index 59f51fec4..9ddd9a47c 100644 --- a/lightningd/handshake/test/Makefile +++ b/lightningd/handshake/test/Makefile @@ -8,9 +8,9 @@ LIGHTNINGD_HANDSHAKE_TEST_PROGRAMS := $(LIGHTNINGD_HANDSHAKE_TEST_OBJS:.o=) update-mocks: $(LIGHTNINGD_HANDSHAKE_TEST_SRC:%=update-mocks/%) -$(LIGHTNINGD_HANDSHAKE_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a utils.o $(LIGHTNINGD_HANDSHAKE_GEN_SRC:.c=.o) +$(LIGHTNINGD_HANDSHAKE_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(CORE_OBJS) $(WIRE_OBJS) $(LIBBASE58_OBJS) $(LIGHTNINGD_LIB_OBJS) libsecp256k1.a utils.o $(LIGHTNINGD_HANDSHAKE_GEN_SRC:.c=.o) -$(LIGHTNINGD_HANDSHAKE_TEST_OBJS): $(LIGHTNINGD_HANDSHAKE_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(WIRE_HEADERS) $(CCAN_HEADERS) $(LIBBASE58_HEADERS) +$(LIGHTNINGD_HANDSHAKE_TEST_OBJS): $(LIGHTNINGD_HANDSHAKE_HEADERS) $(LIGHTNINGD_LIB_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(GEN_HEADERS) $(WIRE_HEADERS) $(CCAN_HEADERS) $(LIBBASE58_HEADERS) lightningd/handshake-tests: $(LIGHTNINGD_HANDSHAKE_TEST_PROGRAMS:%=unittest/%) diff --git a/lightningd/handshake/test/run-handshake.c b/lightningd/handshake/test/run-handshake.c index bfb1d53ed..15b4ad553 100644 --- a/lightningd/handshake/test/run-handshake.c +++ b/lightningd/handshake/test/run-handshake.c @@ -67,13 +67,6 @@ bool hsm_do_ecdh(struct sha256 *ss, const struct pubkey *point) privkey.secret) == 1; } -char *type_to_string_(const tal_t *ctx, const char *typename, - union printable_types u) -{ - assert(streq(typename, "struct pubkey")); - return pubkey_to_hexstr(ctx, u.pubkey); -} - int main(void) { int fds1[2], fds2[2]; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index a323a05b5..331fc44f8 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1,9 +1,12 @@ #include "hsm_control.h" #include "lightningd.h" +#include "peer_control.h" #include "subdaemon.h" #include #include +#include #include +#include #include #include #include @@ -66,6 +69,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) { struct lightningd *ld = tal(ctx, struct lightningd); + list_head_init(&ld->peers); ld->dstate.log_book = new_log_book(&ld->dstate, 20*1024*1024,LOG_INFORM); ld->log = ld->dstate.base_log = new_log(&ld->dstate, ld->dstate.log_book, @@ -170,6 +174,9 @@ int main(int argc, char *argv[]) /* Create RPC socket (if any) */ setup_jsonrpc(&ld->dstate, ld->dstate.rpc_filename); + /* Ready for connections from peers. */ + setup_listeners(ld); + #if 0 /* Initialize block topology. */ setup_topology(dstate); @@ -177,8 +184,6 @@ int main(int argc, char *argv[]) /* Load peers from database. */ db_load_peers(dstate); - /* Ready for connections from peers. */ - setup_listeners(dstate); #endif for (;;) { diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 280dccbdb..fc75d8ae8 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -23,5 +23,8 @@ struct lightningd { /* Bearer of all my secrets. */ struct subdaemon *hsm; + + /* All peers we're tracking. */ + struct list_head peers; }; #endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c new file mode 100644 index 000000000..47f334cdd --- /dev/null +++ b/lightningd/peer_control.c @@ -0,0 +1,236 @@ +#include "lightningd.h" +#include "peer_control.h" +#include "subdaemon.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct peer { + struct lightningd *ld; + + /* Inside ld->peers. */ + struct list_node list; + + /* What stage is this in? */ + struct subdaemon *owner; + + /* ID of peer (NULL before initial handshake). */ + struct pubkey *id; + + /* Our fd to the peer. */ + int fd; + + /* HSM connection for this peer. */ + int hsmfd; +}; + +static void destroy_peer(struct peer *peer) +{ + list_del_from(&peer->ld->peers, &peer->list); + if (peer->fd >= 0) + close(peer->fd); +} + +static struct peer *new_peer(const tal_t *ctx, struct lightningd *ld, int fd) +{ + struct peer *peer = tal(ctx, struct peer); + peer->ld = ld; + peer->owner = NULL; + peer->id = NULL; + peer->fd = fd; + list_add_tail(&ld->peers, &peer->list); + tal_add_destructor(peer, destroy_peer); + return peer; +} + +static void handshake_responder_succeeded(struct subdaemon *hs, const u8 *msg, + struct peer *peer) +{ + struct crypto_state *cs; + struct pubkey id; + + if (!fromwire_handshake_responder_resp(msg, msg, NULL, &id, &cs)) { + log_broken(hs->log, "Malformed responder resp: %s", + tal_hex(peer, msg)); + tal_free(peer); + return; + } + + /* FIXME: Look for peer duplicates! */ + + /* Peer is now a full-fledged citizen. */ + peer->id = tal_dup(peer, struct pubkey, &id); + + log_info_struct(hs->log, "Peer in from %s", struct pubkey, peer->id); + + /* Tell handshaked to exit. */ + subdaemon_req(peer->owner, take(towire_handshake_exit_req(msg)), + -1, NULL, NULL, NULL); + + /* FIXME: start lightningd_connect */ + peer->owner = NULL; +} + +static void peer_got_hsmfd(struct subdaemon *hsm, const u8 *msg, + struct peer *peer) +{ + if (!fromwire_hsmctl_hsmfd_ecdh_response(msg, NULL)) { + log_unusual(peer->ld->log, "Malformed hsmfd response: %s", + tal_hex(peer, msg)); + goto error; + } + + /* Give handshake daemon the hsm fd. */ + peer->owner = new_subdaemon(peer, peer->ld, + "lightningd_handshake", + handshake_status_wire_type_name, + handshake_control_wire_type_name, + NULL, + peer->hsmfd, -1); + if (!peer->owner) { + log_unusual(peer->ld->log, "Could not subdaemon handshake: %s", + strerror(errno)); + goto error; + } + + /* Now handshake owns peer: until it succeeds, peer vanishes + * when it does. */ + tal_steal(peer->owner, peer); + + /* Now hand peer fd to the handshake daemon, it hand back on success */ + subdaemon_req(peer->owner, + take(towire_handshake_responder_req(peer, + &peer->ld->dstate.id)), + peer->fd, &peer->fd, + handshake_responder_succeeded, peer); + + /* Peer struct longer owns fd. */ + peer->fd = -1; + return; + +error: + tal_free(peer); +} + +/* FIXME: timeout handshake if taking too long? */ +static struct io_plan *peer_in(struct io_conn *conn, struct lightningd *ld) +{ + struct peer *peer = new_peer(ld, ld, io_conn_fd(conn)); + + /* Get HSM fd for this peer. */ + /* FIXME: We use pointer as ID. */ + subdaemon_req(ld->hsm, take(towire_hsmctl_hsmfd_ecdh(ld, (u64)peer)), + -1, &peer->hsmfd, peer_got_hsmfd, peer); + + /* We don't need conn, we'll pass fd to handshaked. */ + return io_close_taken_fd(conn); +} + +static int make_listen_fd(struct lightningd *ld, + int domain, void *addr, socklen_t len) +{ + int fd = socket(domain, SOCK_STREAM, 0); + if (fd < 0) { + log_debug(ld->log, "Failed to create %u socket: %s", + domain, strerror(errno)); + return -1; + } + + if (addr) { + int on = 1; + + /* Re-use, please.. */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + log_unusual(ld->log, "Failed setting socket reuse: %s", + strerror(errno)); + + if (bind(fd, addr, len) != 0) { + log_unusual(ld->log, "Failed to bind on %u socket: %s", + domain, strerror(errno)); + goto fail; + } + } + + if (listen(fd, 5) != 0) { + log_unusual(ld->log, "Failed to listen on %u socket: %s", + domain, strerror(errno)); + goto fail; + } + return fd; + +fail: + close_noerr(fd); + return -1; +} + +void setup_listeners(struct lightningd *ld) +{ + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + socklen_t len; + int fd1, fd2; + + if (!ld->dstate.portnum) { + log_debug(ld->log, "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(ld->dstate.portnum); + + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr = in6addr_any; + addr6.sin6_port = htons(ld->dstate.portnum); + + /* IPv6, since on Linux that (usually) binds to IPv4 too. */ + fd1 = make_listen_fd(ld, AF_INET6, &addr6, sizeof(addr6)); + if (fd1 >= 0) { + struct sockaddr_in6 in6; + + len = sizeof(in6); + if (getsockname(fd1, (void *)&in6, &len) != 0) { + log_unusual(ld->log, "Failed get IPv6 sockname: %s", + strerror(errno)); + close_noerr(fd1); + fd1 = -1; + } else { + addr.sin_port = in6.sin6_port; + assert(ld->dstate.portnum == ntohs(addr.sin_port)); + log_debug(ld->log, "Creating IPv6 listener on port %u", + ld->dstate.portnum); + io_new_listener(ld, fd1, peer_in, ld); + } + } + + /* Just in case, aim for the same port... */ + fd2 = make_listen_fd(ld, AF_INET, &addr, sizeof(addr)); + if (fd2 >= 0) { + len = sizeof(addr); + if (getsockname(fd2, (void *)&addr, &len) != 0) { + log_unusual(ld->log, "Failed get IPv4 sockname: %s", + strerror(errno)); + close_noerr(fd2); + fd2 = -1; + } else { + assert(ld->dstate.portnum == ntohs(addr.sin_port)); + log_debug(ld->log, "Creating IPv4 listener on port %u", + ld->dstate.portnum); + io_new_listener(ld, fd2, peer_in, ld); + } + } + + if (fd1 < 0 && fd2 < 0) + fatal("Could not bind to a network address on port %u", + ld->dstate.portnum); +} diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h new file mode 100644 index 000000000..aa5a863e3 --- /dev/null +++ b/lightningd/peer_control.h @@ -0,0 +1,9 @@ +#ifndef LIGHTNING_LIGHTNINGD_PEER_CONTROL_H +#define LIGHTNING_LIGHTNINGD_PEER_CONTROL_H +#include "config.h" +#include + +struct lightningd; + +void setup_listeners(struct lightningd *ld); +#endif /* LIGHTNING_LIGHTNINGD_PEER_CONTROL_H */