mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-18 22:54:25 +01:00
wire/tlvstream: routines to marshal/unmarshal TLV streams.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -6,6 +6,7 @@ wire-wrongdir:
|
||||
|
||||
WIRE_HEADERS_NOGEN := wire/onion_defs.h \
|
||||
wire/peer_wire.h \
|
||||
wire/tlvstream.h \
|
||||
wire/wire.h \
|
||||
wire/wire_sync.h \
|
||||
wire/wire_io.h
|
||||
@@ -16,6 +17,7 @@ WIRE_SRC := wire/wire_sync.c \
|
||||
wire/wire_io.c \
|
||||
wire/fromwire.c \
|
||||
wire/peer_wire.c \
|
||||
wire/tlvstream.c \
|
||||
wire/towire.c
|
||||
WIRE_HEADERS := $(WIRE_HEADERS_NOGEN) $(WIRE_GEN_HEADERS)
|
||||
|
||||
|
||||
139
wire/tlvstream.c
Normal file
139
wire/tlvstream.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
31
wire/tlvstream.h
Normal file
31
wire/tlvstream.h
Normal file
@@ -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 */
|
||||
Reference in New Issue
Block a user