Browse Source

wire/tlvstream: routines to marshal/unmarshal TLV streams.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pull/2938/head
Rusty Russell 6 years ago
parent
commit
7cf0006c78
  1. 2
      wire/Makefile
  2. 139
      wire/tlvstream.c
  3. 31
      wire/tlvstream.h

2
wire/Makefile

@ -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

@ -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

@ -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…
Cancel
Save