core-lightning/tools/gen/impl_template
Christian Decker 2519f934aa tlv: Add validity check codegen for the tlv namespaces
Since the parser itself just parses and doesn't include validation anymore we
need to put that functionality somewhere. The validation consists of enforcing
that the types are in monotonically increasing order without duplicates and
that for the even types we know how to handle it.
2019-11-22 04:40:25 +00:00

464 lines
13 KiB
Plaintext

/* This file was generated by generate-wire.py */
/* Do not modify this file! Modify the _csv file it was generated from. */
/* Original template can be found at tools/gen/impl_template */
#include <${header_filename}>
#include <assert.h>
#include <ccan/array_size/array_size.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <stdio.h>
#ifndef SUPERVERBOSE
#define SUPERVERBOSE(...)
#endif
% for comment in top_comments:
/*${comment} */
% endfor
% for enum_set in enum_sets:
const char *${enum_set['name']}_name(int e)
{
static char invalidbuf[sizeof("INVALID ") + STR_MAX_CHARS(e)];
switch ((enum ${enum_set['name']})e) {
% for msg in enum_set['set']:
case ${msg.enum_name()}: return "${msg.enum_name()}";
% endfor
}
snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e);
return invalidbuf;
}
% endfor
## START PARTIALS
## Subtype and TLV-msg towire_
<%def name="towire_subtype_field(fieldname, f, ptr)">\
% if f.is_array() or f.is_varlen():
% if f.type_obj.has_array_helper():
towire_${f.type_obj.name}_array(${ptr}, ${fieldname}, ${f.size('tal_count(' + fieldname + ')')});
% else:
for (size_t i = 0; i < ${f.size('tal_count(' + fieldname + ')')}; i++)
% if f.type_obj.is_assignable() or f.type_obj.has_len_fields():
towire_${f.type_obj.name}(${ptr}, ${fieldname}[i]);
% else:
towire_${f.type_obj.name}(${ptr}, ${fieldname} + i);
% endif
% endif
% elif f.len_field_of:
towire_${f.type_obj.name}(${ptr}, ${f.name});
% else:
towire_${f.type_obj.name}(${ptr}, ${'' if f.type_obj.is_assignable() else '&'}${fieldname});
% endif
</%def>
## Subtype and TLV-msg fromwire
<%def name="fromwire_subtype_field(fieldname, f, ctx)">\
<%
type_ = f.type_obj.name
typename = f.type_obj.type_name()
if f.type_obj.is_varsize():
typename += ' *'
%>\
% if f.is_varlen():
${fieldname} = ${f.size('*plen')} ? tal_arr(${ctx}, ${typename}, ${f.size('*plen')}) : NULL;
% endif
% if f.is_array() or f.is_varlen():
% if f.type_obj.has_array_helper():
fromwire_${type_}_array(cursor, plen, ${fieldname}, ${f.size('*plen')});
% else:
% if f.is_implicit_len():
for (size_t i = 0; *plen != 0; i++)
% else:
for (size_t i = 0; i < ${f.size()}; i++)
% endif
% if f.type_obj.is_assignable():
(${fieldname})[i] = fromwire_${type_}(cursor, plen);
% elif f.is_varlen() and f.type_obj.is_varsize():
(${fieldname})[i] = fromwire_${type_}(${ctx}, cursor, plen);
% else:
fromwire_${type_}(cursor, plen, ${fieldname} + i);
% endif
% endif
% else:
% if f.type_obj.is_assignable():
${ f.name if f.len_field_of else fieldname} = fromwire_${type_}(cursor, plen);
% elif f.type_obj.is_varsize():
${fieldname} = *fromwire_${type_}(${ctx}, cursor, plen);
% else:
fromwire_${type_}(cursor, plen, &${fieldname});
% endif
%endif
</%def>
## END PARTIALS
## FIXME: extract out partials for the method declarations
## (shared between here and header_template)
% for subtype in subtypes: ## START Subtypes
/* SUBTYPE: ${subtype.name.upper()} */
% for c in subtype.type_comments:
/*${c} */
% endfor
<%
static = '' if options.expose_subtypes else 'static '
%>\
${static}void towire_${subtype.name}(u8 **p, const ${subtype.type_name()} *${subtype.name})
{
% for f in subtype.get_len_fields():
${f.type_obj.type_name()} ${f.name} = tal_count(${subtype.name}->${f.len_field_of});
% endfor
% for f in subtype.fields.values():
% for c in f.field_comments:
/*${c} */
% endfor
<%
fieldname = '{}->{}'.format(subtype.name,f.name)
%>\
${towire_subtype_field(fieldname, f, 'p')}\
% endfor
}
% if subtype.is_varsize():
${static}${subtype.type_name()} *
fromwire_${subtype.name}(const tal_t *ctx, const u8 **cursor, size_t *plen)
% else:
${static}void fromwire_${subtype.name}(${'const tal_t *ctx, ' if subtype.needs_context() else ''}const u8 **cursor, size_t *plen, ${subtype.type_name()} *${subtype.name})
% endif
{
% if subtype.is_varsize():
${subtype.type_name()} *${subtype.name} = tal(ctx, ${subtype.type_name()});
% endif
## Length field declarations
% for f in subtype.get_len_fields():
${f.type_obj.type_name()} ${f.name};
% endfor
% for f in subtype.fields.values():
% for c in f.field_comments:
/*${c} */
% endfor
<%
fieldname = '{}->{}'.format(subtype.name,f.name)
ctx = subtype.name
%> \
${fromwire_subtype_field(fieldname, f, ctx)}\
% endfor
% if subtype.is_varsize():
return ${subtype.name};
% endif
}
% endfor ## END Subtypes
<%def name="fromwire_phrase(f, type_, varsized)" >\
%if f.type_obj.is_assignable():
*${f.name} = fromwire_${type_}(&cursor, &plen);
% elif varsized:
*${f.name} = fromwire_${type_}(ctx, &cursor, &plen);
% else:
fromwire_${type_}(${'ctx, ' if f.needs_context() else ''}&cursor, &plen, ${'*' if f.is_optional else ''}${f.name});
% endif
</%def>
% for tlv in tlvs.values(): ## START TLV's
${tlv.type_name()} *${tlv.struct_name()}_new(const tal_t *ctx)
{
/* Initialize everything to NULL. (Quiet, C pedants!) */
${tlv.type_name()} *inst = talz(ctx, struct ${tlv.struct_name()});
/* Initialized the fields to an empty array. */
inst->fields = tal_arr(inst, struct tlv_field, 0);
return inst;
}
% for msg in tlv.messages.values():
/* ${tlv.name.upper()} MSG: ${msg.name} */
static u8 *towire_${msg.struct_name()}(const tal_t *ctx, const void *vrecord)
{
const struct ${tlv.struct_name()} *r = vrecord;
u8 *ptr;
if (!r->${msg.name})
return NULL;
% for f in msg.get_len_fields():
${f.type_obj.type_name()} ${f.name} = tal_count(r->${msg.name}->${f.len_field_of});
% endfor
ptr = tal_arr(ctx, u8, 0);
% for f in msg.fields.values():
<% fieldname = 'r->{}->{}'.format(msg.name, f.name) %>\
${towire_subtype_field(fieldname, f, '&ptr')}\
% endfor
return ptr;
}
static void fromwire_${msg.struct_name()}(const u8 **cursor, size_t *plen, void *vrecord)
{
struct ${tlv.struct_name()} *r = vrecord;
## Length field declarations
% for f in msg.get_len_fields():
${f.type_obj.type_name()} ${f.name};
% endfor
r->${msg.name} = tal(r, struct ${msg.struct_name()});
% for f in msg.fields.values():
<%
fieldname = 'r->{}->{}'.format(msg.name, f.name)
ctx = 'r->{}'.format(msg.name)
%>\
${fromwire_subtype_field(fieldname, f, ctx)}\
% endfor
}
% endfor
<% static = '' if tlv.name in options.expose_tlv_type else 'static '
%>\
${static}const struct tlv_record_type tlvs_${tlv.name}[] = {
% for msg in tlv.ordered_msgs():
{ ${msg.number}, towire_${msg.struct_name()}, fromwire_${msg.struct_name()} },
% endfor
};
bool fromwire_${tlv.name}(const u8 **cursor, size_t *max, struct ${tlv.struct_name()} *record)
{
size_t num_types = ${len(tlv.messages)};
const struct tlv_record_type *types = tlvs_${tlv.name};
while (*max > 0) {
struct tlv_field field;
/* BOLT #1:
*
* A `varint` is a variable-length, unsigned integer encoding
* using the [BigSize](#appendix-a-bigsize-test-vectors)
* format
*/
field.numtype = fromwire_bigsize(cursor, max);
/* BOLT #1:
* - if a `type` or `length` is not minimally encoded:
* - MUST fail to parse the `tlv_stream`.
*/
if (!*cursor) {
SUPERVERBOSE("type");
goto fail;
}
field.length = fromwire_bigsize(cursor, max);
/* BOLT #1:
* - if a `type` or `length` is not minimally encoded:
* - MUST fail to parse the `tlv_stream`.
*/
if (!*cursor) {
SUPERVERBOSE("length");
goto fail;
}
/* BOLT #1:
* - if `length` exceeds the number of bytes remaining in the
* message:
* - MUST fail to parse the `tlv_stream`.
*/
if (field.length > *max) {
SUPERVERBOSE("value");
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];
if (field.meta) {
/* Length of message can't exceed 16 bits anyway. */
size_t tlvlen = field.length;
field.meta->fromwire(cursor, &tlvlen, record);
if (!*cursor)
goto fail;
/* BOLT #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) {
SUPERVERBOSE("greater than encoding length");
goto fail;
}
} else {
/* 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;
tal_arr_expand(&record->fields, field);
}
return true;
fail:
fromwire_fail(cursor, max);
return false;
}
int ${tlv.name}_is_valid(const struct ${tlv.struct_name()} *record)
{
size_t numfields = tal_count(record->fields);
bool first = true;
u64 prev_type = 0;
for (int i=0; i<numfields; i++) {
struct tlv_field *f = &record->fields[i];
if (f->numtype % 2 == 0 && f->meta == NULL) {
/* BOLT #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.
*/
SUPERVERBOSE("Unknown even type in TLV");
return i;
} else if (!first && f->numtype <= prev_type) {
/* BOLT #1:
* - if decoded `type`s are not monotonically-increasing:
* - MUST fail to parse the `tlv_stream`.
*/
if (f->numtype == prev_type)
SUPERVERBOSE("duplicate tlv type");
else
SUPERVERBOSE("invalid ordering");
return i;
}
first = false;
prev_type = f->numtype;
}
return -1;
}
% endfor ## END TLV's
% for msg in messages: ## START Wire Messages
% if msg.if_token:
#if ${msg.if_token}
% endif
/* WIRE: ${msg.name.upper()} */
% for c in msg.msg_comments:
/*${c} */
% endfor
u8 *towire_${msg.name}(const tal_t *ctx${''.join([f.arg_desc_to() for f in msg.fields.values()])})
{
## FIXME: we're ignoring TLV's rn
% for f in msg.get_len_fields():
${f.type_obj.type_name()} ${f.name} = tal_count(${f.len_field_of});
% endfor
u8 *p = tal_arr(ctx, u8, 0);
towire_u16(&p, ${msg.enum_name()});
% for f in msg.fields.values():
% for c in f.field_comments:
/*${c} */
% endfor
% if f.is_array() or f.is_varlen():
% if f.type_obj.has_array_helper():
towire_${f.type_obj.name}_array(&p, ${f.name}, ${f.size('tal_count(' + f.name + ')')});
% else:
for (size_t i = 0; i < ${f.size('tal_count(' + f.name + ')')}; i++)
% if f.type_obj.is_assignable() or f.type_obj.is_varsize():
towire_${f.type_obj.name}(&p, ${f.name}[i]);
% else:
towire_${f.type_obj.name}(&p, ${f.name} + i);
% endif
% endif
% elif f.type_obj.is_tlv():
towire_tlvs(&p, tlvs_${f.type_obj.tlv.name}, ARRAY_SIZE(tlvs_${f.type_obj.tlv.name}), ${f.name});
% elif f.is_optional: ## is optional?
if (!${f.name})
towire_bool(&p, false);
else {
towire_bool(&p, true);
towire_${f.type_obj.name}(&p, ${'*' if f.type_obj.is_assignable() else ''}${f.name});
}
% else: ## all other cases
towire_${f.type_obj.name}(&p, ${f.name});
% endif
% endfor
return memcheck(p, tal_count(p));
}
bool fromwire_${msg.name}(${'const tal_t *ctx, ' if msg.needs_context() else ''}const void *p${''.join([f.arg_desc_from() for f in msg.fields.values()])})
{
% if msg.get_len_fields():
% for f in msg.get_len_fields():
${f.type_obj.type_name()} ${f.name};
% endfor
% endif
const u8 *cursor = p;
size_t plen = tal_count(p);
if (fromwire_u16(&cursor, &plen) != ${msg.enum_name()})
return false;
% for f in msg.fields.values():
<%
typename = f.type_obj.type_name()
if f.type_obj.is_varsize():
typename = typename + ' *'
type_ = f.type_obj.name
varsized = f.type_obj.is_varsize()
%> \
% for c in f.field_comments:
/*${c} */
% endfor
% if f.is_varlen():
// 2nd case ${f.name}
*${f.name} = ${f.size('plen')} ? tal_arr(ctx, ${typename}, ${f.size('plen')}) : NULL;
% endif
% if f.len_field_of:
${f.name} = fromwire_${type_}(&cursor, &plen);
% elif f.type_obj.is_tlv():
fromwire_tlvs(&cursor, &plen, tlvs_${f.type_obj.tlv.name}, ARRAY_SIZE(tlvs_${f.type_obj.tlv.name}), ${f.name});
% elif f.is_array() or f.is_varlen():
% if f.type_obj.has_array_helper():
fromwire_${type_}_array(&cursor, &plen, ${'*' if f.is_varlen() else ''}${f.name}, ${f.size('plen')});
% else:
% if f.is_implicit_len():
for (size_t i = 0; plen != 0; i++)
% else:
for (size_t i = 0; i < ${f.size()}; i++)
% endif
% if not varsized and not f.type_obj.is_assignable():
% if f.is_varlen():
fromwire_${type_}(&cursor, &plen, *${f.name} + i);
% else:
fromwire_${type_}(&cursor, &plen, &(${f.name}[i]));
% endif
% else:
(${'' if f.type_obj.is_assignable() and f.is_array() else '*'}${f.name})[i] = fromwire_${type_}(${'*'+f.name+', ' if varsized else ''}&cursor, &plen);
% endif
% endif
% else:
% if not f.is_optional:
${fromwire_phrase(f, type_, varsized)}\
% else: ## Start optional
if (!fromwire_bool(&cursor, &plen))
*${f.name} = NULL;
else {
% if not varsized:
*${f.name} = tal(ctx, ${typename});
% endif
${'*' if f.type_obj.is_assignable() else ''}${fromwire_phrase(f, type_, varsized)}\
}
% endif ## End optional
% endif
% endfor
return cursor != NULL;
}
% if msg.if_token:
#endif /* ${msg.if_token} */
% endif
% endfor ## END Wire Messages