From: Alan T. DeKok Date: Thu, 31 Oct 2019 19:06:48 +0000 (-0400) Subject: allow 'length=uint16' as a flag X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fa8eef571e43c7ffd6de7d51865b67f67f6fc523;p=thirdparty%2Ffreeradius-server.git allow 'length=uint16' as a flag This is mainly for DHCPv6, but it allows us to auto-encode lengths inside of structs too, which wasn't previously possible. --- diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 0c2052fc821..0715e479e82 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -99,9 +99,11 @@ enum { enum { FLAG_EXTRA_NONE = 0, //!< no extra meaning, should be invalid FLAG_KEY_FIELD, //!< this is a key field for a subsequent struct + FLAG_LENGTH_UINT16, //!< string / octets type is prefixed by uint16 of length }; #define da_is_key_field(_da) ((_da)->flags.extra && ((_da)->flags.subtype == FLAG_KEY_FIELD)) +#define da_is_length_field(_da) ((_da)->flags.extra && ((_da)->flags.subtype == FLAG_LENGTH_UINT16)) extern const size_t dict_attr_sizes[FR_TYPE_MAX + 1][2]; extern fr_dict_t *fr_dict_internal; diff --git a/src/lib/util/dict_print.c b/src/lib/util/dict_print.c index 286fc9441e3..64b5fddd4c4 100644 --- a/src/lib/util/dict_print.c +++ b/src/lib/util/dict_print.c @@ -52,7 +52,7 @@ do { \ FLAG_SET(concat); FLAG_SET(virtual); - if (dict && flags->subtype) { + if (dict && !flags->extra && flags->subtype) { p += snprintf(p, end - p, "%s", fr_table_str_by_value(dict->subtype_table, flags->subtype, "?")); if (p >= end) return -1; } @@ -63,13 +63,15 @@ do { \ } if (flags->extra) { - switch (type) { - case FR_TYPE_UINT8: - case FR_TYPE_UINT16: - case FR_TYPE_UINT32: + switch (flags->subtype) { + case FLAG_KEY_FIELD: p += snprintf(p, end - p, "key,"); break; + case FLAG_LENGTH_UINT16: + p += snprintf(p, end - p, "length=uint16,"); + break; + default: break; } diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index e9c02a270b3..f3cf192f924 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -343,6 +343,19 @@ static int dict_process_flag_field(dict_tokenize_ctx_t *ctx, char *name, fr_type flags->extra = 1; flags->subtype = FLAG_KEY_FIELD; + } else if (strcmp(key, "length") == 0) { + if (!value || (strcmp(value, "uint16") != 0)) { + fr_strerror_printf("The 'length' flag can only be used with value 'uint16'"); + } + + if ((type != FR_TYPE_STRING) && (type != FR_TYPE_OCTETS) && (type != FR_TYPE_UINT32)) { + fr_strerror_printf("The 'length' flag can only be used for attributes of type 'string' or 'octets'"); + return -1; + } + + flags->extra = 1; + flags->subtype = FLAG_LENGTH_UINT16; + } else if ((type == FR_TYPE_DATE) || (type == FR_TYPE_TIME_DELTA)) { flags->length = 4; flags->type_size = FR_TIME_RES_SEC; diff --git a/src/lib/util/dict_validate.c b/src/lib/util/dict_validate.c index 00c6a1c2f58..e15e08441fe 100644 --- a/src/lib/util/dict_validate.c +++ b/src/lib/util/dict_validate.c @@ -160,7 +160,12 @@ bool dict_attr_flags_valid(fr_dict_t *dict, fr_dict_attr_t const *parent, break; } + /* + * DHCPv6 has arrays of string / octets, prefixed + * with a uint16 field of "length". Also, arrays of dns_labels. + */ if (dict->root->attr == FR_PROTOCOL_DHCPV6) { + ALLOW_FLAG(extra); ALLOW_FLAG(subtype); } @@ -221,8 +226,8 @@ bool dict_attr_flags_valid(fr_dict_t *dict, fr_dict_attr_t const *parent, * the data type. */ if (flags->extra) { - if (flags->subtype != FLAG_KEY_FIELD) { - fr_strerror_printf("The 'key' flag cannot be used with any other flags."); + if ((flags->subtype != FLAG_KEY_FIELD) && (flags->subtype != FLAG_LENGTH_UINT16)) { + fr_strerror_printf("The 'key' and 'length' flags cannot be used with any other flags."); return false; } @@ -230,17 +235,39 @@ bool dict_attr_flags_valid(fr_dict_t *dict, fr_dict_attr_t const *parent, case FR_TYPE_UINT8: case FR_TYPE_UINT16: case FR_TYPE_UINT32: - ALLOW_FLAG(extra); - ALLOW_FLAG(subtype); + if (flags->subtype != FLAG_KEY_FIELD) { + fr_strerror_printf("Invalid type for extra flag."); + return false; + } + if (parent->type != FR_TYPE_STRUCT) { fr_strerror_printf("The 'key' flag can only be used inside of a 'struct'."); return false; } + ALLOW_FLAG(extra); + ALLOW_FLAG(subtype); + break; + + case FR_TYPE_OCTETS: + if (flags->length != 0) { + fr_strerror_printf("Cannot use [..] and length=uint16"); + return false; + } + /* FALL-THROUGH */ + + case FR_TYPE_STRING: + if (flags->subtype != FLAG_LENGTH_UINT16) { + fr_strerror_printf("Invalid type for extra flag."); + return false; + } + + ALLOW_FLAG(extra); + ALLOW_FLAG(array); + ALLOW_FLAG(subtype); break; default: - fr_strerror_printf("The 'key' flag can only be used with unsigned integer data types"); return -1; }