Files
lightning/lightningd/connect_control.c
Rusty Russell 3d3d2ef9af gossipd: remove connectd functionality, enable connectd.
This patch guts gossipd of all peer-related functionality, and hands
all the peer-related requests to channeld instead.

gossipd now gets the final announcable addresses in its init msg, since
it doesn't handle socket binding any more.

lightningd now actually starts connectd, and activates it.  The init
messages for both gossipd and connectd still contain redundant fields
which need cleaning up.

There are shims to handle the fact that connectd's wire messages are
still (mostly) gossipd messages.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-07-25 02:13:52 +00:00

341 lines
9.0 KiB
C

#include <bitcoin/pubkey.h>
#include <ccan/err/err.h>
#include <ccan/fdpass/fdpass.h>
#include <ccan/list/list.h>
#include <ccan/tal/str/str.h>
#include <common/features.h>
#include <common/wireaddr.h>
#include <connectd/gen_connect_wire.h>
#include <errno.h>
#include <gossipd/gen_gossip_wire.h>
#include <hsmd/capabilities.h>
#include <hsmd/gen_hsm_client_wire.h>
#include <lightningd/channel.h>
#include <lightningd/connect_control.h>
#include <lightningd/gossip_control.h>
#include <lightningd/json.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/jsonrpc_errors.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/opening_control.h>
#include <lightningd/param.h>
#include <lightningd/subd.h>
#include <wire/gen_peer_wire.h>
#include <wire/wire_sync.h>
struct connect {
struct list_node list;
struct pubkey id;
struct command *cmd;
};
static void destroy_connect(struct connect *c)
{
list_del(&c->list);
}
static struct connect *new_connect(struct lightningd *ld,
const struct pubkey *id,
struct command *cmd)
{
struct connect *c = tal(cmd, struct connect);
c->id = *id;
c->cmd = cmd;
list_add_tail(&ld->connects, &c->list);
tal_add_destructor(c, destroy_connect);
return c;
}
/* Finds first command which matches. */
static struct connect *find_connect(struct lightningd *ld,
const struct pubkey *id)
{
struct connect *i;
list_for_each(&ld->connects, i, list) {
if (pubkey_eq(&i->id, id))
return i;
}
return NULL;
}
void gossip_connect_result(struct lightningd *ld, const u8 *msg)
{
struct pubkey id;
bool connected;
char *err;
struct connect *c;
if (!fromwire_gossipctl_connect_to_peer_result(tmpctx, msg,
&id,
&connected,
&err))
fatal("Gossip gave bad GOSSIPCTL_CONNECT_TO_PEER_RESULT message %s",
tal_hex(msg, msg));
/* We can have multiple connect commands: complete them all */
while ((c = find_connect(ld, &id)) != NULL) {
if (connected) {
struct json_result *response = new_json_result(c->cmd);
json_object_start(response, NULL);
json_add_pubkey(response, "id", &id);
json_object_end(response);
command_success(c->cmd, response);
} else {
command_fail(c->cmd, LIGHTNINGD, "%s", err);
}
/* They delete themselves from list */
}
}
static void json_connect(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
const jsmntok_t *hosttok, *porttok;
jsmntok_t *idtok;
struct pubkey id;
char *id_str;
char *atptr;
char *ataddr = NULL;
const char *name;
struct wireaddr_internal addr;
u8 *msg;
const char *err_msg;
if (!param(cmd, buffer, params,
p_req("id", json_tok_tok, (const jsmntok_t **) &idtok),
p_opt_tok("host", &hosttok),
p_opt_tok("port", &porttok),
NULL))
return;
/* Check for id@addrport form */
id_str = tal_strndup(cmd, buffer + idtok->start,
idtok->end - idtok->start);
atptr = strchr(id_str, '@');
if (atptr) {
int atidx = atptr - id_str;
ataddr = tal_strdup(cmd, atptr + 1);
/* Cut id. */
idtok->end = idtok->start + atidx;
}
if (!json_tok_pubkey(buffer, idtok, &id)) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"id %.*s not valid",
idtok->end - idtok->start,
buffer + idtok->start);
return;
}
if (hosttok && ataddr) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Can't specify host as both xxx@yyy "
"and separate argument");
return;
}
/* Get parseable host if provided somehow */
if (hosttok)
name = tal_strndup(cmd, buffer + hosttok->start,
hosttok->end - hosttok->start);
else if (ataddr)
name = ataddr;
else
name = NULL;
/* Port without host name? */
if (porttok && !name) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Can't specify port without host");
return;
}
/* Was there parseable host name? */
if (name) {
u32 port;
/* Is there a port? */
if (porttok) {
if (!json_tok_number(buffer, porttok, &port) || !port) {
command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Port %.*s not valid",
porttok->end - porttok->start,
buffer + porttok->start);
return;
}
} else {
port = DEFAULT_PORT;
}
if (!parse_wireaddr_internal(name, &addr, port, false,
!cmd->ld->use_proxy_always
&& !cmd->ld->pure_tor_setup,
true,
&err_msg)) {
command_fail(cmd, LIGHTNINGD, "Host %s:%u not valid: %s",
name, port, err_msg ? err_msg : "port is 0");
return;
}
/* Tell it about the address. */
msg = towire_gossipctl_peer_addrhint(cmd, &id, &addr);
subd_send_msg(cmd->ld->connectd, take(msg));
}
/* If there isn't already a connect command, tell connectd */
if (!find_connect(cmd->ld, &id)) {
msg = towire_gossipctl_connect_to_peer(NULL, &id);
subd_send_msg(cmd->ld->connectd, take(msg));
}
/* Leave this here for gossip_connect_result */
new_connect(cmd->ld, &id, cmd);
command_still_pending(cmd);
}
static const struct json_command connect_command = {
"connect",
json_connect,
"Connect to {id} at {host} (which can end in ':port' if not default). "
"{id} can also be of the form id@host"
};
AUTODATA(json_command, &connect_command);
static void peer_please_disconnect(struct lightningd *ld, const u8 *msg)
{
struct pubkey id;
struct channel *c;
struct uncommitted_channel *uc;
if (!fromwire_connect_reconnected(msg, &id))
fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg));
c = active_channel_by_id(ld, &id, &uc);
if (uc)
kill_uncommitted_channel(uc, "Reconnected");
else if (c)
channel_fail_transient(c, "Reconnected");
}
static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds)
{
/* FIXME: Move away from gossip types */
if (fromwire_peektype(msg) == WIRE_CONNECT_RECONNECTED) {
peer_please_disconnect(connectd->ld, msg);
return 0;
}
return gossip_msg(connectd->ld->gossip, msg, fds);
}
static void connect_init_done(struct subd *connectd,
const u8 *reply,
const int *fds UNUSED,
void *unused UNUSED)
{
struct lightningd *ld = connectd->ld;
if (!fromwire_connectctl_init_reply(ld, reply,
&ld->binding,
&ld->announcable))
fatal("Bad connectctl_activate_reply: %s",
tal_hex(reply, reply));
/* Break out of loop, so we can begin */
io_break(connectd);
}
/* FIXME: This is a transition hack */
static const char *connect_and_gossip_wire_type_name(int e)
{
const char *name = connect_wire_type_name(e);
if (strstarts(name, "INVALID"))
name = gossip_wire_type_name(e);
return name;
}
int connectd_init(struct lightningd *ld)
{
int fds[2];
u8 *msg;
int hsmfd;
u64 capabilities = HSM_CAP_ECDH;
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
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
fatal("Could not socketpair for connectd<->gossipd");
msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, 0, capabilities);
if (!wire_sync_write(ld->hsm_fd, msg))
fatal("Could not write to HSM: %s", strerror(errno));
msg = wire_sync_read(tmpctx, ld->hsm_fd);
if (!fromwire_hsm_client_hsmfd_reply(msg))
fatal("Malformed hsmfd response: %s", tal_hex(msg, msg));
hsmfd = fdpass_recv(ld->hsm_fd);
if (hsmfd < 0)
fatal("Could not read fd from HSM: %s", strerror(errno));
ld->connectd = new_global_subd(ld, "lightning_connectd",
connect_and_gossip_wire_type_name, connectd_msg,
take(&hsmfd), take(&fds[1]), NULL);
if (!ld->connectd)
err(1, "Could not subdaemon connectd");
/* If no addr specified, hand wildcard to connectd */
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_connectctl_init(
tmpctx, ld->config.broadcast_interval,
&get_chainparams(ld)->genesis_blockhash, &ld->id,
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->proxyaddr, ld->use_proxy_always || ld->pure_tor_setup,
allow_localhost, ld->config.use_dns,
ld->tor_service_password ? ld->tor_service_password : "");
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
connect_init_done, NULL);
/* Wait for init_reply */
io_loop(NULL, NULL);
return fds[0];
}
static void connect_activate_done(struct subd *connectd,
const u8 *reply UNUSED,
const int *fds UNUSED,
void *unused UNUSED)
{
/* Break out of loop, so we can begin */
io_break(connectd);
}
void connectd_activate(struct lightningd *ld)
{
const u8 *msg = towire_connectctl_activate(NULL, ld->listen);
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
connect_activate_done, NULL);
/* Wait for activate_reply */
io_loop(NULL, NULL);
}