]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
parse partially known and partially unknown attributes
authorAlan T. DeKok <aland@freeradius.org>
Thu, 18 Dec 2025 07:44:27 +0000 (08:44 +0100)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 18 Dec 2025 08:11:01 +0000 (09:11 +0100)
and commit more pair tests

src/lib/util/value.c
src/tests/unit/dictionary.test [new file with mode: 0644]
src/tests/unit/pair.txt [new file with mode: 0644]

index 027e545beeed696608524371d3d035a178c8b981..c544c0da308e19b542ca553c707d264b5f4a2679 100644 (file)
@@ -5688,12 +5688,22 @@ parse:
                } else if (fr_sbuff_adv_past_str_literal(&our_in, "::")) {
 
                        slen = fr_dict_attr_by_oid_substr(NULL, &dst->vb_attr, dst_enumv, &our_in, rules->terminals);
-                       if (slen > 0) {
-                               fr_assert(dst->vb_attr != NULL);
+                       if (slen <= 0) goto unknown_attr;
+
+                       fr_assert(dst->vb_attr != NULL);
+
+                       if (!fr_sbuff_next_if_char(&our_in, '.')) {
                                FR_SBUFF_SET_RETURN(in, &our_in);
                        }
+
+                       /*
+                        *      Parse the unknown attribute starting from where we left off.
+                        */
+                       dst_enumv = dst->vb_attr;
+                       dst->vb_attr = NULL;
                }
 
+unknown_attr:
                slen = fr_dict_attr_unknown_afrom_oid_substr(ctx, &dst->vb_attr, dst_enumv, &our_in, FR_TYPE_OCTETS);
                if (slen <= 0) {
                        fr_strerror_printf("Failed to find the named attribute in %s", dst_enumv->name);
diff --git a/src/tests/unit/dictionary.test b/src/tests/unit/dictionary.test
new file mode 100644 (file)
index 0000000..d4c893f
--- /dev/null
@@ -0,0 +1 @@
+DEFINE Test-Attr       attribute       internal
diff --git a/src/tests/unit/pair.txt b/src/tests/unit/pair.txt
new file mode 100644 (file)
index 0000000..e12d1b2
--- /dev/null
@@ -0,0 +1,232 @@
+#  -*- text -*-
+#  Copyright (C) 2025 Network RADIUS SARL (legal@networkradius.com)
+#  This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+#
+#  Version $Id$
+#
+
+#
+#  @todo - add error offset to failure cases.
+#
+
+#
+#  Test pair parsing for  fr_pair_list_afrom_substr()
+#
+proto radius
+proto-dictionary radius
+
+load-dictionary dictionary.test
+
+pair User-Name = "bob"
+match User-Name = "bob"
+
+#
+#  @todo - This is arguably wrong.  The rest of the server requires quoted strings, and disallows bare words.
+#
+pair User-Name = 0xabcdef
+match User-Name = "0xabcdef"
+
+#
+#  @todo - This is arguably wrong.  The rest of the server requires "::" prefixes for enumeration values.
+#
+pair Service-Type = Framed-User
+match Service-Type = ::Framed-User
+
+#
+#  Multiple things
+#
+pair User-Name = "bob", User-Password = "hello"
+match User-Name = "bob", User-Password = "hello"
+
+######################################################################
+#
+#  Raw attributes
+#
+######################################################################
+
+# 
+#  A raw attribute.
+#
+#  This is perhaps wrong?  We should probably be printing this as
+#  "raw.1 = 0xabcdef", because it's not the normal attribute.
+#
+pair raw.User-Name = 0xabcdef
+match raw.User-Name = 0xabcdef
+
+#
+#  Change data types.
+#
+#  @todo - This is arguably wrong.  We should be able to change data types, so
+#  long as the attribute is "raw.1", and not "raw.User-Name".
+#
+pair raw.User-Name = (ipaddr) 192.168.0.1
+match Cannot create raw attribute User-Name which changes data type from string to ipaddr
+
+#
+#  This is OK, due to the underlying IP address parser.
+#
+pair Framed-IP-Address = 1
+match Framed-IP-Address = 0.0.0.1
+
+######################################################################
+#
+#  Nested attributes
+#
+######################################################################
+
+#
+#  Empty list
+#
+pair Extended-Attribute-1 = { }
+match Extended-Attribute-1 = { }
+
+#
+#  Internal group can contain a protocol attribute.
+#
+pair Tmp-Group-0 = { User-Name = "bob" }
+match Tmp-Group-0 = { User-Name = "bob" }
+
+#
+#  Internal TLV cannot contain a protocol attribute.
+#
+pair Tmp-TLV-0 = { User-Name = "bob" }
+match Internal attribute 'Tmp-TLV-0' of data type 'tlv' cannot contain protocol attributes
+
+#
+#  Protocol TLV can contain an internal attribute
+#
+#  This is definitely wrong.  The switch to the internal dictionary
+#  confuses the pair printing routines.
+#
+pair Extended-Attribute-1 = { Tmp-Integer-0 = 1 }
+match Extended-Attribute-1 = { Tmp-Integer-0 = 1 }
+
+#
+#  One level of TLVs
+#
+pair Digest-Attributes = { Realm = "foo" }
+match Digest-Attributes = { Realm = "foo" }
+
+#
+#  Two levels of TLVs
+#
+pair Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } }
+match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } }
+
+#
+#  Aliases
+#
+pair IP-Port-Limit-Info.Type = 1
+match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } }
+
+pair IP-Port-Limit-Info = { Type = 1 }
+match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } }
+
+######################################################################
+#
+#  Flat lists with operators
+#
+######################################################################
+
+#
+#  Flat input results in nested output.
+#
+pair Extended-Attribute-1.IP-Port-Limit-Info.Type = 1
+match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } }
+
+#
+#  Flat inputs nest at the lowest level
+#
+pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.Cisco.AVPair = "bar"
+match Vendor-Specific = { Cisco = { AVPair = "foo", AVPair = "bar" } }
+
+#
+#  "+=" gets mashed to "="
+#
+pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.Cisco.AVPair += "bar"
+match Vendor-Specific = { Cisco = { AVPair = "foo", AVPair = "bar" } }
+
+#
+#  Comparison operators, equality.  Only leaf attributes can be compared.
+#
+pair-compare Vendor-Specific.Cisco.AVPair == "foo", Vendor-Specific.Cisco.AVPair == "bar"
+match Vendor-Specific = { Cisco = { AVPair == "foo", AVPair == "bar" } }
+
+#
+#  Except that lists can only be compared for equality
+#
+
+#
+#  Comparison operators, leaf comparisons
+#
+pair-compare Vendor-Specific.Cisco.AVPair < "foo", Vendor-Specific.Cisco.AVPair > "bar"
+match Vendor-Specific = { Cisco = { AVPair < "foo", AVPair > "bar" } }
+
+#
+#  Comparison operators apply only to leaf.
+#
+pair-compare Vendor-Specific < { Cisco == { AVPair == "foo" } }
+match Structural attribute 'Vendor-Specific' must use '=' or '==' for comparisons
+
+#
+#  Invalid operator - comparison operators are not allowed when doing assignments.
+#
+pair Vendor-Specific.Cisco.AVPair == "foo", Vendor-Specific.Cisco.AVPair == "bar"
+match Invalid character '=' after operator '='
+
+#
+#  Flat inputs nest at a higher level, too
+#
+pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.HP.Privilege-Level = 1
+match Vendor-Specific = { Cisco = { AVPair = "foo" }, HP = { Privilege-Level = 1 } }
+
+######################################################################
+#
+#  Error cases
+#
+######################################################################
+
+pair .User-Name = "bob"
+match The '.Attribute' syntax cannot be used at the root of a dictionary
+
+pair Tmp-Group-0 = { .User-Name = "bob" }
+match The '.Attribute' syntax cannot be used with parent Tmp-Group-0 of data type 'group'
+
+pair Vendor-Specific = { Cisco = { .AVPair += "foo" } }
+match The '.Attribute' syntax cannot be used along with the '+=' operator
+
+#
+#  Invalid data types
+#
+pair NAS-Port = 192.168.0.1
+match Unexpected text '.168.0.1 ...' after value
+
+pair NAS-Port = 192.168.0.1, NAS-Port = 2
+match Unexpected text '.168.0.1, NAS-Port = ...' after value
+
+pair NAS-Port = abcdef
+match Failed parsing string as type 'uint32'
+
+pair Extended-Attribute-1 = 0xabcdef
+match Group list for Extended-Attribute-1 MUST start with '{'
+
+pair Extended-Attribute-1.Framed-IP-Address.Tmp-String-0 := "hello"
+match Unknown attribute "Framed-IP-Address.Tmp-String-0" for parent "Extended-Attribute-1"
+
+#
+#  Attributes as values
+#
+pair Test-Attr = ::Vendor-Specific.Cisco.AVPair
+match Test-Attr = ::Vendor-Specific.Cisco.AVPair
+
+pair Test-Attr = ::26.9.1
+match Test-Attr = ::Vendor-Specific.Cisco.AVPair
+
+#
+#  Partially known and partially unknown
+#
+pair Test-Attr = ::Vendor-Specific.12345.1.1
+match Test-Attr = ::Vendor-Specific.12345.1.1
+
+count
+match 71