#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 peer *peer) { struct json_stream *response = json_stream_success(cmd); json_add_node_id(response, "id", &peer->id); json_add_hex_talarr(response, "features", peer->their_features); 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, peer); } } /* 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; errcode_t errcode; char *errmsg; struct connect *c; u32 seconds_to_delay; struct wireaddr_internal *addrhint; struct channel *channel; if (!fromwire_connectctl_connect_failed(tmpctx, msg, &id, &errcode, &errmsg, &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, errcode, "%s", errmsg)); } /* 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 peer *peer) { struct connect *c; /* We can have multiple connect commands: fail them all */ while ((c = find_connect(ld, &peer->id)) != NULL) { /* They delete themselves from list */ connect_cmd_succeed(c->cmd, peer); } } 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; 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, chainparams, ld->our_features, &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); }