diff --git a/connectd/connectd.c b/connectd/connectd.c index 5248108f7..11d1cde73 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2174,6 +2174,10 @@ static struct io_plan *recv_req(struct io_conn *conn, set_custommsgs(daemon, msg); goto out; + case WIRE_CONNECTD_INJECT_ONIONMSG: + inject_onionmsg_req(daemon, msg); + goto out; + case WIRE_CONNECTD_DEV_MEMLEAK: if (daemon->developer) { dev_connect_memleak(daemon, msg); @@ -2216,6 +2220,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_CUSTOMMSG_IN: case WIRE_CONNECTD_PEER_DISCONNECT_DONE: case WIRE_CONNECTD_START_SHUTDOWN_REPLY: + case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY: break; } diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 6f4696f10..393af418f 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -144,6 +144,16 @@ msgdata,connectd_send_onionmsg,onion_len,u16, msgdata,connectd_send_onionmsg,onion,u8,onion_len msgdata,connectd_send_onionmsg,blinding,pubkey, +# Lightningd tells us to digest an onion message. +msgtype,connectd_inject_onionmsg,2042 +msgdata,connectd_inject_onionmsg,blinding,pubkey, +msgdata,connectd_inject_onionmsg,onion_len,u16, +msgdata,connectd_inject_onionmsg,onion,u8,onion_len + +# Reply. If error isn't empty, something went wrong. +msgtype,connectd_inject_onionmsg_reply,2142 +msgdata,connectd_inject_onionmsg_reply,err,wirestring, + # A custom message that we got from a peer and don't know how to handle, so we # forward it to the master for further handling. msgtype,connectd_custommsg_in,2110 diff --git a/connectd/onion_message.c b/connectd/onion_message.c index cfa4953ee..86a531c7e 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -1,6 +1,7 @@ /*~ This contains all the code to handle onion messages. */ #include "config.h" #include +#include #include #include #include @@ -35,12 +36,11 @@ void onionmsg_req(struct daemon *daemon, const u8 *msg) } } -/* Peer sends an onion msg. */ -void handle_onion_message(struct daemon *daemon, - struct peer *peer, const u8 *msg) +static const char *handle_onion(const tal_t *ctx, + struct daemon *daemon, + const struct pubkey *blinding, + const u8 *onion) { - struct pubkey blinding; - u8 *onion; u8 *next_onion_msg; struct pubkey next_node; struct tlv_onionmsg_tlv *final_om; @@ -48,26 +48,12 @@ void handle_onion_message(struct daemon *daemon, struct secret *final_path_id; const char *err; - /* Ignore unless explicitly turned on. */ - if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return; - - /* FIXME: ratelimit! */ - if (!fromwire_onion_message(msg, msg, &blinding, &onion)) { - inject_peer_msg(peer, - towire_warningfmt(NULL, NULL, - "Bad onion_message")); - return; - } - - err = onion_message_parse(tmpctx, onion, &blinding, + err = onion_message_parse(tmpctx, onion, blinding, &daemon->mykey, &next_onion_msg, &next_node, &final_om, &final_alias, &final_path_id); if (err) { - status_peer_debug(&peer->id, "%s", err); - return; + return tal_steal(ctx, err); } if (final_om) { @@ -91,12 +77,50 @@ void handle_onion_message(struct daemon *daemon, node_id_from_pubkey(&next_node_id, &next_node); next_peer = peer_htable_get(daemon->peers, &next_node_id); if (!next_peer) { - status_peer_debug(&peer->id, - "onion msg: unknown next peer %s", - fmt_pubkey(tmpctx, &next_node)); - return; + return tal_fmt(ctx, "onion msg: unknown next peer %s", + fmt_pubkey(tmpctx, &next_node)); } inject_peer_msg(next_peer, take(next_onion_msg)); } + return NULL; } + +/* Peer sends an onion msg, or (if peer NULL) lightningd injects one. */ +void handle_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg) +{ + struct pubkey blinding; + u8 *onion; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return; + + /* FIXME: ratelimit! */ + if (!fromwire_onion_message(msg, msg, &blinding, &onion)) { + inject_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); + return; + } + + handle_onion(tmpctx, daemon, &blinding, onion); +} + +void inject_onionmsg_req(struct daemon *daemon, const u8 *msg) +{ + u8 *onionmsg; + struct pubkey blinding; + const char *err; + + if (!fromwire_connectd_inject_onionmsg(msg, msg, &blinding, &onionmsg)) + master_badmsg(WIRE_CONNECTD_INJECT_ONIONMSG, msg); + + err = handle_onion(tmpctx, daemon, &blinding, onionmsg); + daemon_conn_send(daemon->master, + take(towire_connectd_inject_onionmsg_reply(NULL, err ? err : ""))); +} + + diff --git a/connectd/onion_message.h b/connectd/onion_message.h index 41b25982c..63bef338f 100644 --- a/connectd/onion_message.h +++ b/connectd/onion_message.h @@ -3,11 +3,14 @@ #include "config.h" #include -/* Onion message comes in from peer */ +/* Onion message comes in from peer. */ void handle_onion_message(struct daemon *daemon, struct peer *peer, const u8 *msg); /* Lightningd tells us to send an onion message */ void onionmsg_req(struct daemon *daemon, const u8 *msg); +/* Lightning tells us unwrap onion message as if from peer */ +void inject_onionmsg_req(struct daemon *daemon, const u8 *msg); + #endif /* LIGHTNING_CONNECTD_ONION_MESSAGE_H */ diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 08973b2a0..3169fdfa4 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -608,6 +608,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_PEER_CONNECT_SUBD: case WIRE_CONNECTD_PING: case WIRE_CONNECTD_SEND_ONIONMSG: + case WIRE_CONNECTD_INJECT_ONIONMSG: case WIRE_CONNECTD_CUSTOMMSG_OUT: case WIRE_CONNECTD_START_SHUTDOWN: case WIRE_CONNECTD_SET_CUSTOMMSGS: @@ -619,6 +620,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: case WIRE_CONNECTD_PING_REPLY: case WIRE_CONNECTD_START_SHUTDOWN_REPLY: + case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY: break; case WIRE_CONNECTD_PEER_CONNECTED: diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index baf743fec..3907ffdc3 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -249,3 +249,82 @@ static const struct json_command sendonionmessage_command = { "Send message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" }; AUTODATA(json_command, &sendonionmessage_command); + +static void inject_onionmsg_reply(struct subd *connectd, + const u8 *reply, + const int *fds UNUSED, + struct command *cmd) +{ + char *err; + + if (!fromwire_connectd_inject_onionmsg_reply(cmd, reply, &err)) { + log_broken(connectd->ld->log, "bad onionmsg_reply: %s", + tal_hex(tmpctx, reply)); + return; + } + + if (strlen(err) == 0) + was_pending(command_success(cmd, json_stream_success(cmd))); + else + was_pending(command_fail(cmd, LIGHTNINGD, "%s", err)); +} + +static struct command_result *json_injectonionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct onion_hop *hops; + struct pubkey *blinding; + struct sphinx_path *sphinx_path; + struct onionpacket *op; + struct secret *path_secrets; + size_t onion_size; + + if (!param_check(cmd, buffer, params, + p_req("blinding", param_pubkey, &blinding), + p_req("hops", param_onion_hops, &hops), + NULL)) + return command_param_failed(); + + if (!feature_offered(cmd->ld->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return command_fail(cmd, LIGHTNINGD, + "experimental-onion-messages not enabled"); + + /* Create an onion which encodes this. */ + sphinx_path = sphinx_path_new(cmd, NULL); + for (size_t i = 0; i < tal_count(hops); i++) + sphinx_add_hop(sphinx_path, &hops[i].node, hops[i].tlv); + + /* BOLT-onion-message #4: + * - SHOULD set `onion_message_packet` `len` to 1366 or 32834. + */ + if (sphinx_path_payloads_size(sphinx_path) <= ROUTING_INFO_SIZE) + onion_size = ROUTING_INFO_SIZE; + else + onion_size = 32768; /* VERSION_SIZE + HMAC_SIZE + PUBKEY_SIZE == 66 */ + + op = create_onionpacket(tmpctx, sphinx_path, onion_size, &path_secrets); + if (!op) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Creating onion failed (tlvs too long?)"); + + if (command_check_only(cmd)) + return command_check_done(cmd); + + subd_req(cmd, cmd->ld->connectd, + take(towire_connectd_inject_onionmsg(NULL, + blinding, + serialize_onionpacket(tmpctx, op))), + -1, 0, inject_onionmsg_reply, cmd); + return command_still_pending(cmd); +} + +static const struct json_command injectonionmessage_command = { + "injectonionmessage", + "utility", + json_injectonionmessage, + "Unwrap using {blinding}, encoded over {hops} (id, tlv)" +}; +AUTODATA(json_command, &injectonionmessage_command);