From 3ad8438d91a132c109a6836ca4acfa67a933db34 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 4 Dec 2019 16:26:57 +0100 Subject: [PATCH] json-rpc: Add sendcustommsg command This command injects a custom message into the encrypted transport stream to the peer, allowing users to build custom protocols on top of c-lightning without requiring any changes to c-lightning itself. --- lightningd/peer_control.c | 67 +++++++++++++++++++++ lightningd/test/run-invoice-select-inchan.c | 8 +++ wallet/test/run-wallet.c | 11 ++++ 3 files changed, 86 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4116dae7b..d56ca6207 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -2373,5 +2374,71 @@ void peer_dev_memleak(struct command *cmd) { peer_memleak_req_next(cmd, NULL); } + +static struct command_result *json_sendcustommsg(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct node_id *dest; + struct peer *peer; + struct subd *owner; + u8 *msg; + + if (!param(cmd, buffer, params, + p_req("node_id", param_node_id, &dest), + p_req("msg", param_bin_from_hex, &msg), + NULL)) + return command_param_failed(); + + peer = peer_by_id(cmd->ld, dest); + if (!peer) { + return command_fail(cmd, JSONRPC2_INVALID_REQUEST, + "No such peer: %s", + type_to_string(cmd, struct node_id, dest)); + } + + owner = peer_get_owning_subd(peer); + if (owner == NULL) { + return command_fail(cmd, JSONRPC2_INVALID_REQUEST, + "Peer is not connected: %s", + type_to_string(cmd, struct node_id, dest)); + } + + /* Only a couple of subdaemons have the ability to send custom + * messages. We whitelist those, and error if the current owner is not + * in the whitelist. The reason is that some subdaemons do not handle + * spontaneous messages from the master well (I'm looking at you + * `closingd`...). */ + if (!streq(owner->name, "channeld") && + !streq(owner->name, "openingd")) { + return command_fail(cmd, JSONRPC2_INVALID_REQUEST, + "Peer is currently owned by %s which does " + "not support injecting custom messages.", + owner->name); + } + + subd_send_msg(owner, take(towire_custommsg_out(cmd, msg))); + + response = json_stream_success(cmd); + json_add_string(response, "status", + tal_fmt(cmd, + "Message sent to subdaemon %s for delivery", + owner->name)); + + return command_success(cmd, response); +} + +static const struct json_command sendcustommsg_command = { + "dev-sendcustommsg", + "utility", + json_sendcustommsg, + "Send a custom message to the peer with the given {node_id}", + .verbose = "dev-sendcustommsg node_id hexcustommsg", +}; + +AUTODATA(json_command, &sendcustommsg_command); + #endif /* DEVELOPER */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index f49a19f30..367b79b4f 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -330,6 +330,11 @@ struct command_result *param_array(struct command *cmd UNNEEDED, const char *nam const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const jsmntok_t **arr UNNEEDED) { fprintf(stderr, "param_array called!\n"); abort(); } +/* Generated stub for param_bin_from_hex */ +struct command_result *param_bin_from_hex(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + u8 **bin UNNEEDED) +{ fprintf(stderr, "param_bin_from_hex called!\n"); abort(); } /* Generated stub for param_bitcoin_address */ struct command_result *param_bitcoin_address(struct command *cmd UNNEEDED, const char *name UNNEEDED, @@ -407,6 +412,9 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint64_t **num UNNEEDED) { fprintf(stderr, "param_u64 called!\n"); abort(); } +/* Generated stub for peer_get_owning_subd */ +struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED) +{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); } /* Generated stub for peer_memleak_done */ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 3e1d48356..4709f4b1c 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -420,6 +420,11 @@ void outpointfilter_remove(struct outpointfilter *of UNNEEDED, bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t params[] UNNEEDED, ...) { fprintf(stderr, "param called!\n"); abort(); } +/* Generated stub for param_bin_from_hex */ +struct command_result *param_bin_from_hex(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + u8 **bin UNNEEDED) +{ fprintf(stderr, "param_bin_from_hex called!\n"); abort(); } /* Generated stub for param_bitcoin_address */ struct command_result *param_bitcoin_address(struct command *cmd UNNEEDED, const char *name UNNEEDED, @@ -491,6 +496,9 @@ void payment_store(struct lightningd *ld UNNEEDED, struct wallet_payment *paymen void payment_succeeded(struct lightningd *ld UNNEEDED, struct htlc_out *hout UNNEEDED, const struct preimage *rval UNNEEDED) { fprintf(stderr, "payment_succeeded called!\n"); abort(); } +/* Generated stub for peer_get_owning_subd */ +struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED) +{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); } /* Generated stub for peer_memleak_done */ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } @@ -590,6 +598,9 @@ u8 *towire_connectctl_connect_to_peer(const tal_t *ctx UNNEEDED, const struct no /* Generated stub for towire_connectctl_peer_disconnected */ u8 *towire_connectctl_peer_disconnected(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_connectctl_peer_disconnected called!\n"); abort(); } +/* Generated stub for towire_custommsg_out */ +u8 *towire_custommsg_out(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_custommsg_out called!\n"); abort(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED,