From 344ff9cba451f53edb4209e39188dcfc087e1d4e Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Thu, 28 Nov 2024 17:17:16 -0600 Subject: [PATCH] Rename BEGIN-TLV and END-TLV to BEGIN and END, and make them work for TLVs too --- doc/antora/modules/reference/nav.adoc | 4 +- .../pages/dictionary/begin-protocol.adoc | 2 +- .../dictionary/{begin-tlv.adoc => begin.adoc} | 42 +- .../reference/pages/dictionary/end-tlv.adoc | 20 - .../reference/pages/dictionary/end.adoc | 21 + .../reference/pages/dictionary/index.adoc | 4 +- man/man5/dictionary.5 | 4 +- .../dictionary/eap/aka-sim/dictionary.rfc4187 | 5 +- .../freeradius/dictionary.freeradius.internal | 4 +- .../dictionary.freeradius.internal.ippool | 5 +- .../dictionary.freeradius.internal.password | 9 +- .../ldap/dictionary.freeradius.internal | 4 +- share/dictionary/snmp/dictionary.freeradius | 16 +- src/lib/util/dict.h | 3 + src/lib/util/dict_fixup.c | 35 +- src/lib/util/dict_fixup_priv.h | 2 +- src/lib/util/dict_tokenize.c | 365 +++++++++++------- 17 files changed, 320 insertions(+), 225 deletions(-) rename doc/antora/modules/reference/pages/dictionary/{begin-tlv.adoc => begin.adoc} (52%) delete mode 100644 doc/antora/modules/reference/pages/dictionary/end-tlv.adoc create mode 100644 doc/antora/modules/reference/pages/dictionary/end.adoc diff --git a/doc/antora/modules/reference/nav.adoc b/doc/antora/modules/reference/nav.adoc index 865bc07afe036..0c64019fd70cb 100644 --- a/doc/antora/modules/reference/nav.adoc +++ b/doc/antora/modules/reference/nav.adoc @@ -104,8 +104,8 @@ *** xref:dictionary/vendor.adoc[VENDOR] *** xref:dictionary/begin-protocol.adoc[BEGIN-PROTOCOL] *** xref:dictionary/end-protocol.adoc[END-PROTOCOL] -*** xref:dictionary/begin-tlv.adoc[BEGIN-TLV] -*** xref:dictionary/end-tlv.adoc[END-TLV] +*** xref:dictionary/begin.adoc[BEGIN] +*** xref:dictionary/end.adoc[END] *** xref:dictionary/begin-vendor.adoc[BEGIN-VENDOR] *** xref:dictionary/end-vendor.adoc[END-VENDOR] diff --git a/doc/antora/modules/reference/pages/dictionary/begin-protocol.adoc b/doc/antora/modules/reference/pages/dictionary/begin-protocol.adoc index f7f063ee9e5c3..8593e4e89e911 100644 --- a/doc/antora/modules/reference/pages/dictionary/begin-protocol.adoc +++ b/doc/antora/modules/reference/pages/dictionary/begin-protocol.adoc @@ -25,7 +25,7 @@ END-PROTOCOL RADIUS ---- Note that unlike xref:dictionary/begin-vendor.adoc[END-VENDOR] and -xref:dictionary/begin-tlv.adoc[END-TLV], it is not possible to omit +xref:dictionary/begin.adoc[END], it is not possible to omit the `BEGIN-PROTOCOL` keyword. // Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0. diff --git a/doc/antora/modules/reference/pages/dictionary/begin-tlv.adoc b/doc/antora/modules/reference/pages/dictionary/begin.adoc similarity index 52% rename from doc/antora/modules/reference/pages/dictionary/begin-tlv.adoc rename to doc/antora/modules/reference/pages/dictionary/begin.adoc index 87937bbd01270..e2e250f01d23d 100644 --- a/doc/antora/modules/reference/pages/dictionary/begin-tlv.adoc +++ b/doc/antora/modules/reference/pages/dictionary/begin.adoc @@ -1,40 +1,40 @@ -= The BEGIN-TLV keyword += The BEGIN keyword .Syntax ---- -BEGIN-TLV +BEGIN ---- .Description -The `BEGIN-TLV` keyword starts a "nested" set of -xref:dictionary/attribute.adoc[ATTRIBUTE] definitions wwhich are all -for a particular parent attribute of type `tlv`. +The `BEGIN` keyword starts a "nested" set of +xref:dictionary/attribute.adoc[ATTRIBUTE] definitions which are all +for a particular parent attribute of type `tlv` or `struct`. :: The name of the parent attribute. + -The tlv must have previously been created in anref:dictionary/attribute.adoc[ATTRIBUTE] definition. +The tlv or struct must have previously been created in anref:dictionary/attribute.adoc[ATTRIBUTE] definition. -The `BEGIN-TLV` keyword must always be paired with a matching xref:dictionary/end-tlv.adoc[END-TLV] keyword. +The `BEGIN` keyword must always be paired with a matching xref:dictionary/end.adoc[END] keyword. -Within the context of a ``BEGIN-TLV` block, the numbers for each +Within the context of a ``BEGIN` block, the numbers for each xref:dictionary/attribute.adoc[ATTRIBUTE] definition are relative to -the parent TLV. +the parent tlv or struct. .Example ---- ATTRIBUTE Foo 2 tlv -BEGIN-TLV Foo +BEGIN Foo ATTRIBUTE Bar 1 string -END-TLV Foo +END Foo ---- This example defines an attribute `Foo.Bar`, with OID `2.1`. == Purpose -The `BEGIN-TLV` keyword exists for efficiency. It is not strictly -needed, but without a `BEGIN-TLV` keyword, every +The `BEGIN` keyword exists for efficiency. It is not strictly +needed, but without a `BEGIN` keyword, every xref:dictionary/attribute.adoc[ATTRIBUTE] name would need to contain the parent attribute name, as in the following example. @@ -46,19 +46,19 @@ ATTRIBUTE Foo.bar 2.1 string == Nesting -It is possible to nest multiple `BEGIN-TLV` keywords, so long as each -one is paired with a matching xref:dictionary/end-tlv.adoc[END-TLV] keyword. +It is possible to nest multiple `BEGIN` keywords, so long as each +one is paired with a matching xref:dictionary/end.adoc[END] keyword. -.Example of nested BEGIN-TLV +.Example of nested BEGIN ---- ATTRIBUTE Foo 2 tlv -BEGIN-TLV Foo +BEGIN Foo ATTRIBUTE Bar 1 string ATTRIBUTE Baz 2 tlv -BEGIN-TLV Baz +BEGIN Baz ATTRIBUTE Such 4 ipaddr -END-TLV Baz -END-TLV Foo +END Baz +END Foo ---- The above example is equivalent to the example below. @@ -73,7 +73,7 @@ ATTRIBUTE Such .2.4 ipaddr For short entries, it can be simpler to use the full name an OID. However, for complex dictionaries, it is almost always clearer to use -`BEGIN-TLV` and xref:dictionary/end-tlv.adoc[END-TLV]. +`BEGIN` and xref:dictionary/end.adoc[END]. // Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0. // This documentation was developed by Network RADIUS SAS. diff --git a/doc/antora/modules/reference/pages/dictionary/end-tlv.adoc b/doc/antora/modules/reference/pages/dictionary/end-tlv.adoc deleted file mode 100644 index da6eac5c8f5a2..0000000000000 --- a/doc/antora/modules/reference/pages/dictionary/end-tlv.adoc +++ /dev/null @@ -1,20 +0,0 @@ -= The END-TLV keyword - -.Syntax ----- -END-TLV ----- - -.Description - -The `END-TLV` keyword finished a "nested" set of -xref:dictionary/attribute.adoc[ATTRIBUTE] definitions which are all -for a particular `tlv` parent attribute. - -:: The name of the xref:dictionary/attribute.adoc[ATTRIBUTE] -+ -The dictionary must have previously contained a matching -xref:dictionary/begin-tlv.adoc[BEGIN-TLV]. - -// Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0. -// This documentation was developed by Network RADIUS SAS. diff --git a/doc/antora/modules/reference/pages/dictionary/end.adoc b/doc/antora/modules/reference/pages/dictionary/end.adoc new file mode 100644 index 0000000000000..bd8af2f9af10b --- /dev/null +++ b/doc/antora/modules/reference/pages/dictionary/end.adoc @@ -0,0 +1,21 @@ += The END keyword + +.Syntax +---- +END [] +---- + +.Description + +The `END` keyword finished a "nested" set of +xref:dictionary/attribute.adoc[ATTRIBUTE] definitions which are all +for a particular `tlv` or `struct` parent attribute. + +[]:: The name of the xref:dictionary/attribute.adoc[ATTRIBUTE]. This is useful +for validation purposes, but may be omitted. ++ +The dictionary must have previously contained a matching +xref:dictionary/begin.adoc[BEGIN]. + +// Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0. +// This documentation was developed by Network RADIUS SAS. diff --git a/doc/antora/modules/reference/pages/dictionary/index.adoc b/doc/antora/modules/reference/pages/dictionary/index.adoc index 54725696ce2e0..d08c74892b454 100644 --- a/doc/antora/modules/reference/pages/dictionary/index.adoc +++ b/doc/antora/modules/reference/pages/dictionary/index.adoc @@ -155,8 +155,8 @@ The following keywords still XXX |===== | xref:dictionary/begin-protocol.adoc[BEGIN-PROTOCOL] | Begin defining a protocol dictionary | xref:dictionary/end-protocol.adoc[END-PROTOCOL] | End a protocol dictionary -| xref:dictionary/begin-tlv.adoc[BEGIN-TLV] | Begin defining children of a `tlv` data type -| xref:dictionary/end-tlv.adoc[END-TLV] | End defining children of a `tlv` data type +| xref:dictionary/begin.adoc[BEGIN] | Begin defining children of a `tlv` data type +| xref:dictionary/end.adoc[END] | End defining children of a `tlv` data type | xref:dictionary/begin-vendor.adoc[BEGIN-VENDOR] | Begin defining vendor-specific attributes | xref:dictionary/end-vendor.adoc[END-VENDOR] | End defining vendor-specific attributes |===== diff --git a/man/man5/dictionary.5 b/man/man5/dictionary.5 index da353579c94e4..9eb28ccad6408 100644 --- a/man/man5/dictionary.5 +++ b/man/man5/dictionary.5 @@ -260,12 +260,12 @@ Include dictionary entries from the file \fIfilename\fP. The \fIfilename\fP is taken as relative to the location of the file which is asking for the inclusion. .TP 0.5i -.B BEGIN-TLV name +.B BEGIN name This feature is supported for backwards compatibility with older dictionaries. It should not be used. The new "oid" form for defining the attribute number should be used instead. .TP 0.5i -.B END-TLV name +.B END name This feature is supported for backwards compatibility with older dictionaries. It should not be used. The new "oid" form for defining the attribute number should be used instead. diff --git a/share/dictionary/eap/aka-sim/dictionary.rfc4187 b/share/dictionary/eap/aka-sim/dictionary.rfc4187 index 85164f0a4491f..56eed1cd49b74 100644 --- a/share/dictionary/eap/aka-sim/dictionary.rfc4187 +++ b/share/dictionary/eap/aka-sim/dictionary.rfc4187 @@ -51,14 +51,14 @@ ATTRIBUTE Encr-Data 130 tlv encrypt=aes-cbc # # These attributes are reversibly encrypted # -BEGIN-TLV Encr-Data +BEGIN Encr-Data ATTRIBUTE Padding 6 octets # 4, 8, 12 bytes ATTRIBUTE Counter 19 short ATTRIBUTE Counter-Too-Small 20 bool ATTRIBUTE Nonce-S 21 octets[16] ATTRIBUTE Next-Pseudonym 132 string ATTRIBUTE Next-Reauth-ID 133 string -END-TLV Encr-Data +END Encr-Data ATTRIBUTE Checkcode 134 octets # 0, 20 or 32 bytes ATTRIBUTE Result-Ind 135 bool @@ -66,4 +66,3 @@ ATTRIBUTE Bidding 136 short VALUE Bidding Prefer-AKA 0 VALUE Bidding Prefer-AKA-Prime 32768 # D Bit - diff --git a/share/dictionary/freeradius/dictionary.freeradius.internal b/share/dictionary/freeradius/dictionary.freeradius.internal index ddbe441b2f104..153817e9909ee 100644 --- a/share/dictionary/freeradius/dictionary.freeradius.internal +++ b/share/dictionary/freeradius/dictionary.freeradius.internal @@ -421,7 +421,7 @@ ATTRIBUTE Log-Type 1896 integer ATTRIBUTE WiMAX-MN-NAI 1900 string ATTRIBUTE TLS-Certificate 1901 tlv -BEGIN-TLV TLS-Certificate +BEGIN TLS-Certificate ATTRIBUTE Serial 1 octets ATTRIBUTE Signature 2 octets ATTRIBUTE Signature-Algorithm 3 string @@ -439,7 +439,7 @@ ATTRIBUTE X509v3-Extended-Key-Usage 14 string ATTRIBUTE X509v3-Subject-Key-Identifier 15 string ATTRIBUTE X509v3-Authority-Key-Identifier 16 string ATTRIBUTE X509v3-Basic-Constraints 17 string -END-TLV TLS-Certificate +END TLS-Certificate ATTRIBUTE TLS-PSK-Identity 1933 string ATTRIBUTE TLS-Session-Certificate-File 1934 string diff --git a/share/dictionary/freeradius/dictionary.freeradius.internal.ippool b/share/dictionary/freeradius/dictionary.freeradius.internal.ippool index 2996375f9df04..979621b020f3f 100644 --- a/share/dictionary/freeradius/dictionary.freeradius.internal.ippool +++ b/share/dictionary/freeradius/dictionary.freeradius.internal.ippool @@ -15,12 +15,11 @@ FLAGS internal ATTRIBUTE IP-Pool 5100 tlv -BEGIN-TLV IP-Pool +BEGIN IP-Pool ATTRIBUTE Name 1 string # Generic identifier for the IP pool to allocate from ATTRIBUTE Name-NA 2 string # DHCPv6 - Non-Temporary association pool ATTRIBUTE Name-PD 3 string # DHCPv6 - Prefix-deligation pool ATTRIBUTE Name-TA 4 string # DHCPv6 - Temporary association pool ATTRIBUTE Range 6 string -END-TLV IP-Pool - +END IP-Pool diff --git a/share/dictionary/freeradius/dictionary.freeradius.internal.password b/share/dictionary/freeradius/dictionary.freeradius.internal.password index 2d184710aea5b..8b14b116f6675 100644 --- a/share/dictionary/freeradius/dictionary.freeradius.internal.password +++ b/share/dictionary/freeradius/dictionary.freeradius.internal.password @@ -17,7 +17,7 @@ FLAGS internal ATTRIBUTE Password 2004 tlv -BEGIN-TLV Password +BEGIN Password ATTRIBUTE With-Header 1 string ATTRIBUTE Cleartext 2 string secret @@ -64,14 +64,13 @@ ATTRIBUTE SSHA3-256 28 octets ATTRIBUTE SSHA3-384 29 octets ATTRIBUTE SSHA3-512 30 octets -END-TLV Password +END Password # TOTP passwords and secrets ATTRIBUTE TOTP 2005 tlv -BEGIN-TLV TOTP +BEGIN TOTP ATTRIBUTE Secret 1 string secret ATTRIBUTE Key 2 octets secret ATTRIBUTE From-User 3 string -END-TLV TOTP - +END TOTP diff --git a/share/dictionary/ldap/dictionary.freeradius.internal b/share/dictionary/ldap/dictionary.freeradius.internal index 056823029d9d8..13816f583cc9f 100644 --- a/share/dictionary/ldap/dictionary.freeradius.internal +++ b/share/dictionary/ldap/dictionary.freeradius.internal @@ -30,7 +30,7 @@ VALUE Packet-Type Do-Not-Respond 255 ATTRIBUTE Sync-Packet-ID 1001 integer ATTRIBUTE LDAP-Sync 1193 tlv -BEGIN-TLV LDAP-Sync +BEGIN LDAP-Sync ATTRIBUTE Cookie 1 octets ATTRIBUTE DN 2 string ATTRIBUTE Filter 3 string @@ -45,4 +45,4 @@ ATTRIBUTE Entry-DN 5 string ATTRIBUTE Entry-UUID 6 octets ATTRIBUTE Original-DN 7 string ATTRIBUTE Directory-Root-DN 8 string -END-TLV LDAP-Sync +END LDAP-Sync diff --git a/share/dictionary/snmp/dictionary.freeradius b/share/dictionary/snmp/dictionary.freeradius index 0a8775f890285..3579ef7b1b261 100644 --- a/share/dictionary/snmp/dictionary.freeradius +++ b/share/dictionary/snmp/dictionary.freeradius @@ -48,14 +48,14 @@ ATTRIBUTE FreeRADIUS-Mib-2 .3.6.1.2.1 tlv # # Everything below is defined in the context of MIB-2 # -BEGIN-TLV 1.3.6.1.2.FreeRADIUS-Mib-2 +BEGIN 1.3.6.1.2.FreeRADIUS-Mib-2 ATTRIBUTE Radius-Mib 67 tlv ATTRIBUTE Radius-Authentication .1 tlv ATTRIBUTE Radius-Auth-Serv-Mib .1.1 tlv ATTRIBUTE Radius-Auth-Serv-Mib-Objects .1.1.1 tlv ATTRIBUTE Radius-Auth-Serv .1.1.1.1 tlv -BEGIN-TLV 67.1.1.1.Radius-Auth-Serv +BEGIN 67.1.1.1.Radius-Auth-Serv ATTRIBUTE Radius-Auth-Serv-Ident 1 string ATTRIBUTE Radius-Auth-Serv-Up-Time 2 integer ATTRIBUTE Radius-Auth-Serv-Reset-Time 3 integer @@ -82,7 +82,7 @@ ATTRIBUTE Radius-Auth-Client-Table-Index .0 integer ATTRIBUTE Radius-Auth-Client-Entry .1 tlv # Client statistics -BEGIN-TLV 15.Radius-Auth-Client-Entry +BEGIN 15.Radius-Auth-Client-Entry ATTRIBUTE Radius-Auth-Client-Index 1 integer ATTRIBUTE Radius-Auth-Client-Address 2 ipaddr ATTRIBUTE Radius-Auth-Client-ID 3 string @@ -95,13 +95,13 @@ ATTRIBUTE Radius-Auth-Serv-Malformed-Access-Requests 9 integer ATTRIBUTE Radius-Auth-Serv-Bad-Authenticators 10 integer ATTRIBUTE Radius-Auth-Serv-Packets-Dropped 11 integer ATTRIBUTE Radius-Auth-Serv-Unknown-Types 12 integer -END-TLV 15.Radius-Auth-Client-Entry +END 15.Radius-Auth-Client-Entry ATTRIBUTE Radius-Auth-Client-Ext-Table 16 tlv ATTRIBUTE Radius-Auth-Client-Ext-Table-Index .0 integer ATTRIBUTE Radius-Auth-Client-Ext-Entry .1 tlv -BEGIN-TLV 16.Radius-Auth-Client-Ext-Entry +BEGIN 16.Radius-Auth-Client-Ext-Entry ATTRIBUTE Radius-Auth-Client-Ext-Index 1 integer ATTRIBUTE Radius-Auth-Client-Inet-Address-Type 2 integer ATTRIBUTE Radius-Auth-Client-Inet-Address 3 ipaddr @@ -116,7 +116,7 @@ ATTRIBUTE Radius-Auth-Serv-Ext-Bad-Authenticators 11 integer ATTRIBUTE Radius-Auth-Serv-Ext-Packet-Dropped 12 integer ATTRIBUTE Radius-Auth-Serv-Ext-Unknown-Types 13 integer ATTRIBUTE Radius-Auth-Serv-Counter-Discontinuity 14 integer -END-TLV 16.Radius-Auth-Client-Ext-Entry +END 16.Radius-Auth-Client-Ext-Entry -END-TLV 67.1.1.1.Radius-Auth-Serv -END-TLV 1.3.6.1.2.FreeRADIUS-Mib-2 +END 67.1.1.1.Radius-Auth-Serv +END 1.3.6.1.2.FreeRADIUS-Mib-2 diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 4a6c6989443e9..8d66cf258071b 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -66,6 +66,7 @@ typedef struct value_box_s fr_value_box_t; # define DA_VERIFY(_x) fr_cond_assert(_x) #endif +typedef struct dict_tokenize_ctx_s dict_tokenize_ctx_t; typedef struct fr_dict_autoload_talloc_s fr_dict_autoload_talloc_t; /** Values of the encryption flags @@ -913,6 +914,8 @@ fr_dict_t const *fr_dict_internal(void); * * @{ */ +void dict_dctx_debug(dict_tokenize_ctx_t *dctx); + int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent); diff --git a/src/lib/util/dict_fixup.c b/src/lib/util/dict_fixup.c index 9c28f3d6eabc6..1625470d6991f 100644 --- a/src/lib/util/dict_fixup.c +++ b/src/lib/util/dict_fixup.c @@ -127,10 +127,19 @@ static inline CC_HINT(always_inline) int dict_fixup_common(fr_dlist_head_t *fixu } /** Resolve a ref= or copy= value to a dictionary */ -fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *root, char const *ref) + +/** Resolve a reference string to a dictionary attribute + * + * @param[in] rel Relative attribute to resolve from. + * @param[in] ref Reference string. + * @param[in] absolute_root If true, and there is no '.' prefix, searching will begin from + * the root of the dictionary, else we pretend there was a '.' and + * search from rel. + */ +fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *rel, char const *ref, bool absolute_root) { - fr_dict_t *dict = fr_dict_unconst(root->dict); - fr_dict_attr_t const *da = root, *found; + fr_dict_t *dict = fr_dict_unconst(rel->dict); + fr_dict_attr_t const *da = rel, *found; ssize_t slen; fr_sbuff_t sbuff = FR_SBUFF_IN(ref, strlen(ref)); @@ -171,11 +180,11 @@ fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *root, char c /* * Load the new dictionary, and mark it as loaded from our dictionary. */ - if (fr_dict_protocol_afrom_file(&dict, proto_name, NULL, (root->dict)->root->name) < 0) { + if (fr_dict_protocol_afrom_file(&dict, proto_name, NULL, (rel->dict)->root->name) < 0) { return NULL; } - if (!fr_hash_table_insert((root->dict)->autoref, dict)) { + if (!fr_hash_table_insert((rel->dict)->autoref, dict)) { fr_strerror_const("Failed inserting into internal autoref table"); return NULL; } @@ -192,18 +201,20 @@ fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *root, char c } /* - * If we don't have a '.' to make it relative, we're starting from the dictionary root + * First '.' makes it reletive, subsequent ones traverse up the tree. + * + * No '.' means use the root. */ if (fr_sbuff_next_if_char(&sbuff, '.')) { - do { + while (fr_sbuff_next_if_char(&sbuff, '.')) { if (!da->parent) { fr_strerror_const("Reference attempted to navigate above dictionary root"); return NULL; } da = da->parent; - } while (fr_sbuff_next_if_char(&sbuff, '.')); + }; } else { - da = dict->root; + da = absolute_root ? dict->root : rel; } /* @@ -345,7 +356,7 @@ static inline CC_HINT(always_inline) int dict_fixup_group_apply(UNUSED dict_fixu { fr_dict_attr_t const *da; - da = dict_protocol_reference(fixup->da, fixup->ref); + da = dict_protocol_reference(fixup->da->parent, fixup->ref, true); if (!da) { fr_strerror_printf_push("Failed resolving reference for attribute at %s[%u]", fr_cwd_strip(fixup->da->filename), fixup->da->line); @@ -567,7 +578,7 @@ static inline CC_HINT(always_inline) int dict_fixup_clone_apply(UNUSED dict_fixu { fr_dict_attr_t const *src; - src = dict_protocol_reference(fixup->da, fixup->ref); + src = dict_protocol_reference(fixup->da->parent, fixup->ref, true); if (!src) { fr_strerror_printf_push("Failed resolving reference for attribute at %s[%u]", fr_cwd_strip(fixup->da->filename), fixup->da->line); @@ -625,7 +636,7 @@ static inline CC_HINT(always_inline) int dict_fixup_clone_enum_apply(UNUSED dict fr_dict_attr_t const *src; int copied; - src = dict_protocol_reference(fixup->da, fixup->ref); + src = dict_protocol_reference(fixup->da->parent, fixup->ref, true); if (!src) { fr_strerror_printf_push("Failed resolving reference for attribute at %s[%u]", fr_cwd_strip(fixup->da->filename), fixup->da->line); diff --git a/src/lib/util/dict_fixup_priv.h b/src/lib/util/dict_fixup_priv.h index 7fa9ab1e0715d..94e0688b5c2e5 100644 --- a/src/lib/util/dict_fixup_priv.h +++ b/src/lib/util/dict_fixup_priv.h @@ -40,7 +40,7 @@ typedef struct { fr_dlist_head_t alias; //!< Aliases that can't be resolved immediately. } dict_fixup_ctx_t; -fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *root, char const *ref); +fr_dict_attr_t const *dict_protocol_reference(fr_dict_attr_t const *root, char const *ref, bool absolute_root); int dict_fixup_enumv_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line, char const *attr, size_t attr_len, diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index 5e118f7997dcc..3c269ffc587bf 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -64,6 +64,15 @@ typedef enum CC_HINT(flag_enum) { } dict_nest_t; DIAG_ON(attributes) +fr_table_num_sorted_t const dict_nest_table[] = { + { L("ATTRIBUTE"), NEST_ATTRIBUTE }, + { L("NONE"), NEST_NONE }, + { L("PROTOCOL"), NEST_PROTOCOL }, + { L("ROOT"), NEST_ROOT }, + { L("VENDOR"), NEST_VENDOR } +}; +size_t const dict_nest_table_len = NUM_ELEMENTS(dict_nest_table); + /** Parser context for dict_from_file * * Allows vendor and TLV context to persist across $INCLUDEs @@ -79,7 +88,7 @@ typedef struct { ssize_t struct_size; //!< size of the struct. } dict_tokenize_frame_t; -typedef struct { +struct dict_tokenize_ctx_s { fr_dict_t *dict; //!< Protocol dictionary we're inserting attributes into. dict_tokenize_frame_t stack[DICT_MAX_STACK]; //!< stack of attributes to track @@ -88,7 +97,7 @@ typedef struct { fr_dict_attr_t *value_attr; //!< Cache of last attribute to speed up value processing. fr_dict_attr_t const *relative_attr; //!< for ".82" instead of "1.2.3.82". only for parents of type "tlv" dict_fixup_ctx_t fixup; -} dict_tokenize_ctx_t; +}; static int _dict_from_file(dict_tokenize_ctx_t *dctx, char const *dir_name, char const *filename, @@ -98,6 +107,100 @@ static int _dict_from_file(dict_tokenize_ctx_t *dctx, #define CURRENT_FILENAME(_dctx) (CURRENT_FRAME(_dctx)->filename) #define CURRENT_LINE(_dctx) (CURRENT_FRAME(_dctx)->line) +void dict_dctx_debug(dict_tokenize_ctx_t *dctx) +{ + int i; + + for (i = 0; i <= dctx->stack_depth; i++) { + FR_FAULT_LOG("[%d]: %s %s (%s): %s[%u]", + i, + fr_table_str_by_value(dict_nest_table, dctx->stack[i].nest, ""), + dctx->stack[i].da->name, + fr_type_to_str(dctx->stack[i].da->type), + dctx->stack[i].filename, dctx->stack[i].line); + } +} + +static dict_tokenize_frame_t const *dict_dctx_find_frame(dict_tokenize_ctx_t *dctx, dict_nest_t nest) +{ + int i; + + for (i = dctx->stack_depth; i >= 0; i--) { + if (dctx->stack[i].nest & nest) return &dctx->stack[i]; + } + + return NULL; +} + +static int dict_dctx_push(dict_tokenize_ctx_t *dctx, fr_dict_attr_t const *da, dict_nest_t nest) +{ + if ((dctx->stack_depth + 1) >= DICT_MAX_STACK) { + fr_strerror_const_push("Attribute definitions are nested too deep."); + return -1; + } + + fr_assert(da != NULL); + + dctx->stack[++dctx->stack_depth] = (dict_tokenize_frame_t){ + .dict = dctx->stack[dctx->stack_depth - 1].dict, + .da = da, + .filename = dctx->stack[dctx->stack_depth - 1].filename, + .line = dctx->stack[dctx->stack_depth - 1].line, + .nest = nest + }; + + return 0; +} + +/** Pop the current stack frame + * + * @param[in] dctx Stack to pop from. + * @preturn + * - Pointer to the current stack frame. + * - NULL, if we're already at the root. + */ +static dict_tokenize_frame_t const *dict_dctx_pop(dict_tokenize_ctx_t *dctx) +{ + if (dctx->stack_depth == 0) return NULL; + + return &dctx->stack[dctx->stack_depth--]; +} + +/** Unwind the entire stack, returning the root frame + * + * @param[in] dctx Stack to unwind. + * @return Pointer to the root frame. + */ +static dict_tokenize_frame_t const *dict_dctx_unwind(dict_tokenize_ctx_t *dctx) +{ + while ((dctx->stack_depth > 0) && + (dctx->stack[dctx->stack_depth].nest == NEST_NONE)) { + dctx->stack_depth--; + } + + return &dctx->stack[dctx->stack_depth]; +} + +/** Unwind the stack until it points to a particular type of stack frame + * + * @param[in] dctx Stack to unwind. + * @param[in] nest Frame type to unwind to. + * @return + * - Pointer to the frame matching nest + * - NULL, if we unwound the complete stack and didn't find the frame. + */ +static dict_tokenize_frame_t const *dict_dctx_unwind_until(dict_tokenize_ctx_t *dctx, dict_nest_t nest) +{ + while ((dctx->stack_depth > 0) && + !(dctx->stack[dctx->stack_depth].nest & nest)) { + dctx->stack_depth--; + } + + if (!(dctx->stack[dctx->stack_depth].nest & nest)) return NULL; + + return &dctx->stack[dctx->stack_depth]; +} + /* * String split routine. Splits an input string IN PLACE * into pieces, based on spaces. @@ -653,47 +756,6 @@ static int CC_HINT(nonnull) dict_process_flag_field(dict_tokenize_ctx_t *dctx, c return 0; } -static dict_tokenize_frame_t const *dict_dctx_find_frame(dict_tokenize_ctx_t *dctx, dict_nest_t nest) -{ - int i; - - for (i = dctx->stack_depth; i > 0; i--) { - if (dctx->stack[i].nest == nest) return &dctx->stack[i]; - } - - return NULL; -} - -static int dict_dctx_push(dict_tokenize_ctx_t *dctx, fr_dict_attr_t const *da, dict_nest_t nest) -{ - if ((dctx->stack_depth + 1) >= DICT_MAX_STACK) { - fr_strerror_const_push("Attribute definitions are nested too deep."); - return -1; - } - - fr_assert(da != NULL); - - dctx->stack[++dctx->stack_depth] = (dict_tokenize_frame_t){ - .dict = dctx->stack[dctx->stack_depth - 1].dict, - .da = da, - .filename = dctx->stack[dctx->stack_depth - 1].filename, - .line = dctx->stack[dctx->stack_depth - 1].line, - .nest = nest - }; - - return 0; -} - -static fr_dict_attr_t const *dict_gctx_unwind(dict_tokenize_ctx_t *ctx) -{ - while ((ctx->stack_depth > 0) && - (ctx->stack[ctx->stack_depth].nest == NEST_NONE)) { - ctx->stack_depth--; - } - - return ctx->stack[ctx->stack_depth].da; -} - static int dict_finalise(dict_tokenize_ctx_t *dctx) { if (dict_fixup_apply(&dctx->fixup) < 0) return -1; @@ -758,7 +820,7 @@ static int dict_attr_add_or_fixup(dict_fixup_ctx_t *fixup, fr_dict_attr_t **da_p /* * See if we can immediately apply the clone */ - fr_dict_attr_t const *src = dict_protocol_reference(da, ref->unresolved); + fr_dict_attr_t const *src = dict_protocol_reference(da->parent, ref->unresolved, true); if (src) { if (dict_fixup_clone(da_p, src) < 0) return -1; break; @@ -1127,7 +1189,7 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *dctx, char **argv, i * unwind the stack to match. */ if (argv[1][0] != '.') { - parent = dict_gctx_unwind(dctx); + parent = dict_dctx_unwind(dctx)->da; /* * Allow '0xff00' as attribute numbers, but only @@ -1268,6 +1330,59 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *dctx, char **argv, i return 0; } +static int dict_read_process_begin(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags) +{ + dict_tokenize_frame_t const *frame; + fr_dict_attr_t const *da; + fr_dict_attr_t const *common; + + dctx->value_attr = NULL; + dctx->relative_attr = NULL; + + if (argc != 1) { + fr_strerror_const_push("Invalid BEGIN entry"); + error: + return -1; + } + + frame = dict_dctx_find_frame(dctx, NEST_ROOT | NEST_PROTOCOL | NEST_ATTRIBUTE); + if (!fr_cond_assert_msg(frame, "Context stack doesn't have an attribute or dictionary " + "root to begin searching from %s[%u]", CURRENT_FILENAME(dctx), CURRENT_LINE(dctx)) || + !fr_cond_assert_msg(fr_type_is_structural(frame->da->type), "Context attribute is not structural %s[%u]", + CURRENT_FILENAME(dctx), CURRENT_LINE(dctx))) { + return -1; + } + + /* + * Not really a reference as we don't support any of the + * fancy syntaxes like refs do. A straight OID string + * resolved from the current level of nesting is all we support. + */ + da = fr_dict_attr_by_oid(NULL, frame->da, argv[0]); + if (!da) { + fr_strerror_printf_push("BEGIN '%s' not resolvable in context '%s'", argv[0], frame->da->name); + goto error; + } + + if (!fr_type_is_tlv(da->type) && !fr_type_is_struct(da->type)) { + fr_strerror_printf_push("BEGIN %s should be a 'tlv' or 'struct', but is a '%s'", + argv[0], + fr_type_to_str(da->type)); + goto error; + } + + common = fr_dict_attr_common_parent(frame->da, da, true); + if (!common) { + fr_strerror_printf_push("BEGIN %s should be a child of '%s'", + argv[0], dctx->stack[dctx->stack_depth].da->name); + goto error; + } + + if (dict_dctx_push(dctx, da, NEST_ATTRIBUTE) < 0) goto error; + + return 0; +} + static int dict_read_process_begin_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags) { @@ -1326,47 +1441,6 @@ static int dict_read_process_begin_protocol(dict_tokenize_ctx_t *dctx, char **ar return 0; } -static int dict_read_process_begin_tlv(dict_tokenize_ctx_t *dctx, char **argv, int argc, - UNUSED fr_dict_attr_flags_t *base_flags) -{ - fr_dict_attr_t const *da; - fr_dict_attr_t const *common; - - dctx->value_attr = NULL; - dctx->relative_attr = NULL; - - if (argc != 1) { - fr_strerror_const_push("Invalid BEGIN-TLV entry"); - error: - return -1; - } - - da = fr_dict_attr_by_oid(NULL, dctx->stack[dctx->stack_depth].da, argv[0]); - if (!da) { - fr_strerror_const_push("Failed resolving attribute in BEGIN-TLV entry"); - goto error; - } - - if (da->type != FR_TYPE_TLV) { - fr_strerror_printf_push("Attribute '%s' should be a 'tlv', but is a '%s'", - argv[0], - fr_type_to_str(da->type)); - goto error; - } - - common = fr_dict_attr_common_parent(dctx->stack[dctx->stack_depth].da, da, true); - if (!common || - (common->type == FR_TYPE_VSA)) { - fr_strerror_printf_push("Attribute '%s' should be a child of '%s'", - argv[0], dctx->stack[dctx->stack_depth].da->name); - goto error; - } - - if (dict_dctx_push(dctx, da, NEST_TLV) < 0) goto error; - - return 0; -} - static int dict_read_process_begin_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags) { @@ -1512,8 +1586,9 @@ static int dict_read_process_begin_vendor(dict_tokenize_ctx_t *dctx, char **argv static int dict_read_process_define(dict_tokenize_ctx_t *dctx, char **argv, int argc, fr_dict_attr_flags_t *base_flags) { - fr_dict_attr_t const *parent; - fr_dict_attr_t *da; + fr_dict_attr_t const *parent; + fr_dict_attr_t *da; + dict_tokenize_frame_t const *frame; if ((argc < 2) || (argc > 3)) { fr_strerror_const("Invalid DEFINE syntax"); @@ -1545,9 +1620,10 @@ static int dict_read_process_define(dict_tokenize_ctx_t *dctx, char **argv, int goto error; } - parent = dict_gctx_unwind(dctx); + frame = dict_dctx_unwind(dctx); + if (!fr_cond_assert(frame && frame->da)) goto error; /* Should have provided us with a parent */ - if (!fr_cond_assert(parent)) goto error; /* Should have provided us with a parent */ + parent = frame->da; /* * Members of a 'struct' MUST use MEMBER, not ATTRIBUTE. @@ -1622,6 +1698,62 @@ static int dict_read_process_define(dict_tokenize_ctx_t *dctx, char **argv, int return 0; } +static int dict_read_process_end(dict_tokenize_ctx_t *dctx, char **argv, int argc, + UNUSED fr_dict_attr_flags_t *base_flags) +{ + fr_dict_attr_t const *current; + fr_dict_attr_t const *da; + dict_tokenize_frame_t const *frame; + + dctx->value_attr = NULL; + dctx->relative_attr = NULL; + + if (argc > 2) { + fr_strerror_const("Invalid END syntax, expected END "); + goto error; + } + + /* + * Unwind until we hit an attribute nesting section + */ + if (!dict_dctx_unwind_until(dctx, NEST_ATTRIBUTE)) { + fr_strerror_const("Unbalanced BEGIN and END keywords"); + error: + return -1; + } + + /* + * Pop the stack to get the attribute we're ending. + */ + current = dict_dctx_pop(dctx)->da;; + + /* + * No checks on the attribute, we're just popping _A_ frame, + * we don't care what attribute it represents. + */ + if (argc == 1) return 0; + + /* + * This is where we'll have begun the previous search to + * evaluate the BEGIN keyword. + */ + frame = dict_dctx_find_frame(dctx, NEST_ROOT | NEST_PROTOCOL | NEST_ATTRIBUTE); + if (!fr_cond_assert(frame)) goto error; + + da = fr_dict_attr_by_oid(NULL, frame->da, argv[0]); + if (!da) { + fr_strerror_const_push("Failed resolving attribute in BEGIN entry"); + goto error; + } + + if (da != current) { + fr_strerror_printf_push("END %s does not match previous BEGIN %s", argv[0], current->name); + goto error; + } + + return 0; +} + static int dict_read_process_end_protocol(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags) { @@ -1688,55 +1820,6 @@ static int dict_read_process_end_protocol(dict_tokenize_ctx_t *dctx, char **argv return 0; } -static int dict_read_process_end_tlv(dict_tokenize_ctx_t *dctx, char **argv, int argc, - UNUSED fr_dict_attr_flags_t *base_flags) -{ - fr_dict_attr_t const *da; - - dctx->value_attr = NULL; - dctx->relative_attr = NULL; - - if (argc != 1) { - fr_strerror_const_push("Invalid END-TLV entry"); - error: - return -1; - } - - da = fr_dict_attr_by_oid(NULL, dctx->stack[dctx->stack_depth - 1].da, argv[0]); - if (!da) { - fr_strerror_const_push("Failed resolving attribute in END-TLV entry"); - goto error; - } - - /* - * Pop the stack until we get to a TLV nesting. - */ - while ((dctx->stack_depth > 0) && (dctx->stack[dctx->stack_depth].nest != NEST_TLV)) { - if (dctx->stack[dctx->stack_depth].nest != NEST_NONE) { - fr_strerror_printf_push("END-TLV %s with mismatched BEGIN-??? %s", argv[0], - dctx->stack[dctx->stack_depth].da->name); - goto error; - } - - dctx->stack_depth--; - } - - if (dctx->stack_depth == 0) { - fr_strerror_printf_push("END-TLV %s with no previous BEGIN-TLV", argv[0]); - goto error; - } - - if (da != dctx->stack[dctx->stack_depth].da) { - fr_strerror_printf_push("END-TLV %s does not match previous BEGIN-TLV %s", argv[0], - dctx->stack[dctx->stack_depth].da->name); - goto error; - } - - dctx->stack_depth--; - - return 0; -} - static int dict_read_process_end_vendor(dict_tokenize_ctx_t *dctx, char **argv, int argc, UNUSED fr_dict_attr_flags_t *base_flags) { @@ -2703,12 +2786,12 @@ static int _dict_from_file(dict_tokenize_ctx_t *dctx, static fr_dict_keyword_t const keywords[] = { { L("ALIAS"), { dict_read_process_alias } }, { L("ATTRIBUTE"), { dict_read_process_attribute } }, + { L("BEGIN"), { dict_read_process_begin } }, { L("BEGIN-PROTOCOL"), { dict_read_process_begin_protocol } }, - { L("BEGIN-TLV"), { dict_read_process_begin_tlv } }, { L("BEGIN-VENDOR"), { dict_read_process_begin_vendor } }, { L("DEFINE"), { dict_read_process_define } }, + { L("END"), { dict_read_process_end } }, { L("END-PROTOCOL"), { dict_read_process_end_protocol } }, - { L("END-TLV"), { dict_read_process_end_tlv } }, { L("END-VENDOR"), { dict_read_process_end_vendor } }, { L("ENUM"), { dict_read_process_enum } }, { L("FLAGS"), { dict_read_process_flags } },