diff --git a/common/blindedpath.c b/common/blindedpath.c index d65a0693806d..1976bca46e10 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -188,7 +188,6 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, const struct secret *ss, const u8 *enctlv) { - struct tlv_encrypted_data_tlv *encmsg; const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); size_t maxlen = tal_bytelen(cursor); @@ -197,12 +196,7 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, * - if the `enctlv` is not a valid TLV... * - MUST drop the message. */ - encmsg = fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); - if (!encmsg - || !tlv_fields_valid(encmsg->fields, NULL, NULL)) - return tal_free(encmsg); - - return encmsg; + return fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); } bool decrypt_enctlv(const struct pubkey *blinding, diff --git a/common/onion.c b/common/onion.c index 827ac476ea60..3fb4600f6822 100644 --- a/common/onion.c +++ b/common/onion.c @@ -209,22 +209,21 @@ struct onion_payload *onion_decode(const tal_t *ctx, const u8 *cursor = rs->raw_payload; size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; - size_t badfield; - if (!pull_payload_length(&cursor, &max, true, &len)) - goto general_fail; - - tlv = fromwire_tlv_tlv_payload(p, &cursor, &max); - if (!tlv) { - /* FIXME: Fill in correct thing here! */ - goto general_fail; + if (!pull_payload_length(&cursor, &max, true, &len)) { + *failtlvtype = 0; + *failtlvpos = tal_bytelen(rs->raw_payload); + goto fail_no_tlv; } - /* FIXME: This API makes it really hard to get the actual - * offset of field. */ - if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, &badfield)) { - *failtlvtype = tlv->fields[badfield].numtype; - goto field_bad; + /* We do this manually so we can accept extra types, and get + * error off and type. */ + tlv = tlv_tlv_payload_new(p); + if (!fromwire_tlv(&cursor, &max, tlvs_tlv_tlv_payload, + TLVS_ARRAY_SIZE_tlv_tlv_payload, + tlv, &tlv->fields, accepted_extra_tlvs, + failtlvpos, failtlvtype)) { + goto fail; } /* BOLT #4: @@ -336,14 +335,10 @@ struct onion_payload *onion_decode(const tal_t *ctx, field_bad: *failtlvpos = tlv_field_offset(rs->raw_payload, tal_bytelen(rs->raw_payload), *failtlvtype); - goto fail; - -general_fail: - *failtlvtype = 0; - *failtlvpos = tal_bytelen(rs->raw_payload); - goto fail; fail: tal_free(tlv); + +fail_no_tlv: tal_free(p); return NULL; } diff --git a/common/test/run-json.c b/common/test/run-json.c index 711368786daf..d5511bce4da8 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -11,12 +11,9 @@ /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-param.c b/common/test/run-param.c index 71bdd277d2ce..edda9895ddd4 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -37,7 +37,8 @@ struct command_result *command_fail(struct command *cmd, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for json_to_channel_id */ bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -76,10 +77,6 @@ int segwit_addr_decode( const char* addr ) { fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 22db6f5eccee..c3b375aeae8a 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -31,15 +31,12 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-route.c b/common/test/run-route.c index 31887236ac06..5c575c6fd14d 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -24,15 +24,12 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 36248e44aefb..7af35453bf12 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -57,7 +57,8 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) @@ -65,10 +66,6 @@ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id U /* Generated stub for tlv_field_offset */ size_t tlv_field_offset(const u8 *tlvstream UNNEEDED, size_t tlvlen UNNEEDED, u64 fieldtype UNNEEDED) { fprintf(stderr, "tlv_field_offset called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } diff --git a/plugins/keysend.c b/plugins/keysend.c index 4e47fbab4c08..44d99a5f8f3e 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -342,6 +342,9 @@ static struct command_result *htlc_accepted_call(struct command *cmd, struct out_req *req; struct timeabs now = time_now(); const char *err; + u64 *allowed = tal_arr(cmd, u64, 1); + size_t err_off; + u64 err_type; err = json_scan(tmpctx, buf, params, "{onion:{payload:%},htlc:{payment_hash:%}}", @@ -356,10 +359,15 @@ static struct command_result *htlc_accepted_call(struct command *cmd, if (s != max) { return htlc_accepted_continue(cmd, NULL); } - payload = fromwire_tlv_tlv_payload(cmd, &rawpayload, &max); - if (!payload) { + + /* We explicitly allow our type. */ + allowed[0] = 5482373484; + payload = tlv_tlv_payload_new(cmd); + if (!fromwire_tlv(&rawpayload, &max, tlvs_tlv_tlv_payload, TLVS_ARRAY_SIZE_tlv_tlv_payload, + payload, &payload->fields, allowed, &err_off, &err_type)) { plugin_log( - cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload %.*s", + cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload type %"PRIu64" at off %zu %.*s", + err_type, err_off, json_tok_full_len(params), json_tok_full(buf, params)); return htlc_accepted_continue(cmd, NULL); diff --git a/tools/gen/impl_template b/tools/gen/impl_template index 0903cc0f78b6..3d013f7f144d 100644 --- a/tools/gen/impl_template +++ b/tools/gen/impl_template @@ -269,16 +269,10 @@ void towire_${tlv.name}(u8 **pptr, const struct ${tlv.struct_name()} *record) struct ${tlv.name} *fromwire_${tlv.name}(const tal_t *ctx, const u8 **cursor, size_t *max) { struct ${tlv.name} *record = ${tlv.name}_new(ctx); - if (!fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields)) + if (!fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields, NULL, NULL, NULL)) return tal_free(record); return record; } - -bool ${tlv.name}_is_valid(const struct ${tlv.struct_name()} *record, size_t *err_index) -{ - return tlv_fields_valid(record->fields, NULL, err_index); -} - % endfor ## END TLV's % for msg in messages: ## START Wire Messages diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 3397c8db61c2..dacdb3d2e1c1 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -468,12 +468,12 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_either[i].reason, reason)); max = tal_count(orig_p); p = orig_p; tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); assert(strstr(invalid_streams_either[i].reason, reason)); } @@ -485,7 +485,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_n1[i].reason, reason)); } @@ -497,7 +497,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1_combo[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_n1_combo[i].reason, reason)); } @@ -509,8 +509,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n2_combo[i].hex); max = tal_count(p); tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || - !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); assert(strstr(invalid_streams_n2_combo[i].reason, reason)); } @@ -525,8 +524,7 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert(tlv_n1 && - tlv_n1_is_valid(tlv_n1, NULL)); + assert(tlv_n1); assert(max == 0); assert(tlv_n1_eq(tlv_n1, &valid_streams[i].expect)); @@ -558,13 +556,11 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); max = tal_count(orig_p); p = orig_p; tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || - !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); } } @@ -578,8 +574,7 @@ int main(int argc, char *argv[]) invalid_streams_n1[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); } } @@ -593,8 +588,7 @@ int main(int argc, char *argv[]) invalid_streams_n1_combo[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); } } @@ -624,11 +618,12 @@ int main(int argc, char *argv[]) < pull_type(valid_streams[j].hex); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert(tlv_n1 && - tlv_n1_is_valid(tlv_n1, NULL) == expect_success); - - if (!expect_success) + if (!expect_success) { + assert(!tlv_n1); continue; + } + + assert(tlv_n1); /* Re-encoding should give the same results (except * ignored fields tests!) */ diff --git a/wire/tlvstream.c b/wire/tlvstream.c index aadd04460706..1fff8415a49b 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -110,10 +110,39 @@ size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype) return tlvlen; } +static bool tlv_type_is_allowed(const struct tlv_field *f, + const u64 *extra_types) +{ + /* Simple case: it's an odd field. */ + if (f->numtype % 2 != 0) + return true; + + /* Now iterate through the extras and see if we should make an + * exception. */ + for (size_t i = 0; i < tal_count(extra_types); i++) + if (extra_types[i] == f->numtype) + return true; + return false; +} + +/* Update err_off to point to current offset. */ +static void update_err_off(size_t *err_off, size_t initial_len, size_t max) +{ + if (err_off) + *err_off = initial_len - max; +} + bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, - void *record, struct tlv_field **fields) + void *record, struct tlv_field **fields, + const u64 *extra_types, + size_t *err_off, u64 *err_type) { + bool first = true; + u64 prev_type = 0; + size_t initial_len = *max; + + update_err_off(err_off, initial_len, *max); while (*max > 0) { struct tlv_field field; @@ -129,8 +158,52 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (!*cursor) { SUPERVERBOSE("type"); + if (err_type) + *err_type = 0; + goto fail; + } + + /* BOLT #1: + * - if decoded `type`s are not strictly-increasing + * (including situations when two or more occurrences + * of the same `type` are met): + * - MUST fail to parse the `tlv_stream`. + */ + if (!first && field.numtype <= prev_type) { + if (field.numtype == prev_type) + SUPERVERBOSE("duplicate tlv type"); + else + SUPERVERBOSE("invalid ordering"); + if (err_type) + *err_type = field.numtype; goto fail; } + first = false; + prev_type = field.numtype; + + /* BOLT #1: + * - if `type` is known: + * - MUST decode the next `length` bytes using the known + * encoding for `type`. + */ + field.meta = NULL; + for (size_t i = 0; i < num_types; i++) { + if (types[i].type == field.numtype) { + field.meta = &types[i]; + break; + } + } + + if (!field.meta && !tlv_type_is_allowed(&field, extra_types)) { + SUPERVERBOSE("unknown even"); + if (err_type != NULL) + *err_type = field.numtype; + goto fail; + } + + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + field.length = fromwire_bigsize(cursor, max); /* BOLT #1: @@ -139,6 +212,8 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (!*cursor) { SUPERVERBOSE("length"); + if (err_type) + *err_type = field.numtype; goto fail; } @@ -149,28 +224,31 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (field.length > *max) { SUPERVERBOSE("value"); + if (err_type) + *err_type = field.numtype; goto fail; } - field.value = tal_dup_arr(record, u8, *cursor, field.length, 0); - /* BOLT #1: - * - if `type` is known: - * - MUST decode the next `length` bytes using the known - * encoding for `type`. - */ - field.meta = NULL; - for (size_t i = 0; i < num_types; i++) { - if (types[i].type == field.numtype) - field.meta = &types[i]; - } + /* We're happy with length field. Move on. */ + update_err_off(err_off, initial_len, *max); + + field.value = tal_dup_arr(record, u8, *cursor, field.length, 0); if (field.meta) { /* Length of message can't exceed 16 bits anyway. */ size_t tlvlen = field.length; + + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + + /* FIXME: We could add an err_off in here for more accuracy. */ field.meta->fromwire(cursor, &tlvlen, record); - if (!*cursor) + if (!*cursor) { + if (err_type != NULL) + *err_type = field.numtype; goto fail; + } /* BOLT #1: * - if `length` is not exactly equal to that required @@ -178,16 +256,25 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, * - MUST fail to parse the `tlv_stream`. */ if (tlvlen != 0) { + if (err_type != NULL) + *err_type = field.numtype; SUPERVERBOSE("greater than encoding length"); goto fail; } } else { + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + /* We didn't read from *cursor through a fromwire, so * update manually. */ *cursor += field.length; } /* We've read bytes in ->fromwire, so update max */ *max -= field.length; + + /* We're happy with contents. Move on. */ + update_err_off(err_off, initial_len, *max); + tal_arr_expand(fields, field); } return true; @@ -196,63 +283,6 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, return false; } -static bool tlv_type_is_allowed(const struct tlv_field *f, u64 *extra_types) { - /* Simple case: we have internal meta fields or it's an odd field. */ - if (f->numtype % 2 != 0 || f->meta != NULL) - return true; - - if (extra_types == NULL) - return false; - - /* Now iterate through the extras and see if we should make an - * exception. */ - for (size_t i = 0; i < tal_count(extra_types); i++) - if (extra_types[i] == f->numtype) - return true; - return false; -} - -bool tlv_fields_valid(const struct tlv_field *fields, u64 *allow_extra, - size_t *err_index) -{ - size_t numfields = tal_count(fields); - bool first = true; - u64 prev_type = 0; - for (int i=0; inumtype <= prev_type) { - /* BOLT #1: - * - if decoded `type`s are not strictly-increasing - * (including situations when two or more occurrences - * of the same `type` are met): - * - MUST fail to parse the `tlv_stream`. - */ - if (f->numtype == prev_type) - SUPERVERBOSE("duplicate tlv type"); - else - SUPERVERBOSE("invalid ordering"); - if (err_index != NULL) - *err_index = i; - return false; - } - first = false; - prev_type = f->numtype; - } - return true; -} - void towire_tlv(u8 **pptr, const struct tlv_record_type *types, size_t num_types, const void *record) diff --git a/wire/tlvstream.h b/wire/tlvstream.h index 61addfea8a9f..e87862630523 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -38,15 +38,24 @@ struct tlv_field *tlv_make_fields_(const struct tlv_record_type *types, size_t num_types, const void *record); -/* Generic TLV decode/encode */ +/** + * fromwire_tlv: generic TLV decode engine + * @cursor: cursor to update (set to NULL if we fail). + * @max: max len to update (always set to 0 if we succeed). + * @types / @num_types: table of known tlv types + * @record: the tlv to hand to @type-specific decode + * @fields: the fields array to populate + * @extra_types: tal_arr or NULL of unknown types to allow + * @err_off: NULL, or set to offset in tlv stream which failed. + * @err_type: NULL, or set to tlv type which failed (or 0 if malformed) + */ bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, - void *record, struct tlv_field **fields); + void *record, struct tlv_field **fields, + const u64 *extra_types, size_t *err_off, u64 *err_type); void towire_tlv(u8 **pptr, const struct tlv_record_type *types, size_t num_types, const void *record); -bool tlv_fields_valid(const struct tlv_field *fields, u64 *allow_extra, - size_t *err_index); /* Get the offset of this field: returns size of msg if not found (or * tlv malformed) */