Rusty Russell
6 years ago
3 changed files with 172 additions and 0 deletions
@ -0,0 +1,139 @@ |
|||
#include "wire/tlvstream.h" |
|||
#include <assert.h> |
|||
#include <wire/wire.h> |
|||
|
|||
static const struct tlv_record_type * |
|||
find_record_type(u64 type, |
|||
const struct tlv_record_type types[], |
|||
size_t num_types) |
|||
{ |
|||
for (size_t i = 0; i < num_types; i++) |
|||
if (types[i].type == type) |
|||
return types + i; |
|||
return NULL; |
|||
} |
|||
|
|||
/* Pull all tlvs from a stream. Return false and calls fromwire_fail() on
|
|||
* error. */ |
|||
bool fromwire_tlvs(const u8 **cursor, size_t *max, |
|||
const struct tlv_record_type types[], |
|||
size_t num_types, |
|||
void *record) |
|||
{ |
|||
u64 prev_type; |
|||
bool first = true; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* |
|||
* The receiving node: |
|||
* - if zero bytes remain before parsing a `type`: |
|||
* - MUST stop parsing the `tlv_stream`. |
|||
*/ |
|||
while (*max > 0) { |
|||
u64 type, length; |
|||
const struct tlv_record_type *rtype; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* |
|||
* A `varint` is a variable-length, unsigned integer encoding |
|||
* using the [BigSize](#appendix-a-bigsize-test-vectors) |
|||
* format |
|||
*/ |
|||
type = fromwire_bigsize(cursor, max); |
|||
length = fromwire_bigsize(cursor, max); |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - if a `type` or `length` is not minimally encoded: |
|||
* - MUST fail to parse the `tlv_stream`. |
|||
*/ |
|||
if (!*cursor) |
|||
goto fail; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - if `length` exceeds the number of bytes remaining in the |
|||
* message: |
|||
* - MUST fail to parse the `tlv_stream`. |
|||
*/ |
|||
if (length > *max) |
|||
goto fail; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - if decoded `type`s are not monotonically-increasing: |
|||
* - MUST fail to parse the `tlv_stream`. |
|||
*/ |
|||
if (!first && type <= prev_type) |
|||
goto fail; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - if `type` is known: |
|||
* - MUST decode the next `length` bytes using the known |
|||
* encoding for `type`. |
|||
*/ |
|||
rtype = find_record_type(type, types, num_types); |
|||
if (rtype) { |
|||
/* Length of message can't exceed 16 bits anyway. */ |
|||
size_t tlvlen = length; |
|||
rtype->fromwire(cursor, &tlvlen, record); |
|||
|
|||
if (!*cursor) |
|||
goto fail; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - if `length` is not exactly equal to that required |
|||
* for the known encoding for `type`: |
|||
* - MUST fail to parse the `tlv_stream`. |
|||
*/ |
|||
if (tlvlen != 0) |
|||
goto fail; |
|||
|
|||
/* We've read bytes in ->fromwire, so update max */ |
|||
*max -= length; |
|||
} else { |
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* - otherwise, if `type` is unknown: |
|||
* - if `type` is even: |
|||
* - MUST fail to parse the `tlv_stream`. |
|||
* - otherwise, if `type` is odd: |
|||
* - MUST discard the next `length` bytes. |
|||
*/ |
|||
if (type & 1) |
|||
fromwire(cursor, max, NULL, length); |
|||
else |
|||
goto fail; |
|||
} |
|||
first = false; |
|||
prev_type = type; |
|||
} |
|||
return true; |
|||
|
|||
fail: |
|||
fromwire_fail(cursor, max); |
|||
return false; |
|||
} |
|||
|
|||
/* Append a stream of tlvs. */ |
|||
void towire_tlvs(u8 **pptr, |
|||
const struct tlv_record_type types[], |
|||
size_t num_types, |
|||
const void *record) |
|||
{ |
|||
for (size_t i = 0; i < num_types; i++) { |
|||
u8 *val; |
|||
if (i != 0) |
|||
assert(types[i].type > types[i-1].type); |
|||
val = types[i].towire(NULL, record); |
|||
if (!val) |
|||
continue; |
|||
|
|||
/* BOLT-EXPERIMENTAL #1:
|
|||
* |
|||
* The sending node: |
|||
... |
|||
* - MUST minimally encode `type` and `length`. |
|||
*/ |
|||
towire_bigsize(pptr, types[i].type); |
|||
towire_bigsize(pptr, tal_bytelen(val)); |
|||
towire(pptr, val, tal_bytelen(val)); |
|||
tal_free(val); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
#ifndef LIGHTNING_WIRE_TLVSTREAM_H |
|||
#define LIGHTNING_WIRE_TLVSTREAM_H |
|||
#include "config.h" |
|||
#include <ccan/short_types/short_types.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <stdbool.h> |
|||
#include <stdlib.h> |
|||
|
|||
struct tlv_record_type { |
|||
u64 type; |
|||
/* If this type is present return marshalled value. Otherwise
|
|||
* returns NULL. */ |
|||
u8 *(*towire)(const tal_t *ctx, const void *record); |
|||
/* Must call fromwire_fail() it can't parse. */ |
|||
void (*fromwire)(const u8 **cursor, size_t *max, void *record); |
|||
}; |
|||
|
|||
/* Pull all tlvs from a stream. Return false and calls fromwire_fail() on
|
|||
* error. */ |
|||
bool fromwire_tlvs(const u8 **cursor, size_t *max, |
|||
const struct tlv_record_type types[], |
|||
size_t num_types, |
|||
void *record); |
|||
|
|||
/* Append a stream of tlvs: types[] must be in increasing type order! */ |
|||
void towire_tlvs(u8 **pptr, |
|||
const struct tlv_record_type types[], |
|||
size_t num_types, |
|||
const void *record); |
|||
|
|||
#endif /* LIGHTNING_WIRE_TLVSTREAM_H */ |
Loading…
Reference in new issue