#include "config.h" #include #include #include #include #include #include #include #include /* To reach into io_plan: not a public header! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void adjust_io_write(struct json_out *jout, ptrdiff_t delta, struct json_stream *js) { /* If io_write is in progress, we shift it to point to new buffer pos */ if (js->reader) /* FIXME: This, or something prettier (io_replan?) belong in ccan/io! */ js->reader->plan[IO_OUT].arg.u1.cp += delta; } struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer, struct log *log) { struct json_stream *js = tal(ctx, struct json_stream); /* FIXME: Add magic so tal_resize can fail! */ js->jout = json_out_new(js); json_out_call_on_move(js->jout, adjust_io_write, js); js->writer = writer; js->reader = NULL; js->log = log; return js; } struct json_stream *json_stream_dup(const tal_t *ctx, struct json_stream *original, struct log *log) { struct json_stream *js = tal_dup(ctx, struct json_stream, original); js->jout = json_out_dup(js, original->jout); js->log = log; return js; } /** * json_stream_still_writing - is someone currently writing to this stream? * @js: the json_stream. * * Has this json_stream not been closed yet? */ static bool json_stream_still_writing(const struct json_stream *js) { return js->writer != NULL; } void json_stream_log_suppress(struct json_stream *js, const char *cmd_name) { /* Really shouldn't be used for anything else */ assert(streq(cmd_name, "getlog")); js->log = NULL; } void json_stream_append(struct json_stream *js, const char *str, size_t len) { char *dest; dest = json_out_direct(js->jout, len); memcpy(dest, str, len); } /* We promise it will end in '\n\n' */ void json_stream_double_cr(struct json_stream *js) { const char *contents; size_t len, cr_needed; /* Must be well-formed at this point! */ json_out_finished(js->jout); contents = json_out_contents(js->jout, &len); /* It's an object (with an id!): definitely can't be less that "{}" */ assert(len >= 2); if (contents[len-1] == '\n') { if (contents[len-2] == '\n') return; cr_needed = 1; } else cr_needed = 2; json_stream_append(js, "\n\n", cr_needed); } void json_stream_close(struct json_stream *js, struct command *writer) { /* FIXME: We use writer == NULL for malformed: make writer a void *? * I used to assert(writer); here. */ assert(js->writer == writer); /* Should be well-formed at this point! */ json_stream_double_cr(js); json_stream_flush(js); js->writer = NULL; } /* Also called when we're oom, so it will kill reader. */ void json_stream_flush(struct json_stream *js) { /* Wake the stream reader. FIXME: Could have a flag here to optimize */ io_wake(js); } void json_array_start(struct json_stream *js, const char *fieldname) { json_out_start(js->jout, fieldname, '['); } void json_array_end(struct json_stream *js) { json_out_end(js->jout, ']'); } void json_object_start(struct json_stream *js, const char *fieldname) { json_out_start(js->jout, fieldname, '{'); } void json_object_end(struct json_stream *js) { json_out_end(js->jout, '}'); } void json_add_primitive_fmt(struct json_stream *js, const char *fieldname, const char *fmt, ...) { va_list ap; va_start(ap, fmt); json_out_addv(js->jout, fieldname, false, fmt, ap); va_end(ap); } void json_add_str_fmt(struct json_stream *js, const char *fieldname, const char *fmt, ...) { va_list ap; va_start(ap, fmt); json_out_addv(js->jout, fieldname, true, fmt, ap); va_end(ap); } void json_add_primitive(struct json_stream *js, const char *fieldname, const char *val TAKES) { json_add_primitive_fmt(js, fieldname, "%s", val); if (taken(val)) tal_free(val); } void json_add_string(struct json_stream *js, const char *fieldname, const char *str TAKES) { json_out_addstr(js->jout, fieldname, str); if (taken(str)) tal_free(str); } static char *json_member_direct(struct json_stream *js, const char *fieldname, size_t extra) { char *dest; dest = json_out_member_direct(js->jout, fieldname, extra); return dest; } void json_add_jsonstr(struct json_stream *js, const char *fieldname, const char *jsonstr, size_t jsonstrlen) { char *p; p = json_member_direct(js, fieldname, jsonstrlen); memcpy(p, jsonstr, jsonstrlen); } /* This is where we read the json_stream and write it to conn */ static struct io_plan *json_stream_output_write(struct io_conn *conn, struct json_stream *js) { const char *p; /* For when we've just done some output */ json_out_consume(js->jout, js->len_read); /* Get how much we can write out from js */ p = json_out_contents(js->jout, &js->len_read); /* Nothing in buffer? */ if (!p) { /* We're not doing io_write now, unset. */ js->reader = NULL; if (!json_stream_still_writing(js)) return js->reader_cb(conn, js, js->reader_arg); return io_out_wait(conn, js, json_stream_output_write, js); } js->reader = conn; return io_write(conn, p, js->len_read, json_stream_output_write, js); } struct io_plan *json_stream_output_(struct json_stream *js, struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *conn, struct json_stream *js, void *arg), void *arg) { assert(!js->reader); js->reader_cb = cb; js->reader_arg = arg; js->len_read = 0; return json_stream_output_write(conn, js); } void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value) { json_add_primitive_fmt(result, fieldname, "%u", value); } void json_add_u64(struct json_stream *result, const char *fieldname, uint64_t value) { json_add_primitive_fmt(result, fieldname, "%"PRIu64, value); } void json_add_s64(struct json_stream *result, const char *fieldname, int64_t value) { json_add_primitive_fmt(result, fieldname, "%"PRIi64, value); } void json_add_u32(struct json_stream *result, const char *fieldname, uint32_t value) { json_add_primitive_fmt(result, fieldname, "%u", value); } void json_add_s32(struct json_stream *result, const char *fieldname, int32_t value) { json_add_primitive_fmt(result, fieldname, "%d", value); } void json_add_stringn(struct json_stream *result, const char *fieldname, const char *value TAKES, size_t value_len) { json_add_str_fmt(result, fieldname, "%.*s", (int)value_len, value); if (taken(value)) tal_free(value); } void json_add_bool(struct json_stream *result, const char *fieldname, bool value) { json_add_primitive(result, fieldname, value ? "true" : "false"); } void json_add_null(struct json_stream *stream, const char *fieldname) { json_add_primitive(stream, fieldname, "null"); } void json_add_hex(struct json_stream *js, const char *fieldname, const void *data, size_t len) { /* Size without NUL term */ size_t hexlen = hex_str_size(len); char str[hexlen]; if (!hex_encode(data, len, str, hexlen)) abort(); json_add_string(js, fieldname, str); } void json_add_hex_talarr(struct json_stream *result, const char *fieldname, const tal_t *data) { json_add_hex(result, fieldname, data, tal_bytelen(data)); } void json_add_escaped_string(struct json_stream *result, const char *fieldname, const struct json_escape *esc TAKES) { /* Already escaped, don't re-escape! */ char *dest = json_member_direct(result, fieldname, 1 + strlen(esc->s) + 1); dest[0] = '"'; memcpy(dest + 1, esc->s, strlen(esc->s)); dest[1+strlen(esc->s)] = '"'; if (taken(esc)) tal_free(esc); } void json_add_timeabs(struct json_stream *result, const char *fieldname, struct timeabs t) { json_add_primitive_fmt(result, fieldname, "%" PRIu64 ".%03" PRIu64, (u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000); } void json_add_time(struct json_stream *result, const char *fieldname, struct timespec ts) { char timebuf[100]; snprintf(timebuf, sizeof(timebuf), "%lu.%09u", (unsigned long)ts.tv_sec, (unsigned)ts.tv_nsec); json_add_string(result, fieldname, timebuf); } void json_add_timeiso(struct json_stream *result, const char *fieldname, struct timeabs *time) { char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ")]; char iso8601_s[sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ")]; strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), "%FT%T.%%03dZ", gmtime(&time->ts.tv_sec)); snprintf(iso8601_s, sizeof(iso8601_s), iso8601_msec_fmt, (int) time->ts.tv_nsec / 1000000); json_add_string(result, fieldname, iso8601_s); } void json_add_tok(struct json_stream *result, const char *fieldname, const jsmntok_t *tok, const char *buffer) { char *space; assert(tok->type != JSMN_UNDEFINED); space = json_member_direct(result, fieldname, json_tok_full_len(tok)); memcpy(space, json_tok_full(buffer, tok), json_tok_full_len(tok)); } void json_add_errcode(struct json_stream *result, const char *fieldname, errcode_t code) { json_add_primitive_fmt(result, fieldname, "%" PRIerrcode, code); } void json_add_invstring(struct json_stream *result, const char *invstring) { if (strstarts(invstring, "lni")) json_add_string(result, "bolt12", invstring); else json_add_string(result, "bolt11", invstring); } void json_add_node_id(struct json_stream *response, const char *fieldname, const struct node_id *id) { json_add_hex(response, fieldname, id->k, sizeof(id->k)); } void json_add_channel_id(struct json_stream *response, const char *fieldname, const struct channel_id *cid) { json_add_hex(response, fieldname, cid->id, sizeof(cid->id)); } void json_add_pubkey(struct json_stream *response, const char *fieldname, const struct pubkey *key) { u8 der[PUBKEY_CMPR_LEN]; pubkey_to_der(der, key); json_add_hex(response, fieldname, der, sizeof(der)); } void json_add_point32(struct json_stream *response, const char *fieldname, const struct point32 *key) { u8 output[32]; secp256k1_xonly_pubkey_serialize(secp256k1_ctx, output, &key->pubkey); json_add_hex(response, fieldname, output, sizeof(output)); } void json_add_bip340sig(struct json_stream *response, const char *fieldname, const struct bip340sig *sig) { json_add_hex(response, fieldname, sig->u8, sizeof(sig->u8)); } void json_add_txid(struct json_stream *result, const char *fieldname, const struct bitcoin_txid *txid) { char hex[hex_str_size(sizeof(*txid))]; bitcoin_txid_to_hex(txid, hex, sizeof(hex)); json_add_string(result, fieldname, hex); } void json_add_outpoint(struct json_stream *result, const char *fieldname, const struct bitcoin_outpoint *out) { char hex[hex_str_size(sizeof(out->txid))]; bitcoin_txid_to_hex(&out->txid, hex, sizeof(hex)); json_add_str_fmt(result, fieldname, "%s:%d", hex, out->n); } void json_add_short_channel_id(struct json_stream *response, const char *fieldname, const struct short_channel_id *scid) { json_add_str_fmt(response, fieldname, "%dx%dx%d", short_channel_id_blocknum(scid), short_channel_id_txnum(scid), short_channel_id_outnum(scid)); } void json_add_address(struct json_stream *response, const char *fieldname, const struct wireaddr *addr) { json_object_start(response, fieldname); if (addr->type == ADDR_TYPE_IPV4) { char addrstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN); json_add_string(response, "type", "ipv4"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_IPV6) { char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN); json_add_string(response, "type", "ipv6"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_TOR_V2_REMOVED) { json_add_string(response, "type", "torv2"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_TOR_V3) { json_add_string(response, "type", "torv3"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_DNS) { json_add_string(response, "type", "dns"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_WEBSOCKET) { json_add_string(response, "type", "websocket"); json_add_num(response, "port", addr->port); } json_object_end(response); } void json_add_address_internal(struct json_stream *response, const char *fieldname, const struct wireaddr_internal *addr) { switch (addr->itype) { case ADDR_INTERNAL_SOCKNAME: json_object_start(response, fieldname); json_add_string(response, "type", "local socket"); json_add_string(response, "socket", addr->u.sockname); json_object_end(response); return; case ADDR_INTERNAL_ALLPROTO: json_object_start(response, fieldname); json_add_string(response, "type", "any protocol"); json_add_num(response, "port", addr->u.port); json_object_end(response); return; case ADDR_INTERNAL_AUTOTOR: json_object_start(response, fieldname); json_add_string(response, "type", "Tor generated address"); json_add_address(response, "service", &addr->u.torservice.address); json_object_end(response); return; case ADDR_INTERNAL_STATICTOR: json_object_start(response, fieldname); json_add_string(response, "type", "Tor from blob generated static address"); json_add_address(response, "service", &addr->u.torservice.address); json_object_end(response); return; case ADDR_INTERNAL_FORPROXY: json_object_start(response, fieldname); json_add_string(response, "type", "unresolved"); json_add_string(response, "name", addr->u.unresolved.name); json_add_num(response, "port", addr->u.unresolved.port); json_object_end(response); return; case ADDR_INTERNAL_WIREADDR: json_add_address(response, fieldname, &addr->u.wireaddr); return; } abort(); } void json_add_tx(struct json_stream *result, const char *fieldname, const struct bitcoin_tx *tx) { json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx)); } void json_add_psbt(struct json_stream *stream, const char *fieldname, const struct wally_psbt *psbt TAKES) { const char *psbt_b64; psbt_b64 = psbt_to_b64(NULL, psbt); json_add_string(stream, fieldname, take(psbt_b64)); if (taken(psbt)) tal_free(psbt); } void json_add_amount_msat_compat(struct json_stream *result, struct amount_msat msat, const char *rawfieldname, const char *msatfieldname) { if (deprecated_apis) json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ json_add_amount_msat_only(result, msatfieldname, msat); } void json_add_amount_msat_only(struct json_stream *result, const char *msatfieldname, struct amount_msat msat) { if (!deprecated_apis) assert(strends(msatfieldname, "_msat")); if (deprecated_apis) json_add_string(result, msatfieldname, type_to_string(tmpctx, struct amount_msat, &msat)); else json_add_u64(result, msatfieldname, msat.millisatoshis); /* Raw: low-level helper */ } void json_add_amount_sat_compat(struct json_stream *result, struct amount_sat sat, const char *rawfieldname, const char *msatfieldname) { if (deprecated_apis) json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ json_add_amount_sat_msat(result, msatfieldname, sat); } void json_add_amount_sat_msat(struct json_stream *result, const char *msatfieldname, struct amount_sat sat) { struct amount_msat msat; assert(strends(msatfieldname, "_msat")); if (amount_sat_to_msat(&msat, sat)) json_add_amount_msat_only(result, msatfieldname, msat); } /* When I noticed that we were adding "XXXmsat" fields *not* ending in _msat */ void json_add_amount_sats_deprecated(struct json_stream *result, const char *fieldname, const char *msatfieldname, struct amount_sat sat) { if (deprecated_apis) { struct amount_msat msat; assert(!strends(fieldname, "_msat")); if (amount_sat_to_msat(&msat, sat)) json_add_string(result, fieldname, take(fmt_amount_msat(NULL, msat))); } json_add_amount_sat_msat(result, msatfieldname, sat); } void json_add_sats(struct json_stream *result, const char *fieldname, struct amount_sat sat) { json_add_string(result, fieldname, take(fmt_amount_sat(NULL, sat))); } void json_add_secret(struct json_stream *response, const char *fieldname, const struct secret *secret) { json_add_hex(response, fieldname, secret, sizeof(struct secret)); } void json_add_sha256(struct json_stream *result, const char *fieldname, const struct sha256 *hash) { json_add_hex(result, fieldname, hash, sizeof(*hash)); } void json_add_preimage(struct json_stream *result, const char *fieldname, const struct preimage *preimage) { json_add_hex(result, fieldname, preimage, sizeof(*preimage)); } void json_add_lease_rates(struct json_stream *result, const struct lease_rates *rates) { json_add_amount_sat_msat(result, "lease_fee_base_msat", amount_sat(rates->lease_fee_base_sat)); json_add_num(result, "lease_fee_basis", rates->lease_fee_basis); json_add_num(result, "funding_weight", rates->funding_weight); json_add_amount_msat_only(result, "channel_fee_max_base_msat", amount_msat(rates->channel_fee_max_base_msat)); json_add_num(result, "channel_fee_max_proportional_thousandths", rates->channel_fee_max_proportional_thousandths); }