|
|
|
#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/json_command.h>
|
|
|
|
#include <common/json_helpers.h>
|
|
|
|
#include <common/jsonrpc_errors.h>
|
|
|
|
#include <common/memleak.h>
|
|
|
|
#include <common/node_id.h>
|
|
|
|
#include <common/param.h>
|
|
|
|
#include <common/pseudorand.h>
|
|
|
|
#include <common/timeout.h>
|
|
|
|
#include <common/wireaddr.h>
|
|
|
|
#include <connectd/gen_connect_wire.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <hsmd/capabilities.h>
|
|
|
|
#include <hsmd/gen_hsm_wire.h>
|
|
|
|
#include <lightningd/channel.h>
|
|
|
|
#include <lightningd/connect_control.h>
|
|
|
|
#include <lightningd/hsm_control.h>
|
|
|
|
#include <lightningd/json.h>
|
|
|
|
#include <lightningd/json_stream.h>
|
|
|
|
#include <lightningd/jsonrpc.h>
|
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/log.h>
|
|
|
|
#include <lightningd/opening_control.h>
|
|
|
|
#include <lightningd/peer_control.h>
|
|
|
|
#include <lightningd/subd.h>
|
|
|
|
#include <wire/gen_peer_wire.h>
|
|
|
|
#include <wire/wire_sync.h>
|
|
|
|
|
|
|
|
struct connect {
|
|
|
|
struct list_node list;
|
|
|
|
struct node_id 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 node_id *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 node_id *id)
|
|
|
|
{
|
|
|
|
struct connect *i;
|
|
|
|
|
|
|
|
list_for_each(&ld->connects, i, list) {
|
|
|
|
if (node_id_eq(&i->id, id))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *connect_cmd_succeed(struct command *cmd,
|
|
|
|
const struct node_id *id)
|
|
|
|
{
|
|
|
|
struct json_stream *response = json_stream_success(cmd);
|
|
|
|
json_add_node_id(response, "id", id);
|
|
|
|
return command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *json_connect(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
u32 *port;
|
|
|
|
jsmntok_t *idtok;
|
|
|
|
struct node_id id;
|
|
|
|
char *id_str;
|
|
|
|
char *atptr;
|
|
|
|
char *ataddr = NULL;
|
|
|
|
const char *name;
|
|
|
|
struct wireaddr_internal *addr;
|
|
|
|
u8 *msg;
|
|
|
|
const char *err_msg;
|
|
|
|
struct peer *peer;
|
|
|
|
|
|
|
|
if (!param(cmd, buffer, params,
|
|
|
|
p_req("id", param_tok, (const jsmntok_t **) &idtok),
|
|
|
|
p_opt("host", param_string, &name),
|
|
|
|
p_opt("port", param_number, &port),
|
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
|
|
|
/* Check for id@addrport form */
|
|
|
|
id_str = json_strdup(cmd, buffer, idtok);
|
|
|
|
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_to_node_id(buffer, idtok, &id)) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"id %.*s not valid",
|
|
|
|
json_tok_full_len(idtok),
|
|
|
|
json_tok_full(buffer, idtok));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name && ataddr) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can't specify host as both xxx@yyy "
|
|
|
|
"and separate argument");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get parseable host if provided somehow */
|
|
|
|
if (!name && ataddr)
|
|
|
|
name = ataddr;
|
|
|
|
|
|
|
|
/* Port without host name? */
|
|
|
|
if (port && !name) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can't specify port without host");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we know about peer, see if it's already connected. */
|
|
|
|
peer = peer_by_id(cmd->ld, &id);
|
|
|
|
if (peer) {
|
|
|
|
struct channel *channel = peer_active_channel(peer);
|
|
|
|
|
|
|
|
if (peer->uncommitted_channel
|
|
|
|
|| (channel && channel->connected)) {
|
|
|
|
return connect_cmd_succeed(cmd, &id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Was there parseable host name? */
|
|
|
|
if (name) {
|
|
|
|
/* Is there a port? */
|
|
|
|
if (!port) {
|
|
|
|
port = tal(cmd, u32);
|
|
|
|
*port = DEFAULT_PORT;
|
|
|
|
}
|
|
|
|
addr = tal(cmd, struct wireaddr_internal);
|
|
|
|
if (!parse_wireaddr_internal(name, addr, *port, false,
|
|
|
|
!cmd->ld->use_proxy_always
|
|
|
|
&& !cmd->ld->pure_tor_setup,
|
|
|
|
true,
|
|
|
|
&err_msg)) {
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Host %s:%u not valid: %s",
|
|
|
|
name, *port,
|
|
|
|
err_msg ? err_msg : "port is 0");
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
addr = NULL;
|
|
|
|
|
|
|
|
msg = towire_connectctl_connect_to_peer(NULL, &id, 0, addr);
|
|
|
|
subd_send_msg(cmd->ld->connectd, take(msg));
|
|
|
|
|
|
|
|
/* Leave this here for peer_connected or connect_failed. */
|
|
|
|
new_connect(cmd->ld, &id, cmd);
|
|
|
|
return command_still_pending(cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command connect_command = {
|
|
|
|
"connect",
|
|
|
|
"network",
|
|
|
|
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);
|
|
|
|
|
|
|
|
struct delayed_reconnect {
|
|
|
|
struct channel *channel;
|
|
|
|
u32 seconds_delayed;
|
|
|
|
struct wireaddr_internal *addrhint;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void maybe_reconnect(struct delayed_reconnect *d)
|
|
|
|
{
|
|
|
|
struct peer *peer = d->channel->peer;
|
|
|
|
|
|
|
|
/* Might have gone onchain since we started timer. */
|
|
|
|
if (channel_active(d->channel)) {
|
|
|
|
u8 *msg = towire_connectctl_connect_to_peer(NULL, &peer->id,
|
|
|
|
d->seconds_delayed,
|
|
|
|
d->addrhint);
|
|
|
|
subd_send_msg(peer->ld->connectd, take(msg));
|
|
|
|
}
|
|
|
|
tal_free(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
void delay_then_reconnect(struct channel *channel, u32 seconds_delay,
|
|
|
|
const struct wireaddr_internal *addrhint)
|
|
|
|
{
|
|
|
|
struct delayed_reconnect *d;
|
|
|
|
struct lightningd *ld = channel->peer->ld;
|
|
|
|
|
|
|
|
if (!ld->reconnect)
|
|
|
|
return;
|
|
|
|
|
|
|
|
d = tal(channel, struct delayed_reconnect);
|
|
|
|
d->channel = channel;
|
|
|
|
d->seconds_delayed = seconds_delay;
|
|
|
|
if (addrhint)
|
|
|
|
d->addrhint = tal_dup(d, struct wireaddr_internal, addrhint);
|
|
|
|
else
|
|
|
|
d->addrhint = NULL;
|
|
|
|
|
|
|
|
log_debug(channel->log, "Will try reconnect in %u seconds",
|
|
|
|
seconds_delay);
|
|
|
|
|
|
|
|
/* We fuzz the timer by up to 1 second, to avoid getting into
|
|
|
|
* simultanous-reconnect deadlocks with peer. */
|
|
|
|
notleak(new_reltimer(ld->timers, d,
|
|
|
|
timerel_add(time_from_sec(seconds_delay),
|
|
|
|
time_from_usec(pseudorand(1000000))),
|
|
|
|
maybe_reconnect, d));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void connect_failed(struct lightningd *ld, const u8 *msg)
|
|
|
|
{
|
|
|
|
struct node_id id;
|
|
|
|
char *err;
|
|
|
|
struct connect *c;
|
|
|
|
u32 seconds_to_delay;
|
|
|
|
struct wireaddr_internal *addrhint;
|
|
|
|
struct channel *channel;
|
|
|
|
|
|
|
|
if (!fromwire_connectctl_connect_failed(tmpctx, msg, &id, &err,
|
|
|
|
&seconds_to_delay, &addrhint))
|
|
|
|
fatal("Connect gave bad CONNECTCTL_CONNECT_FAILED message %s",
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
|
|
|
/* We can have multiple connect commands: fail them all */
|
|
|
|
while ((c = find_connect(ld, &id)) != NULL) {
|
|
|
|
/* They delete themselves from list */
|
|
|
|
was_pending(command_fail(c->cmd, LIGHTNINGD, "%s", err));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have an active channel, then reconnect. */
|
|
|
|
channel = active_channel_by_id(ld, &id, NULL);
|
|
|
|
if (channel)
|
|
|
|
delay_then_reconnect(channel, seconds_to_delay, addrhint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect_succeeded(struct lightningd *ld, const struct node_id *id)
|
|
|
|
{
|
|
|
|
struct connect *c;
|
|
|
|
|
|
|
|
/* We can have multiple connect commands: fail them all */
|
|
|
|
while ((c = find_connect(ld, id)) != NULL) {
|
|
|
|
/* They delete themselves from list */
|
|
|
|
connect_cmd_succeed(c->cmd, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void peer_please_disconnect(struct lightningd *ld, const u8 *msg)
|
|
|
|
{
|
|
|
|
struct node_id 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_reconnect(c, "Reconnected");
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds)
|
|
|
|
{
|
|
|
|
enum connect_wire_type t = fromwire_peektype(msg);
|
|
|
|
|
|
|
|
switch (t) {
|
|
|
|
/* These are messages we send, not them. */
|
|
|
|
case WIRE_CONNECTCTL_INIT:
|
|
|
|
case WIRE_CONNECTCTL_ACTIVATE:
|
|
|
|
case WIRE_CONNECTCTL_CONNECT_TO_PEER:
|
|
|
|
case WIRE_CONNECTCTL_PEER_DISCONNECTED:
|
|
|
|
case WIRE_CONNECT_DEV_MEMLEAK:
|
|
|
|
/* This is a reply, so never gets through to here. */
|
|
|
|
case WIRE_CONNECTCTL_INIT_REPLY:
|
|
|
|
case WIRE_CONNECTCTL_ACTIVATE_REPLY:
|
|
|
|
case WIRE_CONNECT_DEV_MEMLEAK_REPLY:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIRE_CONNECT_RECONNECTED:
|
|
|
|
peer_please_disconnect(connectd->ld, msg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIRE_CONNECT_PEER_CONNECTED:
|
|
|
|
if (tal_count(fds) != 3)
|
|
|
|
return 3;
|
|
|
|
peer_connected(connectd->ld, msg, fds[0], fds[1], fds[2]);
|
|
|
|
break;
|
openingd: take peer before we're opening, wait for explicit funding msg.
Prior to this, lightningd would hand uninteresting peers back to connectd,
which would then return it to lightningd if it sent a non-gossip msg,
or if lightningd asked it to release the peer.
Now connectd hands the peer to lightningd once we've done the init
handshake, which hands it off to openingd.
This is a deep structural change, so we do the minimum here and cleanup
in the following patches.
Lightningd:
1. Remove peer_nongossip handling from connect_control and peer_control.
2. Remove list of outstanding fundchannel command; it was only needed to
find the race between us asking connectd to release the peer and it
reconnecting.
3. We can no longer tell if the remote end has started trying to fund a
channel (until it has succeeded): it's very transitory anyway so not
worth fixing.
4. We now always have a struct peer, and allocate an uncommitted_channel
for it, though it may never be used if neither end funds a channel.
5. We start funding on messages for openingd: we can get a funder_reply
or a fundee, or an error in response to our request to fund a channel.
so we handle all of them.
6. A new peer_start_openingd() is called after connectd hands us a peer.
7. json_fund_channel just looks through local peers; there are none
hidden in connectd any more.
8. We sometimes start a new openingd just to send an error message.
Openingd:
1. We always have information we need to accept them funding a channel (in
the init message).
2. We have to listen for three fds: peer, gossip and master, so we opencode
the poll.
3. We have an explicit message to start trying to fund a channel.
4. We can be told to send a message in our init message.
Testing:
1. We don't handle some things gracefully yet, so two tests are disabled.
2. 'hand_back_peer .*: now local again' from connectd is no longer a message,
openingd says 'Handed peer, entering loop' once its managing it.
3. peer['state'] used to be set to 'GOSSIPING' (otherwise this field doesn't
exist; 'state' is now per-channel. It doesn't exist at all now.
4. Some tests now need to turn on IO logging in openingd, not connectd.
5. There's a gap between connecting on one node and having connectd on
the peer hand over the connection to openingd. Our tests sometimes
checked getpeers() on the peer, and didn't see anything, so line_graph
needed updating.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
6 years ago
|
|
|
|
|
|
|
case WIRE_CONNECTCTL_CONNECT_FAILED:
|
|
|
|
connect_failed(connectd->ld, msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
int connectd_init(struct lightningd *ld)
|
|
|
|
{
|
|
|
|
int fds[2];
|
|
|
|
u8 *msg;
|
|
|
|
int hsmfd;
|
|
|
|
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
|
|
|
|
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
|
|
|
|
|
|
|
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
|
|
|
fatal("Could not socketpair for connectd<->gossipd");
|
|
|
|
|
|
|
|
hsmfd = hsm_get_global_fd(ld, HSM_CAP_ECDH);
|
|
|
|
|
|
|
|
ld->connectd = new_global_subd(ld, "lightning_connectd",
|
|
|
|
connect_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->id,
|
|
|
|
wireaddrs,
|
|
|
|
listen_announce,
|
|
|
|
ld->proxyaddr, ld->use_proxy_always || ld->pure_tor_setup,
|
|
|
|
IFDEV(ld->dev_allow_localhost, false), ld->config.use_dns,
|
|
|
|
ld->tor_service_password ? ld->tor_service_password : "",
|
|
|
|
ld->config.use_v3_autotor);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|