From 9f7d4312ff30d7335058f49b2193a67e6428d066 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 26 Mar 2018 10:38:47 +1030 Subject: [PATCH] bolt11: undo json encoding for description bytes. We don't handle \u, since we assume everyone sane is using UTF-8. We'd still have to reject '\u0000' and maybe other weird cases if we did. Signed-off-by: Rusty Russell --- common/json_escaped.c | 44 +++++++++++++++++++++++++++++++++++++++++++ common/json_escaped.h | 3 +++ devtools/bolt11-cli.c | 2 +- lightningd/invoice.c | 35 ++++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/common/json_escaped.c b/common/json_escaped.c index 5f7bbb53e..7722f95ed 100644 --- a/common/json_escaped.c +++ b/common/json_escaped.c @@ -84,3 +84,47 @@ struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES) tal_free(str); return esc; } + +/* By policy, we don't handle \u. Use UTF-8. */ +const char *json_escaped_unescape(const tal_t *ctx, + const struct json_escaped *esc) +{ + char *unesc = tal_arr(ctx, char, strlen(esc->s) + 1); + size_t i, n; + + for (i = n = 0; esc->s[i]; i++, n++) { + if (esc->s[i] != '\\') { + unesc[n] = esc->s[i]; + continue; + } + + i++; + switch (esc->s[i]) { + case 'n': + unesc[n] = '\n'; + break; + case 'b': + unesc[n] = '\b'; + break; + case 'f': + unesc[n] = '\f'; + break; + case 't': + unesc[n] = '\t'; + break; + case 'r': + unesc[n] = '\r'; + break; + case '/': + case '\\': + case '"': + unesc[n] = esc->s[i]; + break; + default: + return tal_free(unesc); + } + } + + unesc[n] = '\0'; + return unesc; +} diff --git a/common/json_escaped.h b/common/json_escaped.h index 31ec389db..62a2a0a82 100644 --- a/common/json_escaped.h +++ b/common/json_escaped.h @@ -33,4 +33,7 @@ void json_add_escaped_string(struct json_result *result, struct json_escaped *json_escaped_string_(const tal_t *ctx, const void *bytes, size_t len); +/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */ +const char *json_escaped_unescape(const tal_t *ctx, + const struct json_escaped *esc); #endif /* LIGHTNING_COMMON_JSON_ESCAPED_H */ diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index 00bd49ac0..7b5cd66fa 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -105,7 +105,7 @@ int main(int argc, char *argv[]) if (b11->msatoshi) printf("msatoshi: %"PRIu64"\n", *b11->msatoshi); if (b11->description) - printf("description: %s\n", b11->description); + printf("description: '%s'\n", b11->description); if (b11->description_hash) printf("description_hash: %s\n", tal_hexstr(ctx, b11->description_hash, diff --git a/lightningd/invoice.c b/lightningd/invoice.c index a4f8417eb..ff97f9678 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -109,9 +109,9 @@ static void json_invoice(struct command *cmd, { struct invoice invoice; struct invoice_details details; - jsmntok_t *msatoshi, *label, *desc, *exp, *fallback; + jsmntok_t *msatoshi, *label, *desctok, *exp, *fallback; u64 *msatoshi_val; - const struct json_escaped *label_val; + const struct json_escaped *label_val, *desc; const char *desc_val; enum address_parse_result fallback_parse; struct json_result *response = new_json_result(cmd); @@ -125,7 +125,7 @@ static void json_invoice(struct command *cmd, if (!json_get_params(cmd, buffer, params, "msatoshi", &msatoshi, "label", &label, - "description", &desc, + "description", &desctok, "?expiry", &exp, "?fallback", &fallback, NULL)) { @@ -163,18 +163,31 @@ static void json_invoice(struct command *cmd, INVOICE_MAX_LABEL_LEN); return; } + + desc = json_tok_escaped_string(cmd, buffer, desctok); + if (!desc) { + command_fail(cmd, "description '%.*s' not a string", + desctok->end - desctok->start, + buffer + desctok->start); + return; + } + desc_val = json_escaped_unescape(cmd, desc); + if (!desc_val) { + command_fail(cmd, "description '%s' is invalid" + " (note: we don't allow \\u)", + desc->s); + return; + } /* description */ - if (desc->end - desc->start >= BOLT11_FIELD_BYTE_LIMIT) { + if (strlen(desc_val) >= BOLT11_FIELD_BYTE_LIMIT) { command_fail(cmd, "Descriptions greater than %d bytes " "not yet supported " - "(description length %d)", + "(description length %zu)", BOLT11_FIELD_BYTE_LIMIT, - desc->end - desc->start); + strlen(desc_val)); return; } - desc_val = tal_strndup(cmd, buffer + desc->start, - desc->end - desc->start); /* expiry */ if (exp && !json_tok_u64(buffer, exp, &expiry)) { command_fail(cmd, "Expiry '%.*s' invalid seconds", @@ -629,8 +642,10 @@ static void json_decodepay(struct command *cmd, json_add_pubkey(response, "payee", &b11->receiver_id); if (b11->msatoshi) json_add_u64(response, "msatoshi", *b11->msatoshi); - if (b11->description) - json_add_string(response, "description", b11->description); + if (b11->description) { + struct json_escaped *esc = json_escape(NULL, b11->description); + json_add_escaped_string(response, "description", take(esc)); + } if (b11->description_hash) json_add_hex(response, "description_hash", b11->description_hash,