From: Alan T. DeKok Date: Wed, 13 Nov 2019 21:27:59 +0000 (-0500) Subject: pass TLV stack to struct encoder X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d79665d950bcf9c87b6745e8bbbdfb7fa37321e9;p=thirdparty%2Ffreeradius-server.git pass TLV stack to struct encoder along with a protocol-specific encode_value() callback. Which for now is only used in limited cases, because of poor interation between the requirements of the struct encoder, and the tlv_stack magic. --- diff --git a/src/lib/util/struct.c b/src/lib/util/struct.c index d30a3bf326d..f2ac117e6fe 100644 --- a/src/lib/util/struct.c +++ b/src/lib/util/struct.c @@ -258,13 +258,15 @@ done: ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, - fr_dict_attr_t const *parent, fr_cursor_t *cursor) + fr_dict_attr_t const **tlv_stack, unsigned int depth, + fr_cursor_t *cursor, void *encoder_ctx, + fr_encode_value_t encode_value) { ssize_t len; unsigned int child_num = 1; uint8_t *p = out; VALUE_PAIR const *vp = fr_cursor_current(cursor); - fr_dict_attr_t const *key_da; + fr_dict_attr_t const *key_da, *parent; if (!vp) { fr_strerror_printf("%s: Can't encode empty struct", __FUNCTION__); @@ -272,6 +274,7 @@ ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, } VP_VERIFY(vp); + parent = tlv_stack[depth]; if (parent->type != FR_TYPE_STRUCT) { fr_strerror_printf("%s: Expected type \"struct\" got \"%s\"", __FUNCTION__, @@ -323,29 +326,49 @@ ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, } /* - * Encode fixed-size octets fields so that they - * are exactly the fixed size, UNLESS the entire - * output is truncated. + * Call the protocol encoder, but ONLY if there + * are special flags required. */ - if ((vp->da->type == FR_TYPE_OCTETS) && vp->da->flags.length) { - size_t mylen = vp->da->flags.length; + if (encode_value && !child->flags.extra && child->flags.subtype) { + ssize_t slen; - if (mylen > outlen) mylen = outlen; - - if (vp->vp_length < mylen) { - memcpy(p, vp->vp_ptr, vp->vp_length); - memset(p + vp->vp_length, 0, mylen - vp->vp_length); - } else { - memcpy(p, vp->vp_ptr, mylen); - } - len = mylen; + tlv_stack[depth + 1] = child; + slen = encode_value(p, outlen, tlv_stack, depth + 1, cursor, encoder_ctx); + if (slen < 0) return slen; + len = slen; + vp = fr_cursor_current(cursor); } else { /* - * Determine the nested type and call the appropriate encoder + * Encode fixed-size octets fields so that they + * are exactly the fixed size, UNLESS the entire + * output is truncated. */ - len = fr_value_box_to_network(NULL, p, outlen, &vp->data); - if (len <= 0) return -1; + if ((vp->da->type == FR_TYPE_OCTETS) && vp->da->flags.length) { + size_t mylen = vp->da->flags.length; + + if (mylen > outlen) mylen = outlen; + + if (vp->vp_length < mylen) { + memcpy(p, vp->vp_ptr, vp->vp_length); + memset(p + vp->vp_length, 0, mylen - vp->vp_length); + } else { + memcpy(p, vp->vp_ptr, mylen); + } + len = mylen; + + } else { + /* + * Determine the nested type and call the appropriate encoder + */ + len = fr_value_box_to_network(NULL, p, outlen, &vp->data); + if (len <= 0) return -1; + } + + do { + vp = fr_cursor_next(cursor); + if (!vp || !vp->da->flags.internal) break; + } while (vp != NULL); } if (da_is_key_field(child)) { @@ -356,11 +379,6 @@ ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, outlen -= len; /* Subtract from the buffer we have available */ child_num++; - do { - vp = fr_cursor_next(cursor); - if (!vp || !vp->da->flags.internal) break; - } while (vp != NULL); - /* * Nothing more to do, or we've done all of the * entries in this structure, stop. @@ -383,8 +401,9 @@ ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, */ if ((vp->da->parent->parent == key_da) && (vp->da->parent->type == FR_TYPE_STRUCT)) { - len = fr_struct_to_network(p, outlen, - vp->da->parent, cursor); + tlv_stack[depth + 1] = vp->da->parent; /* hackity hack */ + len = fr_struct_to_network(p, outlen, tlv_stack, depth + 1, + cursor, encoder_ctx, encode_value); if (len < 0) return len; return (p - out) + len; } diff --git a/src/lib/util/struct.h b/src/lib/util/struct.h index 0bc28a48030..1c4b016b75c 100644 --- a/src/lib/util/struct.h +++ b/src/lib/util/struct.h @@ -34,8 +34,13 @@ extern "C" { ssize_t fr_struct_from_network(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, fr_dict_attr_t const **child) CC_HINT(nonnull(2,3,4)); -ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, - fr_dict_attr_t const *parent, fr_cursor_t *cursor) CC_HINT(nonnull); + +typedef ssize_t (*fr_encode_value_t)(uint8_t *out, size_t outlen, fr_dict_attr_t const **tlv_stack, unsigned int depth, + fr_cursor_t *cursor, void *encoder_ctx); + +ssize_t fr_struct_to_network(uint8_t *out, size_t outlen, fr_dict_attr_t const **tlv_stack, unsigned int depth, + fr_cursor_t *cursor, void *encoder_ctx, + fr_encode_value_t encode_value) CC_HINT(nonnull(1,3,5)); VALUE_PAIR *fr_unknown_from_network(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len) CC_HINT(nonnull); diff --git a/src/protocols/dhcpv6/encode.c b/src/protocols/dhcpv6/encode.c index 62d010c0aea..b31a82372ee 100644 --- a/src/protocols/dhcpv6/encode.c +++ b/src/protocols/dhcpv6/encode.c @@ -134,7 +134,7 @@ static inline ssize_t encode_option_hdr(uint8_t *out, size_t outlen, uint16_t op static ssize_t encode_struct(uint8_t *out, size_t outlen, fr_dict_attr_t const **tlv_stack, unsigned int depth, - fr_cursor_t *cursor, UNUSED void *encoder_ctx) + fr_cursor_t *cursor, void *encoder_ctx) { VP_VERIFY(fr_cursor_current(cursor)); FR_PROTO_STACK_PRINT(tlv_stack, depth); @@ -150,7 +150,7 @@ static ssize_t encode_struct(uint8_t *out, size_t outlen, return PAIR_ENCODE_ERROR; } - return fr_struct_to_network(out, outlen, tlv_stack[depth], cursor); + return fr_struct_to_network(out, outlen, tlv_stack, depth, cursor, encoder_ctx, encode_value); } static ssize_t encode_value(uint8_t *out, size_t outlen, diff --git a/src/protocols/radius/encode.c b/src/protocols/radius/encode.c index 0b1553ecdd8..3de85cace97 100644 --- a/src/protocols/radius/encode.c +++ b/src/protocols/radius/encode.c @@ -33,7 +33,7 @@ RCSID("$Id$") static unsigned int salt_offset = 0; static ssize_t encode_value(uint8_t *out, size_t outlen, - fr_dict_attr_t const **tlv_stack, int depth, + fr_dict_attr_t const **tlv_stack, unsigned int depth, fr_cursor_t *cursor, void *encoder_ctx); static ssize_t encode_rfc_hdr_internal(uint8_t *out, size_t outlen, @@ -524,7 +524,7 @@ static ssize_t encode_tlv_hdr(uint8_t *out, size_t outlen, * < 0, failure. */ static ssize_t encode_value(uint8_t *out, size_t outlen, - fr_dict_attr_t const **tlv_stack, int depth, + fr_dict_attr_t const **tlv_stack, unsigned int depth, fr_cursor_t *cursor, void *encoder_ctx) { size_t offset; @@ -553,7 +553,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen, if (da->type == FR_TYPE_STRUCT) { ssize_t struct_len; - struct_len = fr_struct_to_network(out, outlen, tlv_stack[depth], cursor); + struct_len = fr_struct_to_network(out, outlen, tlv_stack, depth, cursor, encoder_ctx, encode_value); if (struct_len <= 0) return struct_len; vp = fr_cursor_current(cursor); diff --git a/src/tests/unit/protocols/dhcpv6/rfc4704.txt b/src/tests/unit/protocols/dhcpv6/rfc4704.txt index a83452f30e3..d4104098acd 100644 --- a/src/tests/unit/protocols/dhcpv6/rfc4704.txt +++ b/src/tests/unit/protocols/dhcpv6/rfc4704.txt @@ -36,13 +36,13 @@ proto-dictionary dhcpv6 # (with length option-len - 1) # +encode-pair Client-FQDN-Flags = Server-Update, Client-FQDN-Domain-Name = "tapioca01" +match 00 27 00 0c 01 09 74 61 70 69 6f 63 61 30 31 00 + # # TODO: The current API do not support 'struct' attributes with # fields like 'string encode=dns_label'. e.g: Client-FQDN-Domain-Name # -encode-pair Client-FQDN-Flags = Server-Update, Client-FQDN-Domain-Name = "tapioca01" -no match 00 27 00 0c 01 09 74 61 70 69 6f 63 61 30 31 00 - decode-pair - no match Client-FQDN-Flags = Server-Update, Client-FQDN-Domain-Name = "tapioca01"