]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Start of nested attribute support
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 26 Aug 2020 17:49:07 +0000 (12:49 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 21 Sep 2020 16:17:20 +0000 (11:17 -0500)
This also moves a significant number of functions to using sbuffs for printing and parsing.

This commit also significantly alters xlat and condition tokenizing

...and drops legacy tag support

290 files changed:
doc/antora/modules/howto/pages/modules/ldap/authorization/groups.adoc
doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc
doc/antora/modules/howto/pages/modules/sqlippool/index.adoc
doc/antora/modules/raddb/pages/mods-available/cache_eap.adoc
doc/antora/modules/raddb/pages/mods-available/delay.adoc
doc/antora/modules/raddb/pages/mods-available/dhcp_sqlippool.adoc
doc/antora/modules/raddb/pages/mods-available/json.adoc
doc/antora/modules/raddb/pages/mods-available/ldap.adoc
doc/antora/modules/raddb/pages/mods-available/linelog.adoc
doc/antora/modules/raddb/pages/mods-available/sqlippool.adoc
doc/antora/modules/raddb/pages/sites-available/check-eap-tls.adoc
doc/antora/modules/raddb/pages/sites-available/coa-relay.adoc
doc/antora/modules/raddb/pages/sites-available/default.adoc
doc/antora/modules/unlang/pages/call.adoc
doc/antora/modules/unlang/pages/type/string/double.adoc
doc/antora/modules/unlang/pages/xlat/attribute.adoc
doc/antora/modules/unlang/pages/xlat/builtin.adoc
man/man5/unlang.5
raddb/mods-available/cache_eap
raddb/mods-available/delay
raddb/mods-available/dhcp_sqlippool
raddb/mods-available/json
raddb/mods-available/ldap
raddb/mods-available/linelog
raddb/mods-available/sqlippool
raddb/mods-config/sql/cui/mysql/queries.conf
raddb/mods-config/sql/cui/postgresql/queries.conf
raddb/mods-config/sql/cui/sqlite/queries.conf
raddb/mods-config/sql/ippool-dhcp/mssql/procedure.sql
raddb/mods-config/sql/ippool-dhcp/mssql/queries.conf
raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql
raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf
raddb/mods-config/sql/ippool-dhcp/oracle/procedure.sql
raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf
raddb/mods-config/sql/ippool-dhcp/postgresql/procedure.sql
raddb/mods-config/sql/ippool-dhcp/postgresql/queries.conf
raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf
raddb/mods-config/sql/ippool/mssql/procedure.sql
raddb/mods-config/sql/ippool/mssql/queries.conf
raddb/mods-config/sql/ippool/mysql/procedure-no-skip-locked.sql
raddb/mods-config/sql/ippool/mysql/procedure.sql
raddb/mods-config/sql/ippool/mysql/queries.conf
raddb/mods-config/sql/ippool/oracle/procedure.sql
raddb/mods-config/sql/ippool/oracle/queries.conf
raddb/mods-config/sql/ippool/postgresql/procedure.sql
raddb/mods-config/sql/ippool/postgresql/queries.conf
raddb/mods-config/sql/ippool/sqlite/queries.conf
raddb/mods-config/sql/main/cassandra/queries.conf
raddb/mods-config/sql/main/mssql/queries.conf
raddb/mods-config/sql/main/mysql/extras/wimax/queries.conf
raddb/mods-config/sql/main/mysql/queries.conf
raddb/mods-config/sql/main/oracle/queries.conf
raddb/mods-config/sql/main/postgresql/queries.conf
raddb/mods-config/sql/main/sqlite/queries.conf
raddb/policy.d/cui
raddb/policy.d/tacacs
raddb/sites-available/check-eap-tls
raddb/sites-available/coa-relay
raddb/sites-available/default
raddb/sites-available/dhcp
src/bin/radclient.c
src/bin/radsniff.c
src/bin/radsnmp.c
src/bin/unit_test_attribute.c
src/bin/unit_test_map.c
src/bin/unit_test_module.c
src/lib/eap/base.c
src/lib/eap/chbind.c
src/lib/eap/compose.c
src/lib/eap/session.c
src/lib/eap_aka_sim/encode.c
src/lib/eap_aka_sim/state_machine.c
src/lib/eap_aka_sim/vector.c
src/lib/eap_aka_sim/xlat.c
src/lib/io/app_io.c
src/lib/io/master.c
src/lib/json/json.c
src/lib/ldap/control.c
src/lib/redis/redis.c
src/lib/server/auth.c
src/lib/server/cf_file.c
src/lib/server/cf_parse.c
src/lib/server/cf_util.c
src/lib/server/client.c
src/lib/server/cond.h
src/lib/server/cond_eval.c
src/lib/server/cond_eval.h
src/lib/server/cond_tokenize.c
src/lib/server/exec.c
src/lib/server/log.c
src/lib/server/map.c
src/lib/server/map.h
src/lib/server/map_async.c
src/lib/server/paircmp.c
src/lib/server/rcode.c
src/lib/server/state.c
src/lib/server/tmpl.c
src/lib/server/tmpl.h
src/lib/server/trigger.c
src/lib/server/virtual_servers.c
src/lib/server/virtual_servers.h
src/lib/sim/id.h
src/lib/soh/soh.c
src/lib/tls/cache.c
src/lib/tls/ocsp.c
src/lib/tls/session.c
src/lib/unlang/base.c
src/lib/unlang/compile.c
src/lib/unlang/foreach.c
src/lib/unlang/map.c
src/lib/unlang/subrequest.c
src/lib/unlang/switch.c
src/lib/unlang/tmpl.c
src/lib/unlang/unlang_priv.h
src/lib/unlang/xlat.c
src/lib/unlang/xlat.h
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_inst.c
src/lib/unlang/xlat_priv.h
src/lib/unlang/xlat_tokenize.c
src/lib/util/dict.h
src/lib/util/dict_unknown.c
src/lib/util/dict_util.c
src/lib/util/libfreeradius-util.mk
src/lib/util/pair.c
src/lib/util/pair.h
src/lib/util/pair_cursor.c
src/lib/util/pair_cursor.h
src/lib/util/pair_legacy.c
src/lib/util/pair_print.c
src/lib/util/print.c
src/lib/util/regex.c
src/lib/util/regex.h
src/lib/util/sbuff.c
src/lib/util/sbuff.h
src/lib/util/token.c
src/lib/util/value.c
src/lib/util/value.h
src/modules/proto_arp/proto_arp_process.c
src/modules/proto_detail/proto_detail_process.c
src/modules/proto_dhcpv4/dhcpclient.c
src/modules/proto_dhcpv4/proto_dhcpv4_process.c
src/modules/proto_dhcpv6/proto_dhcpv6_process.c
src/modules/proto_ldap_sync/proto_ldap_sync.c
src/modules/proto_radius/proto_radius_acct.c
src/modules/proto_radius/proto_radius_auth.c
src/modules/proto_radius/proto_radius_coa.c
src/modules/proto_radius/proto_radius_dynamic_client.c
src/modules/proto_radius/proto_radius_load.c
src/modules/proto_radius/proto_radius_status.c
src/modules/proto_tacacs/proto_tacacs.c
src/modules/proto_tacacs/proto_tacacs_acct.c
src/modules/proto_tacacs/proto_tacacs_auth.c
src/modules/proto_tacacs/proto_tacacs_autz.c
src/modules/proto_vmps/proto_vmps_dynamic_client.c
src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_cache/serialize.c
src/modules/rlm_chap/rlm_chap.c
src/modules/rlm_couchbase/mod.c
src/modules/rlm_couchbase/rlm_couchbase.c
src/modules/rlm_csv/rlm_csv.c
src/modules/rlm_digest/rlm_digest.c
src/modules/rlm_eap/rlm_eap.c
src/modules/rlm_eap/types/rlm_eap_fast/eap_fast.c
src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c
src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c
src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c
src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c
src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c
src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c
src/modules/rlm_exec/rlm_exec.c
src/modules/rlm_expiration/rlm_expiration.c
src/modules/rlm_expr/rlm_expr.c
src/modules/rlm_files/rlm_files.c
src/modules/rlm_imap/rlm_imap.c
src/modules/rlm_isc_dhcp/rlm_isc_dhcp.c
src/modules/rlm_json/rlm_json.c
src/modules/rlm_krb5/rlm_krb5.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_ldap/user.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_logintime/rlm_logintime.c
src/modules/rlm_lua/lua.c
src/modules/rlm_mruby/rlm_mruby.c
src/modules/rlm_mschap/auth_wbclient.c
src/modules/rlm_mschap/opendir.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_opendirectory/rlm_opendirectory.c
src/modules/rlm_pam/rlm_pam.c
src/modules/rlm_pap/rlm_pap.c
src/modules/rlm_passwd/rlm_passwd.c
src/modules/rlm_perl/rlm_perl.c
src/modules/rlm_python/rlm_python.c
src/modules/rlm_radius/rlm_radius.c
src/modules/rlm_radius/rlm_radius_udp.c
src/modules/rlm_radutmp/rlm_radutmp.c
src/modules/rlm_redis_ippool/redis_ippool.h
src/modules/rlm_redis_ippool/rlm_redis_ippool.c
src/modules/rlm_rediswho/rlm_rediswho.c
src/modules/rlm_rest/rest.c
src/modules/rlm_rest/rlm_rest.c
src/modules/rlm_securid/mem.c
src/modules/rlm_securid/rlm_securid.c
src/modules/rlm_soh/rlm_soh.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sqlcounter/rlm_sqlcounter.c
src/modules/rlm_sqlippool/rlm_sqlippool.c
src/modules/rlm_stats/rlm_stats.c
src/modules/rlm_unix/rlm_unix.c
src/modules/rlm_unpack/rlm_unpack.c
src/modules/rlm_wimax/rlm_wimax.c
src/modules/rlm_winbind/rlm_winbind.c
src/modules/rlm_yubikey/decrypt.c
src/modules/rlm_yubikey/rlm_yubikey.c
src/protocols/dhcpv4/base.c
src/protocols/dhcpv4/packet.c
src/protocols/dhcpv4/raw.c
src/protocols/dhcpv6/base.c
src/protocols/radius/decode.c
src/protocols/radius/encode.c
src/protocols/tacacs/base.c
src/protocols/tacacs/encode.c
src/protocols/vmps/vmps.c
src/tests/auth/chap
src/tests/auth/chap_header
src/tests/keywords/concat
src/tests/keywords/escape
src/tests/keywords/if-multivalue
src/tests/keywords/ipaddr.raw
src/tests/keywords/ipprefix
src/tests/keywords/md4
src/tests/keywords/md5
src/tests/keywords/pairs
src/tests/keywords/pap
src/tests/keywords/pap-ssha2
src/tests/keywords/parallel
src/tests/keywords/sha1
src/tests/keywords/sha2
src/tests/keywords/switch-escape
src/tests/keywords/switch-value-error2 [deleted file]
src/tests/keywords/unknown
src/tests/keywords/update-add-ref-index [deleted file]
src/tests/keywords/update-add-ref-tag [deleted file]
src/tests/keywords/update-index
src/tests/keywords/update-remove-any
src/tests/keywords/update-remove-tag [deleted file]
src/tests/keywords/update-remove-value
src/tests/keywords/update-tag [deleted file]
src/tests/keywords/update-unknown
src/tests/keywords/update-xlat
src/tests/keywords/urlquote
src/tests/keywords/vendor_specific.raw
src/tests/keywords/virtual
src/tests/keywords/wimax
src/tests/keywords/xlat-attr-tag [deleted file]
src/tests/keywords/xlat-error
src/tests/keywords/xlat-exec
src/tests/keywords/xlat-explode
src/tests/keywords/xlat-inline
src/tests/keywords/xlat-list
src/tests/keywords/xlat-virtual-attr
src/tests/modules/cache_rbtree/cache-update.unlang
src/tests/modules/cache_rbtree/module.conf
src/tests/modules/chap/chap_password.unlang
src/tests/modules/ldap/module.conf
src/tests/modules/linelog/module.conf
src/tests/modules/redis/cluster_key.unlang
src/tests/modules/redis/cluster_reset.inc
src/tests/modules/redis_ippool/alloc.unlang
src/tests/modules/redis_ippool/pool_tool_delete.unlang
src/tests/modules/redis_ippool/pool_tool_modify.unlang
src/tests/modules/redis_ippool/pool_tool_release.unlang
src/tests/modules/redis_ippool/release.unlang
src/tests/modules/redis_ippool/update.unlang
src/tests/modules/redis_ippool/update_alloc.unlang
src/tests/modules/sql/map.unlang
src/tests/radclient/config/radiusd.conf
src/tests/unit/condition/base.txt
src/tests/unit/condition/escape.txt
src/tests/unit/condition/regex.txt
src/tests/unit/protocols/radius/packet_radius.txt
src/tests/unit/protocols/radius/packet_rfc3162.txt
src/tests/unit/protocols/radius/packet_wireshark01.txt
src/tests/unit/protocols/radius/rfc2868.txt
src/tests/unit/protocols/radius/wimax.txt
src/tests/unit/unknown.txt.broken
src/tests/unit/xlat/base.txt

index 76259d639cf798469364357d8ce144eb333f3ec2..ef6cbf0a0a14fa76ee7e9f0e974ee97b88d59ba5 100644 (file)
@@ -82,8 +82,8 @@ ldap {
                # example - 'cn'
                name_attribute = '<group_name_attribute>'  <3>
 
-               # example - "(member=%{control:Ldap-UserDn})"
-               membership_filter = "(<group_membership_dn_attribute>=%{control:Ldap-UserDn})"  <4>
+               # example - "(member=%{control.Ldap-UserDn})"
+               membership_filter = "(<group_membership_dn_attribute>=%{control.Ldap-UserDn})"  <4>
        }
 }
 ----
@@ -107,8 +107,8 @@ ldap {
                # example - 'cn'
                name_attribute = '<group_name_attribute>'  <3>
 
-               # example - "(memberUid=%{control:Ldap-UserDn})"
-               membership_filter = "(<group_membership_uid_attribute>=%{control:Ldap-UserDn})"  <4>
+               # example - "(memberUid=%{control.Ldap-UserDn})"
+               membership_filter = "(<group_membership_uid_attribute>=%{control.Ldap-UserDn})"  <4>
        }
 }
 ----
index 67a1caebb5de9398028f0e5b30888100c4ab582a..9ebf79a4a3c56d333a695ea9745bceeceb3da6ae 100644 (file)
@@ -69,7 +69,7 @@ Group objects reference users using DNs.
 [width="100%",cols="30%,70%",options="header",]
 |===
 | Purpose                                     | `ldap { group { ... } }` config item
-| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(<group_membership_dn_attribute>=%{control:Ldap-UserDn})"```
+| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(<group_membership_dn_attribute>=%{control.Ldap-UserDn})"```
 |===
 
 == Groups - variant 4
@@ -95,6 +95,6 @@ both on user DN and user name e.g.
 
 [source,config]
 ----
-membership_filter = "(|(<group_membership_filter_by_uid>=%{control:Ldap-UserDn})(<group_membership_filter_by_name>=%{%{Stripped-User-Name}:-%{User-Name}}))"
+membership_filter = "(|(<group_membership_filter_by_uid>=%{control.Ldap-UserDn})(<group_membership_filter_by_name>=%{%{Stripped-User-Name}:-%{User-Name}}))"
 ----
 ****
index 91cc83457c95976b520b7a1844fd2cdaa4e16f7b..d32d6b395d0fb866d41360f9f5af6b1830c71665 100644 (file)
@@ -788,7 +788,7 @@ allocate_begin = ""
 
 alloc_find = "\
       CALL fr_allocate_previous_or_new_framedipaddress( \
-              '%{control:${pool_name}}', \
+              '%{control.${pool_name}}', \
               '%{User-Name}', \
               '%{Calling-Station-Id}', \
               '%{NAS-IP-Address}', \
@@ -860,7 +860,7 @@ any RADIUS attribute that is available during authentication can be used.
 alloc_begin = ""
 alloc_find = "\
         CALL fr_allocate_previous_or_new_framedipaddress( \
-                '%{control:${pool_name}}', \
+                '%{control.${pool_name}}', \
                 '%{User-Name}', \
                 '%{Calling-Station-Id}', \
                 '%{NAS-IP-Address}', \
index 4257dd74ce7a1e42fb9f5edfdb4b1d28861f71d8..f16863932be2afceae87d2d0f69dcf5a2547b8aa 100644 (file)
@@ -68,7 +68,7 @@ update <section> { ... }::
 
 ```
 cache cache_eap {
-       key = "%{%{control:State}:-%{%{reply:State}:-%{State}}}"
+       key = "%{%{control.State}:-%{%{reply.State}:-%{State}}}"
        ttl = 15
        update reply {
                &reply: += &reply:
index a7789b00fa4dcc088606f5aa94e0c28b4ec48d5b..3f0983b040e1679bdca11e615f0ea2e80b4c6458 100644 (file)
@@ -78,7 +78,7 @@ delay {
 #      relative = no
 }
 delay delay_reject {
-       delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}"
+       delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}"
        relative = yes
 }
 ```
index d2aea4a18cbc213d1147a0bd229b416d63b93497..aac4095cc6f4f3510e26a743dd0da51f0530098d 100644 (file)
@@ -51,10 +51,10 @@ sqlippool dhcp_sqlippool {
        attribute_name = DHCP-Your-IP-Address
        $INCLUDE ${modconfdir}/sql/ippool-dhcp/mysql/queries.conf
 #      $INCLUDE ${modconfdir}/sql/ippool-dhcp/sqlite/queries.conf
-       sqlippool_log_exists = "DHCP: Existing IP: %{reply:${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
-       sqlippool_log_success = "DHCP: Allocated IP: %{reply:${..attribute_name}} from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_exists = "DHCP: Existing IP: %{reply.${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_success = "DHCP: Allocated IP: %{reply.${..attribute_name}} from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
        sqlippool_log_clear = "DHCP: Released IP %{${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} user %{User-Name})"
-       sqlippool_log_failed = "DHCP: IP Allocation FAILED from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_failed = "DHCP: IP Allocation FAILED from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
        sqlippool_log_nopool = "DHCP: No Pool-Name defined (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 }
 ```
index 20077c67b11862fea5aa96bdf349b59ceed99fbe..093dea1936e3e1de570cd52b2791789d41cba4a5 100644 (file)
@@ -161,7 +161,7 @@ update control {
   &Tmp-String-0 := '$.my.json.payload[1]'
 }
 update reply {
-  &Reply-Message := "Validation of %{control:Tmp-String-0} is %{jpathvalidate:$.my.json.payload[1]}"
+  &Reply-Message := "Validation of %{control.Tmp-String-0} is %{jpathvalidate:$.my.json.payload[1]}"
 }
 ----
 
@@ -185,7 +185,7 @@ update control {
   &Tmp-String-0 := "caipirinha/gelada"
 }
 update reply {
-  &Reply-Message := "The string %{control:Tmp-String-0} should be %{jsonquote:%{control:Tmp-String-0}} to be a valid JSON string."
+  &Reply-Message := "The string %{control.Tmp-String-0} should be %{jsonquote:%{control.Tmp-String-0}} to be a valid JSON string."
 }
 ----
 
index 0baf09786a0179edf91f521f193e6c80aaa8f725..789b8a04f2ccb9913aa1bef500ee96e5a5dcc646 100644 (file)
@@ -742,7 +742,7 @@ update control {
     &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?(objectClass=radiusprofile)"
 }
 update reply {
-    &Reply-Message := "The LDAP url is %{ldap_escape:%{control:Tmp-String-0}}"
+    &Reply-Message := "The LDAP url is %{ldap_escape:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -766,7 +766,7 @@ update control {
     &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
 }
 update reply {
-    &Reply-Message := "The LDAP url is %{ldap_unescape:%{control:Tmp-String-0}}"
+    &Reply-Message := "The LDAP url is %{ldap_unescape:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -830,7 +830,7 @@ ldap {
                filter = '(objectClass=posixGroup)'
 #              scope = 'sub'
 #              name_attribute = cn
-#              membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+#              membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
                membership_attribute = 'memberOf'
 #              cacheable_name = 'no'
 #              cacheable_dn = 'no'
index 0a54808cf511ea74f001f06c65370bf3990b87fd..5f38b8533a9bc552ee87fae8b56bfd468ce88345 100644 (file)
@@ -339,7 +339,7 @@ Don't log anything for other `link:https://freeradius.org/rfc/rfc2866.html#Acct-
 linelog {
 #      delimiter = "\n"
        format = "This is a log message for %{User-Name}"
-       reference = "messages.%{%{reply:Packet-Type}:-default}"
+       reference = "messages.%{%{reply.Packet-Type}:-default}"
        messages {
                default = "Unknown packet type %{Packet-Type}"
                Access-Accept = "Sent accept: %{User-Name}"
index 97135ec7d53fe6045d98f41b491ec4f419e8b6fd..a11fb1a5ef104753bc03fa045f06ec560f7e1a5e 100644 (file)
@@ -83,10 +83,10 @@ sqlippool {
        attribute_name = radius.Framed-IP-Address
        pool_key = "%{NAS-Port}"
        messages {
-               exists = "Existing IP: %{reply:${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
-               success = "Allocated IP: %{reply:${..attribute_name}} from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               exists = "Existing IP: %{reply.${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               success = "Allocated IP: %{reply.${..attribute_name}} from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
                clear = "Released IP %{${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} user %{User-Name})"
-               failed = "IP Allocation FAILED from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               failed = "IP Allocation FAILED from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
                nopool = "No Pool-Name defined (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
        }
        $INCLUDE ${modconfdir}/sql/ippool/${dialect}/queries.conf
index 1221322a17833e44967aa53cef6e3981a365d010..dccce5852f3fb1ee9f6a8788a78a7fa80aad5d08 100644 (file)
@@ -62,7 +62,7 @@ basedn = "dc=example, dc=com"
 filter = "(servicePrincipalName=%{User-Name})"
 base_filter = "(objectClass=computer)"
 groupname_attribute = cn
-groupmembership_filter = "(&(objectClass=group)(member=%{control:Ldap-UserDn}))"
+groupmembership_filter = "(&(objectClass=group)(member=%{control.Ldap-UserDn}))"
 
 
 Now let's test membership of an LDAP group (the ldap bind user will
@@ -98,7 +98,7 @@ recv Access-Request {
        update config {
                &Auth-Type := Accept
        }
-#      if ("%{session-state:TLS-Client-Cert-Common-Name}" == 'client.example.com') {
+#      if ("%{session-state.TLS-Client-Cert-Common-Name}" == 'client.example.com') {
 #              update config {
 #                      &Auth-Type := Accept
 #              }
@@ -111,7 +111,7 @@ recv Access-Request {
 #                      &Reply-Message := "Your certificate is not valid."
 #              }
 #      }
-#      if (&User-Name == "host/%{session-state:TLS-Client-Cert-Common-Name}") {
+#      if (&User-Name == "host/%{session-state.TLS-Client-Cert-Common-Name}") {
 #              update config {
 #                      &Auth-Type := Accept
 #              }
index 268b7fa643054d2c3b0daebd62aa7a1e994a7e57..d9ce274989d4abcb984f78c8cffdb242b2e0dd81 100644 (file)
@@ -267,7 +267,7 @@ server coa {
                }
                if (&control:Tmp-Integer-0) {
                        update reply {
-                               &Reply-Message += "Sent updates for %{control:Tmp-Integer-0} active sessions"
+                               &Reply-Message += "Sent updates for %{control.Tmp-Integer-0} active sessions"
                        }
                        ok
                } else {
index db917afed02f1b52d8c6b0740ef1d4017e696933..ed05ad8c1e6b5ea258b380c8493a3499f08331ba 100644 (file)
@@ -1499,22 +1499,22 @@ send Access-Accept {
 #      }
 #      update reply {
 #              &WiMAX-FA-RK-Key = 0x00
-#              &WiMAX-MSK = "%{reply:EAP-MSK}"
+#              &WiMAX-MSK = "%{reply.EAP-MSK}"
 #      }
 #      wimax
 #      update reply {
-#              &Reply-Message += "%{session-state:TLS-Cert-Serial}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Expiration}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Subject}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Issuer}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Common-Name}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Subject-Alt-Name-Email}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Serial}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Expiration}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Subject}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Issuer}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Common-Name}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Subject-Alt-Name-Email}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Serial}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Expiration}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Subject}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Issuer}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Common-Name}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Subject-Alt-Name-Email}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Serial}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Expiration}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Subject}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Issuer}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Common-Name}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Subject-Alt-Name-Email}"
 #      }
 #      insert_acct_class
 #      if (&reply:EAP-Session-Id) {
index c6c27b5bcf236b24bdbec3e353889ea55f37b716..819a3dc434aa8923f68171c3ba36e591007c6ff6 100644 (file)
@@ -53,7 +53,7 @@ the resulting `Packet-Type` back to the parent request.
 ----
 call inner-tunnel {
     update parent.reply {
-        &Packet-Type := "%{reply:Packet-Type}"
+        &Packet-Type := "%{reply.Packet-Type}"
     }
 }
 ----
index 22642af38fa3523e7d0bd4354f2e15e304051138..9e3feb3c92a5ff3c5fb83d14d6f5f49220a255b7 100644 (file)
@@ -55,7 +55,7 @@ represent in datastores, or the FreeRADIUS configuration files.
 `"foo\"bar\""` +
 `"this is a long string"` +
 `"this has embedded\ncharacters"` +
-`"attribute\tvalue\nusername\t%{User-Name}\nreply-message\t%{reply:Reply-Message}"`
+`"attribute\tvalue\nusername\t%{User-Name}\nreply-message\t%{reply.Reply-Message}"`
 `"The result of 'SELECT * FROM foo WHERE 1' is: %{sql:SELECT * FROM foo WHERE 1}"`
 
 // Licenced under CC-by-NC 4.0.
index fd80d4afa017a29e19ecb62c5e455874cd9545fd..0464947273ee780d1f24ca332d195c5d930b96d6 100644 (file)
@@ -25,9 +25,9 @@ reference is replacedd with an empty string.
 .Examples
 
 `%{User-Name}` +
-`%{request:User-Name} # same as above` +
-`%{reply:User-Name}` +
-`%{outer.request:User-Name} # from inside of a TTLS/PEAP tunnel`
+`%{request.User-Name} # same as above` +
+`%{reply.User-Name}` +
+`%{outer.request.User-Name} # from inside of a TTLS/PEAP tunnel`
 
 Examples of using references inside of a string:
 
index 4d86133f56ca625e63a156c67ceeee13b48eb277..d311ebb3fd327702e270563c6ddf2bbabcfefd09 100644 (file)
@@ -27,8 +27,8 @@ update control {
 }
 
 update reply {
-    &Reply-Message := "The length of %{control:Tmp-String-0} is %{length:&control:Tmp-String-0}"
-    &Reply-Message += "The length of %{control:Framed-IP-Address} is %{length:&control:Framed-IP-Address}"
+    &Reply-Message := "The length of %{control.Tmp-String-0} is %{length:&control:Tmp-String-0}"
+    &Reply-Message += "The length of %{control.Framed-IP-Address} is %{length:&control:Framed-IP-Address}"
 }
 ----
 
@@ -122,7 +122,7 @@ update request {
 }
 
 update reply {
-    &Reply-Message := "The tag value of the second instance of Tunnel-Server-Enpoint is %{request:Tunnel-Server-Endpoint[1]}"
+    &Reply-Message := "The tag value of the second instance of Tunnel-Server-Enpoint is %{request.Tunnel-Server-Endpoint[1]}"
 }
 ----
 
@@ -153,7 +153,7 @@ update control {
     &Tmp-Octets-0 := 0x7465737431
 }
 update reply {
-    &Reply-Message := "The string value of %{control:Tmp-Octets-0} is %{string:%{control:Tmp-Octets-0}}"
+    &Reply-Message := "The string value of %{control.Tmp-Octets-0} is %{string:%{control.Tmp-Octets-0}}"
 }
 ----
 ====
@@ -328,8 +328,8 @@ update {
 }
 
 update reply {
-    &Reply-Message += "%{concat:, %{control:Tmp-String-0[*]}}"
-    &Reply-Message += "%{concat:,%{control:Tmp-String-0[*]}}"
+    &Reply-Message += "%{concat:, %{control.Tmp-String-0[*]}}"
+    &Reply-Message += "%{concat:,%{control.Tmp-String-0[*]}}"
 }
 ----
 
@@ -361,7 +361,7 @@ update control {
 "%{explode:&control:Tmp-String-0 @}"
 
 update reply {
-    &Reply-Message := "Welcome %{control:Tmp-String-0[0]}"
+    &Reply-Message := "Welcome %{control.Tmp-String-0[0]}"
 }
 ----
 
@@ -480,7 +480,7 @@ update control {
     &Tmp-String-0 := "Caipirinha"
 }
 update reply {
-    &Reply-Message := "The length of %{control:Tmp-String-0} is %{strlen:&control:Tmp-String-0}"
+    &Reply-Message := "The length of %{control.Tmp-String-0} is %{strlen:&control:Tmp-String-0}"
 }
 ----
 
@@ -505,7 +505,7 @@ update control {
     &Tmp-String-0 := "CAIPIRINHA"
 }
 update reply {
-    &Reply-Message := "tolower of %{control:Tmp-String-0} is %{tolower:%{control:Tmp-String-0}}"
+    &Reply-Message := "tolower of %{control.Tmp-String-0} is %{tolower:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -530,7 +530,7 @@ update control {
     &Tmp-String-0 := "caipirinha"
 }
 update reply {
-    &Reply-Message := "toupper of %{control:Tmp-String-0} is %{toupper:%{control:Tmp-String-0}}"
+    &Reply-Message := "toupper of %{control.Tmp-String-0} is %{toupper:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -556,7 +556,7 @@ update control {
     &Tmp-String-0 := "Caipirinha"
 }
 update reply {
-    &Reply-Message := "The base64 of %{control:Tmp-String-0} is %{base64:%{control:Tmp-String-0}}"
+    &Reply-Message := "The base64 of %{control.Tmp-String-0} is %{base64:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -580,7 +580,7 @@ update control {
     &Tmp-String-0 := "Q2FpcGlyaW5oYQ=="
 }
 update reply {
-    &Reply-Message := "The base64decode of %{control:Tmp-String-0} is %{base64decode:%{control:Tmp-String-0}}"
+    &Reply-Message := "The base64decode of %{control.Tmp-String-0} is %{base64decode:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -604,7 +604,7 @@ update control {
     &Tmp-String-0 := "10"
 }
 update reply {
-    &Reply-Message := "The %{control:Tmp-String-0} in binary is %{bin:%{control:Tmp-String-0}}"
+    &Reply-Message := "The %{control.Tmp-String-0} in binary is %{bin:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -628,7 +628,7 @@ update control {
     &Tmp-String-0 := "12345"
 }
 update reply {
-    &Reply-Message := "The value of %{control:Tmp-String-0} in hex is %{hex:%{control:Tmp-String-0}}"
+    &Reply-Message := "The value of %{control.Tmp-String-0} in hex is %{hex:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -652,7 +652,7 @@ update {
     &control:Tmp-String-0 := "http://example.org/"
 }
 update reply {
-    &Reply-Message += "The urlquote of %{control:Tmp-String-0} is %{urlquote:%{control:Tmp-String-0}}"
+    &Reply-Message += "The urlquote of %{control.Tmp-String-0} is %{urlquote:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -676,7 +676,7 @@ update {
     &control:Tmp-String-0 := "http%%3A%%2F%%2Fexample.org%%2F" # Attention for the double %.
 }
 update reply {
-    &Reply-Message += "The urlunquote of %{control:Tmp-String-0} is %{urlunquote:%{control:Tmp-String-0}}"
+    &Reply-Message += "The urlunquote of %{control.Tmp-String-0} is %{urlunquote:%{control.Tmp-String-0}}"
 }
 ----
 
@@ -703,12 +703,12 @@ update {
     &control:Tmp-String-1 := "Caipirinha"
 }
 update {
-    &control:Tmp-Octets-0 := "%{hmacmd5:%{control:Tmp-String-0} %{control:Tmp-String-1}}"
+    &control:Tmp-Octets-0 := "%{hmacmd5:%{control.Tmp-String-0} %{control.Tmp-String-1}}"
 }
 
 update reply {
-    &Reply-Message := "The HMAC-MD5 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}"
-    &Reply-Message += "The HMAC-MD5 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
+    &Reply-Message := "The HMAC-MD5 of %{control.Tmp-String-1} in octets is %{control.Tmp-Octets-0}"
+    &Reply-Message += "The HMAC-MD5 of %{control.Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
 }
 ----
 
@@ -734,12 +734,12 @@ update {
     &control:Tmp-String-1 := "Caipirinha"
 }
 update {
-    &control:Tmp-Octets-0 := "%{hmacsha1:%{control:Tmp-String-0} %{control:Tmp-String-1}}"
+    &control:Tmp-Octets-0 := "%{hmacsha1:%{control.Tmp-String-0} %{control.Tmp-String-1}}"
 }
 
 update reply {
-    &Reply-Message := "The HMAC-SHA1 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}"
-    &Reply-Message += "The HMAC-SHA1 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
+    &Reply-Message := "The HMAC-SHA1 of %{control.Tmp-String-1} in octets is %{control.Tmp-Octets-0}"
+    &Reply-Message += "The HMAC-SHA1 of %{control.Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
 }
 ----
 
@@ -765,8 +765,8 @@ update control {
     &Tmp-String-0 := "Caipirinha"
 }
 update reply {
-    &Reply-Message := "md5 of %{control:Tmp-String-0} is octal=%{md5:%{control:Tmp-String-0}}"
-    &Reply-Message := "md5 of %{control:Tmp-String-0} is hex=%{hex:%{md5:%{control:Tmp-String-0}}}"
+    &Reply-Message := "md5 of %{control.Tmp-String-0} is octal=%{md5:%{control.Tmp-String-0}}"
+    &Reply-Message := "md5 of %{control.Tmp-String-0} is hex=%{hex:%{md5:%{control.Tmp-String-0}}}"
 }
 ----
 
@@ -815,8 +815,8 @@ update {
     &control:Tmp-String-0 := "Caipirinha"
 }
 update reply {
-    &Reply-Message := "The md5 of %{control:Tmp-String-0} in octal is %{md5:%{control:Tmp-String-0}}"
-    &Reply-Message += "The md5 of %{control:Tmp-String-0} in hex is %{hex:%{md5:%{control:Tmp-String-0}}}"
+    &Reply-Message := "The md5 of %{control.Tmp-String-0} in octal is %{md5:%{control.Tmp-String-0}}"
+    &Reply-Message += "The md5 of %{control.Tmp-String-0} in hex is %{hex:%{md5:%{control.Tmp-String-0}}}"
 }
 ----
 
@@ -905,7 +905,7 @@ See also the `unpack` module, which is the inverse to `pack`.
 [source,unlang]
 ----
 update reply {
-    &Class := "%{pack:%{reply:Framed-IP-Address}%{NAS-IP-Address}}"
+    &Class := "%{pack:%{reply.Framed-IP-Address}%{NAS-IP-Address}}"
 }
 ----
 
@@ -950,7 +950,7 @@ update control {
 
 if (&control:Tmp-String-0 =~ /^(?<login>(.*))@(?<domain>(.*))$/) {
     update reply {
-        &Reply-Message := "The %{control:Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }"
+        &Reply-Message := "The %{control.Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }"
     }
 }
 ----
@@ -975,7 +975,7 @@ update control {
     &Tmp-String-0 := "Caipirinha is a light and refreshing drink!"
 }
 update reply {
-    &Reply-Message := "%{sub:/ / , %{control:Tmp-String-0}}"
+    &Reply-Message := "%{sub:/ / , %{control.Tmp-String-0}}"
 }
 ----
 
index dc691387a3b044373ba7413b76fc050357ae32f5..46a5befd79708c3fb7684c1ebc4ddbb4860ad270 100644 (file)
@@ -842,7 +842,7 @@ as the separator. Use the %{pairs:} xlat to get a list of attributes and
 values.
 
 e.g. If a response contains "Reply-Message = 'Hello', Reply-Message = 'bob'
-the expansion of "%{reply:Reply-Message[*]} will yield "Hello\\nbob"
+the expansion of "%{reply.Reply-Message[*]} will yield "Hello\\nbob"
 .RE
 .PP
 Other built in expansions
index d70e420e032483d3c175808479b0d21dd89f779f..c44116fb77b7dc16723cfc1e5a3e7e08355306ee 100644 (file)
@@ -59,7 +59,7 @@ cache cache_eap {
        #
        #  key:: Cache key.
        #
-       key = "%{%{control:State}:-%{%{reply:State}:-%{State}}}"
+       key = "%{%{control.State}:-%{%{reply.State}:-%{State}}}"
 
        #
        #  ttl:: TTL for cache entries.
index e2041833cbd205c35a6d118f6f8aa1e715a8eb07..4f6984eeff2b879d4168ea6ae8b63d291be393a6 100644 (file)
@@ -77,6 +77,6 @@ delay {
 #  received.
 #
 delay delay_reject {
-       delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}"
+       delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}"
        relative = yes
 }
index d7dd2b9d3fead440c9085aca56f40e71b1107ae8..f41d60041c27da6f74c0de53dff45cf29792096c 100644 (file)
@@ -73,13 +73,13 @@ sqlippool dhcp_sqlippool {
        #
        $INCLUDE ${modconfdir}/sql/ippool-dhcp/${dialect}/queries.conf
 
-       sqlippool_log_exists = "DHCP: Existing IP: %{reply:${attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_exists = "DHCP: Existing IP: %{reply.${attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
-       sqlippool_log_success = "DHCP: Allocated IP: %{reply:${attribute_name}} from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_success = "DHCP: Allocated IP: %{reply.${attribute_name}} from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
        sqlippool_log_clear = "DHCP: Released IP %{${attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} user %{User-Name})"
 
-       sqlippool_log_failed = "DHCP: IP Allocation FAILED from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+       sqlippool_log_failed = "DHCP: IP Allocation FAILED from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
        sqlippool_log_nopool = "DHCP: No Pool-Name defined (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 }
index d32f7f66ee9ace4137df75bd3bd63a9231051926..1e710e9986407c12ed09789667977b8d3499c46e 100644 (file)
@@ -178,7 +178,7 @@ json {
 #    &Tmp-String-0 := '$.my.json.payload[1]'
 #  }
 #  update reply {
-#    &Reply-Message := "Validation of %{control:Tmp-String-0} is %{jpathvalidate:$.my.json.payload[1]}"
+#    &Reply-Message := "Validation of %{control.Tmp-String-0} is %{jpathvalidate:$.my.json.payload[1]}"
 #  }
 #  ----
 #
@@ -202,7 +202,7 @@ json {
 #    &Tmp-String-0 := "caipirinha/gelada"
 #  }
 #  update reply {
-#    &Reply-Message := "The string %{control:Tmp-String-0} should be %{jsonquote:%{control:Tmp-String-0}} to be a valid JSON string."
+#    &Reply-Message := "The string %{control.Tmp-String-0} should be %{jsonquote:%{control.Tmp-String-0}} to be a valid JSON string."
 #  }
 #  ----
 #
index 47f33fb8212e6404993249c47bb020b70e081a15..47670e9ee29d3eb3ee95f6d001c4e103c73b1fed 100644 (file)
@@ -399,7 +399,7 @@ ldap {
                #  That is, group objects with attributes that identify
                #  members (the inverse of `membership_attribute`).
                #
-#              membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+#              membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
 
                #
                #  membership_attribute:: The attribute, in user objects, which contain
@@ -868,7 +868,7 @@ ldap {
 #      &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?(objectClass=radiusprofile)"
 #  }
 #  update reply {
-#      &Reply-Message := "The LDAP url is %{ldap_escape:%{control:Tmp-String-0}}"
+#      &Reply-Message := "The LDAP url is %{ldap_escape:%{control.Tmp-String-0}}"
 #  }
 #  ----
 #
@@ -892,7 +892,7 @@ ldap {
 #      &Tmp-String-0 := "ldap:///ou=profiles,dc=example,dc=com??sub?\28objectClass=radiusprofile\29"
 #  }
 #  update reply {
-#      &Reply-Message := "The LDAP url is %{ldap_unescape:%{control:Tmp-String-0}}"
+#      &Reply-Message := "The LDAP url is %{ldap_unescape:%{control.Tmp-String-0}}"
 #  }
 #  ----
 #
index 29d4c5e0555c4e9be1b17273e03b4eb1a96d9ed4..88dffa8a51787b5a7162ae60fdadbad6753a4852 100644 (file)
@@ -66,7 +66,7 @@ linelog {
        #
        #  May be an attribute reference, e.g. `&User-Name`, or `xlat`, `literal` or `exec`.
        #
-       reference = "messages.%{%{reply:Packet-Type}:-default}"
+       reference = "messages.%{%{reply.Packet-Type}:-default}"
 
        #
        #  messages { ... }::
index 78394fbfbbacb72c0d581b2b2ae7330145bdcf93..dbb8f055488035bee3364ac562a62d4feebf5318 100644 (file)
@@ -74,13 +74,13 @@ sqlippool {
        #  or using MySQL with InnoDB.
        #
        messages {
-               exists = "Existing IP: %{reply:${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               exists = "Existing IP: %{reply.${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
-               success = "Allocated IP: %{reply:${..attribute_name}} from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               success = "Allocated IP: %{reply.${..attribute_name}} from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
                clear = "Released IP %{${..attribute_name}} (did %{Called-Station-Id} cli %{Calling-Station-Id} user %{User-Name})"
 
-               failed = "IP Allocation FAILED from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
+               failed = "IP Allocation FAILED from %{control.Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
                nopool = "No Pool-Name defined (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
        }
index e043cd61a01557d3f4d7858aba61ff71982c5ffd..7085b532235af5f5cda288ed42abd9a50dcad8b8 100644 (file)
@@ -10,10 +10,10 @@ post-auth {
                        (clientipaddress, callingstationid, username, cui, lastaccounting) \
                VALUES \
                        ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
-                       '%{User-Name}', '%{reply:Chargeable-User-Identity}', NULL) \
+                       '%{User-Name}', '%{reply.Chargeable-User-Identity}', NULL) \
                ON DUPLICATE KEY UPDATE \
                        lastaccounting='0000-00-00 00:00:00', \
-                       cui='%{reply:Chargeable-User-Identity}'"
+                       cui='%{reply.Chargeable-User-Identity}'"
 
 }
 
index cffd99e3e7c4ffe8fcea50d532aebf5b09e74594..373c61ac0306cc4e7ad2aca927ed74231951338d 100644 (file)
@@ -13,7 +13,7 @@ post-auth {
                        (clientipaddress, callingstationid, username, cui) \
                VALUES \
                        ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
-                       '%{User-Name}', '%{reply:Chargeable-User-Identity}') \
+                       '%{User-Name}', '%{reply.Chargeable-User-Identity}') \
                ON CONFLICT ON CONSTRAINT ${..cui_table}_pkey \
                DO UPDATE SET cui = EXCLUDED.cui, lastaccounting = '-infinity'::timestamp"
 
index e6c1d491853ddde91fdf4b496cce9db5cd9360eb..a1a3df8b81fa69b2fa4dc71e9373e67e141d96e9 100644 (file)
@@ -10,7 +10,7 @@ post-auth {
                        (clientipaddress, callingstationid, username, cui, lastaccounting) \
                VALUES \
                        ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
-                       '%{User-Name}', '%{reply:Chargeable-User-Identity}', NULL)"
+                       '%{User-Name}', '%{reply.Chargeable-User-Identity}', NULL)"
 
 }
 
index 05cea96570c2d86566a0617649e79268207aeb9b..5605a29b90e1934ed934ee31839fb7dd53996534 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --      EXEC fr_dhcp_allocate_previous_or_new_framedipaddress \
---              @v_pool_name = '%{control:${pool_name}}', \
+--              @v_pool_name = '%{control.${pool_name}}', \
 --              @v_gateway = '%{DHCP-Gateway-IP-Address}', \
 --              @v_pool_key = '${pool_key}', \
 --              @v_lease_duration = ${offer_duration}, \
index acd60c396a4cbac9c3e4b096d708dde99a85245f..5c17465d2926d6fb39aa941db4d06c48565fc555 100644 (file)
@@ -27,7 +27,7 @@ alloc_existing = "\
                SELECT TOP(1) framedipaddress, expiry_time, gateway \
                FROM ${ippool_table} WITH (xlock rowlock readpast) \
                JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND pool_key = '%{pool_key}' \
                AND dhcpstatus.status IN ('dynamic', 'static') \
                ORDER BY expiry_time DESC \
@@ -46,7 +46,7 @@ alloc_requested = "\
                SELECT TOP(1) framedipaddress, expiry_time, gateway \
                FROM ${ippool_table} WITH (xlock rowlock readpast) \
                JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
                AND dhcpstatus.status = 'dynamic' \
                AND expiry_time < CURRENT_TIMESTAMP \
@@ -67,7 +67,7 @@ alloc_find = "\
                SELECT TOP(1) framedipaddress, expiry_time, gateway, pool_key \
                FROM ${ippool_table} WITH (xlock rowlock readpast) \
                JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND expiry_time < CURRENT_TIMESTAMP \
                AND dhcpstatus.status = 'dynamic' \
                ORDER BY expiry_time \
@@ -98,20 +98,20 @@ alloc_find = "\
 #              SELECT TOP (1) id FROM ( \
 #                      (SELECT TOP(1) id, 1 AS o FROM ${ippool_table} WITH (xlock rowlock readpast) \
 #                      JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#                      WHERE pool_name = '%{control:${pool_name}}' \
+#                      WHERE pool_name = '%{control.${pool_name}}' \
 #                      AND pool_key = '${pool_key}' \
 #                      AND dhcpstatus.status IN ('dynamic', 'static')) \
 #                      UNION \
 #                      (SELECT TOP(1) id, 2 AS o FROM ${ippool_table} WITH (xlock rowlock readpast) \
 #                      JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#                      WHERE pool_name = '%{control:${pool_name}}' \
+#                      WHERE pool_name = '%{control.${pool_name}}' \
 #                      AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
 #                      AND dhcpstatus.status = 'dynamic' \
 #                      AND expiry_time < CURRENT_TIMESTAMP ) \
 #                      UNION \
 #                      (SELECT TOP(1) id, 3 AS o FROM ${ippool_table} WITH (xlock rowlock readpast) \
 #                      JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#                      WHERE pool_name = '%{control:${pool_name}}' \
+#                      WHERE pool_name = '%{control.${pool_name}}' \
 #                      AND expiry_time < CURRENT_TIMESTAMP \
 #                      AND dhcpstatus.status = 'dynamic' \
 #                      ORDER BY expiry_time) \
@@ -126,7 +126,7 @@ alloc_find = "\
 #      WITH cte AS ( \
 #              SELECT TOP(1) FramedIPAddress FROM ${ippool_table} \
 #              JOIN dhcpstatus ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND expiry_time < CURRENT_TIMESTAMP \
 #              AND dhcpstatus.status = 'dynamic' \
 #              ORDER BY \
@@ -145,7 +145,7 @@ alloc_find = "\
 pool_check = "\
        SELECT TOP(1) id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:${pool_name}}'"
+       WHERE pool_name='%{control.${pool_name}}'"
 
 #
 #  This is the final IP Allocation query, which saves the allocated ip details.
@@ -165,7 +165,7 @@ pool_check = "\
 #alloc_begin = ""
 #alloc_find = "\
 #      EXEC fr_dhcp_allocate_previous_or_new_framedipaddress \
-#              @v_pool_name = '%{control:${pool_name}}', \
+#              @v_pool_name = '%{control.${pool_name}}', \
 #              @v_gateway = '%{DHCP-Gateway-IP-Address}', \
 #              @v_pool_key = '${pool_key}', \
 #              @v_lease_duration = ${offer_duration}, \
@@ -189,7 +189,7 @@ update_free = "\
                gateway = '', \
                pool_key = '', \
                expiry_time = CURRENT_TIMESTAMP \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress <> '%{DHCP-Requested-IP-Address}' \
        AND expiry_time > CURRENT_TIMESTAMP \
@@ -208,7 +208,7 @@ update_update = "\
        SET \
                expiry_time = DATEADD(SECOND,${lease_duration},CURRENT_TIMESTAMP), \
                counter = counter + 1 \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{%{DHCP-Requested-IP-Address}:-%{DHCP-Client-IP-Address}}'"
 
@@ -225,7 +225,7 @@ release_clear = "\
        SET gateway = '', \
                pool_key = '0', \
                expiry_time = CURRENT_TIMESTAMP \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{DHCP-Client-IP-Address}'"
 
@@ -240,6 +240,6 @@ release_clear = "\
 mark_update = "\
        UPDATE ${ippool_table} \
        SET status_id = (SELECT status_id FROM dhcpstatus WHERE status = 'declined') \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{DHCP-Requested-IP-Address}' \
        AND pool_key = '${pool_key}'"
index cbfec9326d4e788a12f4892446b8d23c387344b0..7cdaef877750678af6e4b1a6a99f161ff60b1174 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --     CALL fr_dhcp_allocate_previous_or_new_framedipaddress( \
---             '%{control:${pool_name}}', \
+--             '%{control.${pool_name}}', \
 --             '%{DHCP-Gateway-IP-Address}', \
 --             '${pool_key}', \
 --             ${offer_duration}, \
index 97d2ffd293fdfe7564cde5ab7705dc170818f62a..6c9bd1c65e8392397ad96c70c8f43a25ec16cf4b 100644 (file)
@@ -24,7 +24,7 @@
 alloc_existing = "\
        SELECT framedipaddress \
        FROM ${ippool_table} \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND `status` IN ('dynamic', 'static') \
        ORDER BY expiry_time DESC \
@@ -37,7 +37,7 @@ alloc_existing = "\
 alloc_requested = "\
        SELECT framedipaddress \
        FROM ${ippool_table} \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
        AND `status` = 'dynamic' \
        AND expiry_time < NOW() \
@@ -50,7 +50,7 @@ alloc_requested = "\
 alloc_find = "\
        SELECT framedipaddress \
        FROM ${ippool_table} \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND expiry_time < NOW() \
        AND `status` = 'dynamic' \
        ORDER BY expiry_time \
@@ -63,20 +63,20 @@ alloc_find = "\
 #
 #alloc_find = "\
 #      (SELECT framedipaddress, 1 AS o FROM ${ippool_table} \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND pool_key = '${pool_key}' \
 #              AND `status` IN ('dynamic', 'static') \
 #              ORDER BY expiry_time DESC LIMIT 1 FOR UPDATE ${skip_locked} \
 #      ) UNION ( \
 #      SELECT framedipaddress, 2 AS o FROM ${ippool_table} \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
 #              AND `status` = 'dynamic' \
 #              AND expiry_time < NOW() \
 #              FOR UPDATE SKIP LOCKED \
 #      ) UNION ( \
 #      SELECT framedipaddress, 3 AS o FROM ${ippool_name} \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND expiry_time < NOW() \
 #              AND `status` = 'dynamic' \
 #              ORDER BY expiry_time LIMIT 1 FOR UPDATE ${skip_locked} \
@@ -89,7 +89,7 @@ alloc_find = "\
 #
 #alloc_find = "\
 #      SELECT framedipaddress FROM ${ippool_table} \
-#      WHERE pool_name = '%{control:${pool_name}}' \
+#      WHERE pool_name = '%{control.${pool_name}}' \
 #      AND expiry_time < NOW() \
 #      AND `status` = 'dynamic' \
 #      ORDER BY \
@@ -106,7 +106,7 @@ alloc_find = "\
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:${pool-name}}' \
+       WHERE pool_name='%{control.${pool-name}}' \
        LIMIT 1"
 
 #
@@ -126,7 +126,7 @@ alloc_update = "\
 #alloc_begin = ""
 #alloc_find = "\
 #      CALL fr_dhcp_allocate_previous_or_new_framedipaddress( \
-#              '%{control:${pool_name}}', \
+#              '%{control.${pool_name}}', \
 #              '%{DHCP-Gateway-IP-Address}', \
 #              '${pool_key}', \
 #              ${offer_duration}, \
@@ -150,7 +150,7 @@ update_free = "\
                gateway = '', \
                pool_key = '', \
                expiry_time = NOW() \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress <> '%{DHCP-Requested-IP-Address}' \
        AND expiry_time > NOW() \
@@ -168,7 +168,7 @@ update_update = "\
        SET \
                expiry_time = NOW() + INTERVAL ${lease_duration} SECOND, \
                counter = counter + 1 \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{%{DHCP-Requested-IP-Address}:-%{DHCP-Client-IP-Address}}'"
 
@@ -185,7 +185,7 @@ release_clear = "\
        SET gateway = '', \
                pool_key = '0', \
                expiry_time = NOW() \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{DHCP-Client-IP-Address}'"
 
@@ -200,6 +200,6 @@ release_clear = "\
 mark_update = "\
        UPDATE ${ippool_table} \
        SET `status` = 'declined' \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{DHCP-Requested-IP-Address}' \
        AND pool_key = '${pool_key}'"
index 6114a2c36f4d50804e81c180f890d348ff3b1947..65a61eb3a59f90481504cdc910c2bc317693127c 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --      SELECT fr_dhcp_allocate_previous_or_new_framedipaddress( \
---              '%{control:${pool_name}}', \
+--              '%{control.${pool_name}}', \
 --              '%{DHCP-Gateway-IP-Address}', \
 --              '${pool_key}', \
 --              ${offer_duration}, \
index cea76570b32976009f97fd44fa1bdb6df655bf83..5fa398500bb3e3d15bd0364c077ff0192f6a02b7 100644 (file)
@@ -22,7 +22,7 @@
 alloc_begin = ""
 alloc_find = "\
        SELECT fr_dhcp_allocate_previous_or_new_framedipaddress( \
-               '%{control:${pool_name}}', \
+               '%{control.${pool_name}}', \
                '%{DHCP-Gateway-IP-Address}', \
                '${pool_key}', \
                '${offer_duration}', \
@@ -43,7 +43,7 @@ alloc_commit = ""
 #                      FROM ${ippool_table} \
 #                      JOIN dhcpstatus \
 #                      ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#                      WHERE pool_name = '%{control:${pool_name}}' \
+#                      WHERE pool_name = '%{control.${pool_name}}' \
 #                      AND expiry_time < current_timestamp \
 #                      AND dhcpstatus.status = 'dynamic' \
 #                      ORDER BY DBMS_RANDOM.VALUE \
@@ -61,7 +61,7 @@ alloc_commit = ""
 #                      FROM ${ippool_table} \
 #                      JOIN dhcpstatus \
 #                      ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#                      WHERE pool_name = '%{control:${pool_name}}' \
+#                      WHERE pool_name = '%{control.${pool_name}}' \
 #                      AND expiry_time < current_timestamp \
 #                      AND dhcpstatus.status = 'dynamic' \
 #                      ORDER BY DBMS_RANDOM.VALUE \
@@ -76,7 +76,7 @@ alloc_commit = ""
 #              SELECT id FROM ${ippool_table} \
 #              JOIN dhcpstatus \
 #              ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND expiry_time < current_timestamp \
 #              AND dhcpstatus.status = 'dynamic' \
 #              ORDER BY DBMS_RANDOM.VALUE \
@@ -94,7 +94,7 @@ pool_check = "\
        FROM (\
                SELECT id \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:Pool-Name}'\
+               WHERE pool_name = '%{control.Pool-Name}'\
        ) WHERE ROWNUM = 1"
 
 #
@@ -127,7 +127,7 @@ update_update = "\
        SET \
                expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1), \
                counter = counter + 1 \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{%{DHCP-Requested-IP-Address}:-%{DHCP-Client-IP-Address}}'"
 update_commit = "commit"
@@ -141,7 +141,7 @@ release_clear = "\
        SET gateway = '', \
                pool_key = '0', \
                expiry_time = current_timestamp \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{DHCP-Client-IP-Address}'"
 release_commit = "commit"
@@ -153,7 +153,7 @@ mark_begin = "commit"
 mark_update = "\
        UPDATE ${ippool_table} \
        SET status_id = (SELECT status_id FROM dhcpstatus WHERE status = 'declined') \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{DHCP-Requested-IP-Address}' \
        AND pool_key = '${pool_key}'"
 mark_commit = "commit"
index 1141057612bd4c76a65e299a37749af5f7072db7..773de2c547921c41759003fa3378efcf5a715867 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --     SELECT fr_dhcp_allocate_previous_or_new_framedipaddress( \
---             '%{control:${pool_name}}', \
+--             '%{control.${pool_name}}', \
 --             '%{DHCP-Gateway-IP-Address}', \
 --             '${pool_key}', \
 --             ${offer_duration}, \
index d1fc8f65e599c5bfe5ec80e38912cca4d3e91920..0610fd2bd64507e100f93338b7b27ac4b8e8c769 100644 (file)
@@ -29,7 +29,7 @@ alloc_existing = "\
        WITH cte AS ( \
                SELECT framedipaddress \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND pool_key = '${pool_key}' \
                AND status IN ('dynamic', 'static') \
                ORDER BY expiry_time DESC \
@@ -51,7 +51,7 @@ alloc_requested = "\
        WITH cte AS ( \
                SELECT framedipaddress \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
                AND expiry_time < 'now'::timestamp(0) \
                AND status = 'dynamic' \
@@ -72,7 +72,7 @@ alloc_find = "\
        WITH cte AS ( \
                SELECT framedipaddress \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND expiry_time < 'now'::timestamp(0) \
                AND status = 'dynamic' \
                ORDER BY expiry_time \
@@ -94,7 +94,7 @@ alloc_find = "\
 #
 #alloc_find = "\
 #      SELECT framedipaddress FROM ${ippool_table} \
-#      WHERE pool_name = '%{control:${pool_name}}' AND expiry_time < 'now'::timestamp(0) \
+#      WHERE pool_name = '%{control.${pool_name}}' AND expiry_time < 'now'::timestamp(0) \
 #      AND status = 'dynamic' \
 #      ORDER BY RANDOM() \
 #      LIMIT 1 \
@@ -128,7 +128,7 @@ alloc_commit = ""
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:${pool_name}}' \
+       WHERE pool_name='%{control.${pool_name}}' \
        LIMIT 1"
 
 #
@@ -147,7 +147,7 @@ pool_check = "\
 #alloc_find = "\
 #      /*NO LOAD BALANCE*/ \
 #      SELECT fr_allocate_previous_or_new_framedipaddress( \
-#              '%{control:${pool_name}}', \
+#              '%{control.${pool_name}}', \
 #              '%{DHCP-Gateway-IP-Address}', \
 #              '${pool_key}', \
 #              '${offer_duration}', \
@@ -170,7 +170,7 @@ update_free = "\
                gateway = '', \
                pool_key = '', \
                expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress <> '%{DHCP-Requested-IP-Address}' \
        AND expiry_time > 'now'::timestamp(0) \
@@ -188,7 +188,7 @@ update_update = "\
        SET \
                expiry_time = 'now'::timestamp(0) + '${lease_duration} second'::interval, \
                counter = counter + 1 \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{%{DHCP-Requested-IP-Address}:-%{DHCP-Client-IP-Address}}'"
 
@@ -205,7 +205,7 @@ release_clear = "\
        SET gateway = '', \
                pool_key = '', \
                expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{DHCP-Client-IP-Address}'"
 
@@ -220,6 +220,6 @@ release_clear = "\
 mark_update = "\
        UPDATE ${ippool_table} \
        SET status = 'declined' \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{DHCP-Requested-IP-Address}' \
        AND pool_key = '${pool_key}'"
index 63e219490c0c52367df3dc4993507311fa157042..b3df89e9e3ab6afb66cc0d9680772fb4468e9521 100644 (file)
@@ -38,7 +38,7 @@ alloc_existing = "SELECT framedipaddress \
        FROM ${ippool_table} \
        JOIN dhcpstatus \
        ON ${ippool_table}.status_id = dhcpstatus.status_id \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND status IN ('dynamic', 'static') \
        ORDER BY expiry_time DESC \
@@ -51,7 +51,7 @@ alloc_requested = "SELECT framedipaddress \
        FROM ${ippool_table} \
        JOIN dhcpstatus \
        ON ${ippool_table}.status_id = dhcpstatus.status_id \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \
        AND status = 'dynamic'"
 
@@ -63,7 +63,7 @@ alloc_find = "\
        FROM ${ippool_table} \
        JOIN dhcpstatus \
        ON ${ippool_table}.status_id = dhcpstatus.status_id \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND expiry_time < datetime('now') \
        AND status = 'dynamic' \
        ORDER BY expiry_time LIMIT 1"
@@ -75,7 +75,7 @@ alloc_find = "\
 #      SELECT framedipaddress FROM ${ippool_table} \
 #      JOIN dhcpstatus \
 #      ON ${ippool_table}.status_id = dhcpstatus.status_id \
-#      WHERE pool_name = '%{control:${pool-name}}' \
+#      WHERE pool_name = '%{control.${pool-name}}' \
 #      AND expiry_time IS NULL \
 #      AND status = 'dynamic' \
 #      ORDER BY RAND() \
@@ -91,7 +91,7 @@ alloc_find = "\
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:Pool-Name}' \
+       WHERE pool_name='%{control.Pool-Name}' \
        LIMIT 1"
 
 #
@@ -120,7 +120,7 @@ update_free = "\
                gateway = '', \
                pool_key = '', \
                expiry_time = datetime('now') \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress <> '%{DHCP-Requested-IP-Address}' \
        AND expiry_time > datetime('now') \
@@ -139,7 +139,7 @@ update_update = "\
        SET \
                expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch'), \
                counter = counter + 1 \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{%{DHCP-Requested-IP-Address}:-%{DHCP-Client-IP-Address}}'"
 
@@ -156,7 +156,7 @@ release_clear = "\
        SET gateway = '', \
                pool_key = '0', \
                expiry_time = datetime('now') \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND pool_key = '${pool_key}' \
        AND framedipaddress = '%{DHCP-Client-IP-Address}' \
        AND ${ippool_table}.status_id IN \
@@ -173,6 +173,6 @@ release_clear = "\
 mark_update = "\
        UPDATE ${ippool_table} \
        SET status_id = (SELECT status_id FROM dhcpstatus WHERE status = 'declined') \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND framedipaddress = '%{DHCP-Requested-IP-Address}' \
        AND pool_key = '${pool_key}'"
index 54e9f26684fd27e9c7145d977e0eed3e501fff99..52fe0b81dab574240b3d6f136071623c8947cebe 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --      EXEC fr_allocate_previous_or_new_framedipaddress \
---              @v_pool_name = '%{control:${pool_name}}', \
+--              @v_pool_name = '%{control.${pool_name}}', \
 --              @v_username = '%{User-Name}', \
 --              @v_callingstationid = '%{Calling-Station-Id}', \
 --              @v_nasipaddress = '%{NAS-IP-Address}', \
index 9b560260b8290c0c3119ea8b53a4d156bb22b69b..31b158e732b6bc797872b203b1d407df03b0b14c 100644 (file)
@@ -20,7 +20,7 @@ alloc_existing = "\
                SELECT TOP(1) \
                FramedIPAddress, NASIPAddress, pool_key, CallingStationId, UserName, expiry_time \
                FROM ${ippool_table} WITH (xlock rowlock readpast) \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND NASIPAddress = '%{NAS-IP-Address}' \
                AND pool_key = '${pool_key}'  \
                ORDER BY expiry_time DESC \
@@ -42,7 +42,7 @@ alloc_find = "\
                SELECT TOP(1) \
                FramedIPAddress, NASIPAddress, pool_key, CallingStationId, UserName, expiry_time \
                FROM ${ippool_table} WITH (xlock rowlock readpast) \
-               WHERE pool_name = '%{control:${pool_name}}' \
+               WHERE pool_name = '%{control.${pool_name}}' \
                AND expiry_time < CURRENT_TIMESTAMP \
                ORDER BY expiry_time \
        ) \
@@ -65,7 +65,7 @@ alloc_find = "\
 #alloc_find = "\
 #      WITH cte AS ( \
 #              SELECT TOP(1) FramedIPAddress FROM ${ippool_table} \
-#              WHERE pool_name = '%{control:${pool_name}}' \
+#              WHERE pool_name = '%{control.${pool_name}}' \
 #              AND expiry_time < CURRENT_TIMESTAMP \
 #              ORDER BY \
 #                      newid() \
@@ -83,7 +83,7 @@ alloc_find = "\
 pool_check = "\
        SELECT TOP(1) id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:${pool_name}}'"
+       WHERE pool_name='%{control.${pool_name}}'"
 
 #
 #  This is the final IP Allocation query, which saves the allocated ip details.
@@ -103,7 +103,7 @@ pool_check = "\
 #alloc_begin = ""
 #alloc_find = "\
 #      EXEC fr_allocate_previous_or_new_framedipaddress \
-#              @v_pool_name = '%{control:${pool_name}}', \
+#              @v_pool_name = '%{control.${pool_name}}', \
 #              @v_username = '%{User-Name}', \
 #              @v_callingstationid = '%{Calling-Station-Id}', \
 #              @v_nasipaddress = '%{NAS-IP-Address}', \
index cc934aaee1bfa5c90a034f72c73f77d0d8ebf4ce..45088f72379c4d227f7478bc2a3e8463c0a5c4cd 100644 (file)
@@ -31,7 +31,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --     CALL fr_allocate_previous_or_new_framedipaddress( \
---             '%{control:${pool_name}}', \
+--             '%{control.${pool_name}}', \
 --             '%{User-Name}', \
 --             '%{Calling-Station-Id}', \
 --             '%{NAS-IP-Address}', \
index 3835cd55d097c48500e69b706f90ce85c1938d27..709433525117c2d09964756d710a05955e285576 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --     CALL fr_allocate_previous_or_new_framedipaddress( \
---             '%{control:${pool_name}}', \
+--             '%{control.${pool_name}}', \
 --             '%{User-Name}', \
 --             '%{Calling-Station-Id}', \
 --             '%{NAS-IP-Address}', \
index 1a4fb090df3b1915a6ecdbb7bb9fc49a86c6fb52..31106cc519668af4c2b5be752966d9c55c11375f 100644 (file)
@@ -52,7 +52,7 @@
 #
 alloc_existing = "\
        SELECT framedipaddress FROM ${ippool_table} \
-       WHERE pool_lane = '%{control:Pool-Name}' \
+       WHERE pool_lane = '%{control.Pool-Name}' \
        AND nasipaddress = '%{NAS-IP-Address}' \
        AND pool_key = '${pool_key}' \
        ORDER BY expiry_time DESC \
@@ -66,7 +66,7 @@ alloc_existing = "\
 #
 alloc_find = "\
        SELECT framedipaddress FROM ${ippool_table} \
-       WHERE pool_name = '%{control:Pool-Name}' \
+       WHERE pool_name = '%{control.Pool-Name}' \
        AND expiry_time < NOW() \
        ORDER BY expiry_time \
        LIMIT 1 \
@@ -78,7 +78,7 @@ alloc_find = "\
 #
 #alloc_find = "\
 #      SELECT framedipaddress FROM ${ippool_table} \
-#      WHERE pool_name = '%{control:Pool-Name}' \
+#      WHERE pool_name = '%{control.Pool-Name}' \
 #      AND expiry_time IS 0 \
 #      ORDER BY \
 #              RAND() \
@@ -98,7 +98,7 @@ alloc_find = "\
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:Pool-Name}' \
+       WHERE pool_name='%{control.Pool-Name}' \
        LIMIT 1"
 
 #
@@ -125,7 +125,7 @@ alloc_update = "\
 #alloc_existing = ""
 #alloc_find = "\
 #      CALL fr_allocate_previous_or_new_framedipaddress( \
-#              '%{control:${pool_name}}', \
+#              '%{control.${pool_name}}', \
 #              '%{User-Name}', \
 #              '%{Calling-Station-Id}', \
 #              '%{NAS-IP-Address}', \
index d157112f605ed04b6e5beb9e7e33aa68e96a5f33..a2e88f266ac1cc87e9790aff0d10e789a5ab84d5 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --         SELECT fr_allocate_previous_or_new_framedipaddress( \
---                 '%{control:${pool_name}}', \
+--                 '%{control.${pool_name}}', \
 --                 '%{User-Name}', \
 --                 '%{%{Calling-Station-Id}:-0}', \
 --                 '%{NAS-IP-Address}', \
index e13d3bfa3eee6e961039b646b681743bdee4b9a2..de12f207939ca4e7598f2fb97468129b08472dbf 100644 (file)
@@ -17,7 +17,7 @@ alloc_find = "\
                SELECT id FROM ( \
                        SELECT * \
                        FROM ${ippool_table} \
-                       WHERE pool_name = '%{control:Pool-Name}' \
+                       WHERE pool_name = '%{control.Pool-Name}' \
                        AND ( \
                                expiry_time < current_timestamp \
                                OR ( nasipaddress = '%{NAS-IP-Address}' AND pool_key = '${pool_key}' ) \
@@ -38,7 +38,7 @@ alloc_find = "\
 #              SELECT id FROM ( \
 #                      SELECT * \
 #                      FROM ${ippool_table} \
-#                      WHERE pool_name = '%{control:Pool-Name}' \
+#                      WHERE pool_name = '%{control.Pool-Name}' \
 #                      AND ( \
 #                              expiry_time < current_timestamp \
 #                              OR ( nasipaddress = '%{NAS-IP-Address}' AND pool_key = '${pool_key}' ) \
@@ -59,7 +59,7 @@ alloc_find = "\
 #              SELECT id FROM ( \
 #                      SELECT * \
 #                      FROM ${ippool_table} \
-#                      WHERE pool_name = '%{control:Pool-Name}' \
+#                      WHERE pool_name = '%{control.Pool-Name}' \
 #                      AND expiry_time < current_timestamp \
 #                      ORDER BY DBMS_RANDOM.VALUE \
 #              ) WHERE ROWNUM <= 1 \
@@ -74,7 +74,7 @@ alloc_find = "\
 #              SELECT id FROM (\
 #                      SELECT * \
 #                      FROM ${ippool_table} \
-#                      WHERE pool_name = '%{control:Pool-Name}' \
+#                      WHERE pool_name = '%{control.Pool-Name}' \
 #                      AND expiry_time < current_timestamp \
 #                      ORDER BY DBMS_RANDOM.VALUE \
 #              ) WHERE ROWNUM <= 1 \
@@ -91,7 +91,7 @@ pool_check = "\
        FROM (\
                SELECT id \
                FROM ${ippool_table} \
-               WHERE pool_name='%{control:Pool-Name}'\
+               WHERE pool_name='%{control.Pool-Name}'\
        ) \
        WHERE ROWNUM = 1"
 
@@ -116,7 +116,7 @@ alloc_update = "\
 #alloc_begin = ""
 #alloc_find = "\
 #      SELECT fr_allocate_previous_or_new_framedipaddress( \
-#              '%{control:Pool-Name}', \
+#              '%{control.Pool-Name}', \
 #              '%{SQL-User-Name}', \
 #              '%{%{Calling-Station-Id}:-0}', \
 #              '%{NAS-IP-Address}', \
index bf94abd6995a323ed1e566b319b4745265cb11e0..5b3d46d8ddd5604e549d46f2348993beedcda159 100644 (file)
@@ -18,7 +18,7 @@
 -- allocate_begin = ""
 -- allocate_find = "\
 --     SELECT fr_allocate_previous_or_new_framedipaddress( \
---             '%{control:${pool_name}}', \
+--             '%{control.${pool_name}}', \
 --             '%{User-Name}', \
 --             '%{Calling-Station-Id}', \
 --             '%{NAS-IP-Address}', \
index 01d474018aff1593403dfc8fe146b019ef0dd832..a49b1bd041c3f98916af4002293bd5ad34491424 100644 (file)
@@ -18,7 +18,7 @@ alloc_existing "\
        WITH cte AS ( \
                SELECT framedipaddress \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:Pool-Name}' \
+               WHERE pool_name = '%{control.Pool-Name}' \
                AND nasipaddress = '%{NAS-IP-Address}' \
                AND pool_key = '${pool_key}' \
                LIMIT 1 \
@@ -43,7 +43,7 @@ alloc_find = "\
        WITH cte AS ( \
                SELECT framedipaddress \
                FROM ${ippool_table} \
-               WHERE pool_name = '%{control:Pool-Name}' \
+               WHERE pool_name = '%{control.Pool-Name}' \
                AND expiry_time < 'now'::timestamp(0) \
                ORDER BY expiry_time \
                LIMIT 1 \
@@ -75,7 +75,7 @@ alloc_commit = ""
 #
 #alloc_find = "\
 #      SELECT framedipaddress FROM ${ippool_table} \
-#      WHERE pool_name = '%{control:${pool_name}}' AND expiry_time < 'now'::timestamp(0) \
+#      WHERE pool_name = '%{control.${pool_name}}' AND expiry_time < 'now'::timestamp(0) \
 #      ORDER BY RANDOM() \
 #      LIMIT 1 \
 #      FOR UPDATE ${skip_locked}"
@@ -89,7 +89,7 @@ alloc_commit = ""
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:Pool-Name}' \
+       WHERE pool_name='%{control.Pool-Name}' \
        LIMIT 1"
 
 #
@@ -124,7 +124,7 @@ pool_check = "\
 #alloc_find = "\
 #      /*NO LOAD BALANCE*/ \
 #      SELECT fr_allocate_previous_or_new_framedipaddress( \
-#              '%{control:${pool_name}}', \
+#              '%{control.${pool_name}}', \
 #              '%{SQL-User-Name}', \
 #              '%{Calling-Station-Id}', \
 #              '%{NAS-IP-Address}', \
index 4628cf993df78d1fa07be63030616746991fc637..441559d522a4bb9e989bdab4c005d268ad643b67 100644 (file)
@@ -27,7 +27,7 @@ alloc_commit = "COMMIT"
 alloc_existing = "\
        SELECT framedipaddress \
        FROM ${ippool_table} \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        AND nasipaddress = '%{NAS-IP-Address}' \
        AND pool_key = '${pool_key}' \
        ORDER BY expiry_time DESC \
@@ -40,7 +40,7 @@ alloc_existing = "\
 alloc_find = "\
        SELECT framedipaddress \
        FROM ${ippool_table} \
-       WHERE pool_name = '%{control:${pool_name}}' \
+       WHERE pool_name = '%{control.${pool_name}}' \
        expiry_time < datetime('now') \
        ORDER BY expiry_time \
        LIMIT 1"
@@ -54,7 +54,7 @@ alloc_find = "\
 #alloc_find = "\
 #      SELECT framedipaddress \
 #      FROM ${ippool_table} \
-#      WHERE pool_name = '%{control:${pool_name}}' \
+#      WHERE pool_name = '%{control.${pool_name}}' \
 #      AND expiry_time < datetime('now') \
 #      ORDER BY RAND() \
 #      LIMIT 1"
@@ -68,7 +68,7 @@ alloc_find = "\
 pool_check = "\
        SELECT id \
        FROM ${ippool_table} \
-       WHERE pool_name='%{control:${pool_name}}' \
+       WHERE pool_name='%{control.${pool_name}}' \
        LIMIT 1"
 
 #
index a575e44d5287e4dd6e963ac7bdb84663ff6cda54..42e56ba3a89662be9127ceb112ba218aa45f1f94 100644 (file)
@@ -363,6 +363,6 @@ post-auth {
                VALUES ( \
                        '%{SQL-User-Name}', \
                        '%{%{User-Password}:-%{Chap-Password}}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        '%{expr:%l * 1000 + %M / 1000}')"
 }
index 6f1b9a063d858f3fee1502d9b0f6df1151c87231..b4e2754924ea0fbf8e5af91fec10b6cc140b8920 100644 (file)
@@ -353,6 +353,6 @@ post-auth {
                VALUES(\
                        '%{User-Name}', \
                        '%{%{User-Password}:-CHAP-PASSWORD}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        '%S.%{expr:%M / 1000}')"
 }
index 6923c11748ae395ad34d3f9547386ea103c0700d..83e901561487e23f0f46707a36e7b0f19b584342 100644 (file)
@@ -36,5 +36,5 @@ postauth_query = "INSERT INTO wimax \
                  (username, authdate, spi, mipkey, lifetime) \
                  VALUES ( \
                  '%{User-Name}', '%S' \
-                 '%{%{reply:WiMAX-MN-hHA-MIP4-SPI}:-%{reply:WiMAX-MN-hHA-MIP6-SPI}}', \
-                 '%{%{reply:WiMAX-MN-hHA-MIP4-Key}:-%{reply:WiMAX-MN-hHA-MIP6-Key}}', '%{%{reply:Session-Timeout}:-86400}' )"
+                 '%{%{reply.WiMAX-MN-hHA-MIP4-SPI}:-%{reply.WiMAX-MN-hHA-MIP6-SPI}}', \
+                 '%{%{reply.WiMAX-MN-hHA-MIP4-Key}:-%{reply.WiMAX-MN-hHA-MIP6-Key}}', '%{%{reply.Session-Timeout}:-86400}' )"
index 77daae146eb5f0d89c730b9f26065b7977adcd9a..96fcbe5036d5eba53fd90ce8670797cf509f0c5d 100644 (file)
@@ -385,6 +385,6 @@ post-auth {
                VALUES ( \
                        '%{SQL-User-Name}', \
                        '%{%{User-Password}:-%{Chap-Password}}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        '%S.%M')"
 }
index 18bc1c88452c5a3cdd0588c865d138946c38d38f..3bc510158edc804083a5ece5a48cdf0f244506bc 100644 (file)
@@ -399,6 +399,6 @@ post-auth {
                VALUES (\
                        '%{User-Name}', \
                        '%{%{User-Password}:-%{Chap-Password}}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        TO_TIMESTAMP('%S.%M','YYYY-MM-DDHH24:MI:SS.FF'))"
 }
index 528b421ddc70d9eee4b0c8fc2eaf1f21d65e11cf..72926eb6648dddc89ee3005aacdec2052ac77ce5 100644 (file)
@@ -454,6 +454,6 @@ post-auth {
                VALUES(\
                        '%{User-Name}', \
                        '%{%{User-Password}:-Chap-Password}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        '%S.%M')"
 }
index fe51a85b0761fe82c4cf54b16cbcc0d8a0d588c4..3a14ca121ab10071cab7577930178472b9d2b29f 100644 (file)
@@ -399,6 +399,6 @@ post-auth {
                VALUES ( \
                        '%{SQL-User-Name}', \
                        '%{%{User-Password}:-%{Chap-Password}}', \
-                       '%{reply:Packet-Type}', \
+                       '%{reply.Packet-Type}', \
                        '%S.%M')"
 }
index 43d4dea9726ddce24d512a1e4da03183f91044cb..a2ed6e9cd50f9665bda16d7b643d20bad88a208b 100644 (file)
@@ -83,7 +83,7 @@ cui-inner.post-auth {
        if (&outer.request:Chargeable-User-Identity && \
            (&outer.request:Operator-Name || ('${policy.cui_require_operator_name}' != 'yes'))) {
                update reply {
-                       &Chargeable-User-Identity := "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}%{%{outer.request:Operator-Name}:-}}}"
+                       &Chargeable-User-Identity := "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}%{%{outer.request.Operator-Name}:-}}}"
                }
        }
 }
index 410767ea57830d35c6887a852dc064e4abb5fb4a..09e5995ae83270d6424027eab849dbcdd86c3bc5 100644 (file)
@@ -17,9 +17,9 @@ tacacs_set_authentication_status {
 tacacs_pap {
        subrequest RADIUS.Access-Request {
                update {
-                       &request:User-Name := &parent.request:TACACS-User-Name
-                       &request:User-Password := &parent.request:TACACS-Data
-                       &control:Cleartext-Password := &parent.control:Cleartext-Password
+                       &request.User-Name := &parent.request.TACACS-User-Name
+                       &request.User-Password := &parent.request.TACACS-Data
+                       &control.Cleartext-Password := &parent.control.Cleartext-Password
                }
                pap.authorize
                pap.authenticate
@@ -31,7 +31,7 @@ tacacs_pap {
 tacacs_chap {
        subrequest RADIUS.Access-Request {
                update {
-                       &request:User-Name := &parent.request:TACACS-User-Name
+                       &request.User-Name := &parent.request.TACACS-User-Name
 
                        #
                        #  TACACS-Data length N is 1 octet of ID, followed by
@@ -41,9 +41,9 @@ tacacs_chap {
                        #  @todo - update code to create these, so that the
                        #  poor user doesn't need to.
                        #
-#                      &request:CHAP-Password := ...
-#                      &request:CHAP-Challenge := ...
-                       &control:Cleartext-Password := &parent.control:Cleartext-Password
+#                      &request.CHAP-Password := ...
+#                      &request.CHAP-Challenge := ...
+                       &control.Cleartext-Password := &parent.control.Cleartext-Password
                }
                chap.authenticate
        }
@@ -54,7 +54,7 @@ tacacs_chap {
 tacacs_mschap {
        subrequest RADIUS.Access-Request {
                update {
-                       &request:User-Name := &parent.request:TACACS-User-Name
+                       &request.User-Name := &parent.request.TACACS-User-Name
 
                        #
                        #  TACACS-Data length N is 1 octet of ID, followed by
@@ -64,9 +64,9 @@ tacacs_mschap {
                        #  @todo - update code to create these, so that the
                        #  poor user doesn't need to.
                        #
-#                      &request:MS-CHAP-Challenge := ...
-#                      &request:MS-CHAP-Response := ...
-                       &control:Cleartext-Password := &parent.control:Cleartext-Password
+#                      &request.MS-CHAP-Challenge := ...
+#                      &request.MS-CHAP-Response := ...
+                       &control.Cleartext-Password := &parent.control.Cleartext-Password
                }
                chap.authenticate
        }
index 81727b5132cdd8eb8a6aeca052d2a59a169b5e7c..992abb84cd121d42bc569a78a76ef5663349357b 100644 (file)
@@ -50,7 +50,7 @@ recv Access-Request {
        #  Check the client certificate matches a string, and reject otherwise
        #
 
-#      if ("%{session-state:TLS-Client-Cert-Common-Name}" == 'client.example.com') {
+#      if ("%{session-state.TLS-Client-Cert-Common-Name}" == 'client.example.com') {
 #              update config {
 #                      &Auth-Type := Accept
 #              }
@@ -68,7 +68,7 @@ recv Access-Request {
        #
        #  Check the client certificate common name against the supplied User-Name
        #
-#      if (&User-Name == "host/%{session-state:TLS-Client-Cert-Common-Name}") {
+#      if (&User-Name == "host/%{session-state.TLS-Client-Cert-Common-Name}") {
 #              update config {
 #                      &Auth-Type := Accept
 #              }
@@ -95,7 +95,7 @@ recv Access-Request {
        #  filter = "(servicePrincipalName=%{User-Name})"
        #  base_filter = "(objectClass=computer)"
        #  groupname_attribute = cn
-       #  groupmembership_filter = "(&(objectClass=group)(member=%{control:Ldap-UserDn}))"
+       #  groupmembership_filter = "(&(objectClass=group)(member=%{control.Ldap-UserDn}))"
 
 #      ldap
 
index 3941c522063d8db3d8074abc5e3196244cc387dc..941cf1ccd0189c24fceaa31dd944b472741405fd 100644 (file)
@@ -257,7 +257,7 @@ server coa {
                #
                if (&control:Tmp-Integer-0) {
                        update reply {
-                               &Reply-Message += "Sent updates for %{control:Tmp-Integer-0} active sessions"
+                               &Reply-Message += "Sent updates for %{control.Tmp-Integer-0} active sessions"
                        }
                        ok
                } else {
index 14aa785bd9016b650deb543eb129f69793d6b4de..abe297f660d5bead71f4fddfa13ffff27647a219 100644 (file)
@@ -1243,7 +1243,7 @@ send Access-Accept {
        #
 #      update reply {
 #              &WiMAX-FA-RK-Key = 0x00
-#              &WiMAX-MSK = "%{reply:EAP-MSK}"
+#              &WiMAX-MSK = "%{reply.EAP-MSK}"
 #      }
 
        #  You may want to delete the `MS-MPPE-*-Keys` from the
@@ -1268,19 +1268,19 @@ send Access-Accept {
        #  available).
        #
 #      update reply {
-#              &Reply-Message += "%{session-state:TLS-Cert-Serial}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Expiration}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Subject}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Issuer}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Common-Name}"
-#              &Reply-Message += "%{session-state:TLS-Cert-Subject-Alt-Name-Email}"
-#
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Serial}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Expiration}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Subject}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Issuer}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Common-Name}"
-#              &Reply-Message += "%{session-state:TLS-Client-Cert-Subject-Alt-Name-Email}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Serial}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Expiration}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Subject}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Issuer}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Common-Name}"
+#              &Reply-Message += "%{session-state.TLS-Cert-Subject-Alt-Name-Email}"
+#
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Serial}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Expiration}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Subject}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Issuer}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Common-Name}"
+#              &Reply-Message += "%{session-state.TLS-Client-Cert-Subject-Alt-Name-Email}"
 #      }
 
        #
index b4789c507275d8deaf2a5077189ad35a9aa2f030..038fd5285bf3301726737381b90e34d2a4d125fc 100644 (file)
@@ -225,7 +225,7 @@ recv DHCP-Request {
 
        if (ok) {
                update reply {
-                       &DHCP-Your-IP-Address := "%{%{request:DHCP-Requested-IP-Address}:-{request:DHCP-Client-IP-Address}}"
+                       &DHCP-Your-IP-Address := "%{%{request.DHCP-Requested-IP-Address}:-{request:DHCP-Client-IP-Address}}"
                }
        }
 
index b8028fdbaee8adaeae59e6af3df05d7125e9d948..74f51a1ac671e113fe4ff95b4c0e350ba352eb87 100644 (file)
@@ -466,7 +466,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                        /*
                         *      This allows efficient list comparisons later
                         */
-                       fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag);
+                       fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da);
                }
 
                /*
@@ -834,11 +834,11 @@ static int send_one_packet(rc_request_t *request)
                if (request->password) {
                        VALUE_PAIR *vp;
 
-                       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY)) != NULL) {
+                       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_user_password)) != NULL) {
                                fr_pair_value_strdup(vp, request->password->vp_strvalue);
 
                        } else if ((vp = fr_pair_find_by_da(request->packet->vps,
-                                                           attr_chap_password, TAG_ANY)) != NULL) {
+                                                           attr_chap_password)) != NULL) {
                                uint8_t buffer[17];
 
                                fr_radius_encode_chap_password(buffer, request->packet,
@@ -847,7 +847,7 @@ static int send_one_packet(rc_request_t *request)
                                                               request->password->vp_length);
                                fr_pair_value_memdup(vp, buffer, sizeof(buffer), false);
 
-                       } else if (fr_pair_find_by_da(request->packet->vps, attr_ms_chap_password, TAG_ANY) != NULL) {
+                       } else if (fr_pair_find_by_da(request->packet->vps, attr_ms_chap_password) != NULL) {
                                mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue);
 
                        } else {
@@ -1075,7 +1075,7 @@ static int recv_one_packet(fr_time_t wait_time)
        } else {
                VALUE_PAIR const *failed[2];
 
-               fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag);
+               fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da);
                if (fr_pair_validate(failed, request->filter, request->reply->vps)) {
                        RDEBUG("%s: Response passed filter", request->name);
                        stats.passed++;
index 8d038793f7461b3fe543eb26548e60d435e409b3..2b93547356993f64937cb71114f83d57b2121e06 100644 (file)
@@ -308,94 +308,72 @@ static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *h
                                UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response,
                                bool body)
 {
-       char const *status_str;
-       char buffer[2048];
-       char *p = buffer;
+       char const      *status_str;
+       char            buffer[1024];
+       fr_sbuff_t      sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
 
        char src[INET6_ADDRSTRLEN];
        char dst[INET6_ADDRSTRLEN];
 
-       ssize_t len, s = sizeof(buffer);
-
        inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.addr, src, sizeof(src));
        inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.addr, dst, sizeof(dst));
 
        status_str = fr_table_str_by_value(rs_events, status, NULL);
        RS_ASSERT(status_str);
 
-       len = snprintf(p, s, "%s,%" PRIu64 ",%s,", status_str, count, timestr);
-       p += len;
-       s -= len;
-
-       if (s <= 0) return;
+       if (fr_sbuff_in_sprintf(&sbuff, "%s,%" PRIu64 ",%s,", status_str, count, timestr) < 0) return;
 
        if (latency) {
-               len = snprintf(p, s, "%u.%03u,",
-                              (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
-               p += len;
-               s -= len;
+               if (fr_sbuff_in_sprintf(&sbuff, "%u.%03u,",
+                                       (unsigned int) latency->tv_sec,
+                                       ((unsigned int) latency->tv_usec / 1000)) < 0) return;
        } else {
-               *p = ',';
-               p += 1;
-               s -= 1;
+               if (fr_sbuff_in_char(&sbuff, ',') < 0) return;
        }
 
-       if (s <= 0) return;
-
        /* Status, Type, Interface, Src, Src port, Dst, Dst port, ID */
        if (is_radius_code(packet->code)) {
-               len = snprintf(p, s, "%s,%s,%s,%i,%s,%i,%i,", fr_packet_codes[packet->code], handle->name,
-                              src, packet->src_port, dst, packet->dst_port, packet->id);
+               if (fr_sbuff_in_sprintf(&sbuff, "%s,%s,%s,%i,%s,%i,%i,",
+                                       fr_packet_codes[packet->code], handle->name,
+                                       src, packet->src_port, dst, packet->dst_port, packet->id) < 0) return;
        } else {
-               len = snprintf(p, s, "%u,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
-                              src, packet->src_port, dst, packet->dst_port, packet->id);
+               if (fr_sbuff_in_sprintf(&sbuff, "%u,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
+                                       src, packet->src_port, dst, packet->dst_port, packet->id) < 0) return;
        }
-       p += len;
-       s -= len;
-
-       if (s <= 0) return;
 
        if (body) {
                int i;
                VALUE_PAIR *vp;
 
                for (i = 0; i < conf->list_da_num; i++) {
-                       vp = fr_pair_find_by_da(packet->vps, conf->list_da[i], TAG_ANY);
+                       vp = fr_pair_find_by_da(packet->vps, conf->list_da[i]);
                        if (vp && (vp->vp_length > 0)) {
                                if (conf->list_da[i]->type == FR_TYPE_STRING) {
-                                       *p++ = '"';
-                                       s--;
-                                       if (s <= 0) return;
-
-                                       len = rs_snprint_csv(p, s, vp->vp_strvalue, vp->vp_length);
-                                       p += len;
-                                       s -= len;
-                                       if (s <= 0) return;
-
-                                       *p++ = '"';
-                                       s--;
-                                       if (s <= 0) return;
+                                       ssize_t slen;
+
+                                       if (fr_sbuff_in_char(&sbuff, '"') < 0) return;
+
+                                       slen = rs_snprint_csv(fr_sbuff_current(&sbuff), fr_sbuff_remaining(&sbuff),
+                                                             vp->vp_strvalue, vp->vp_length);
+                                       if (slen < 0) return;
+                                       fr_sbuff_advance(&sbuff, (size_t)slen);
+
+                                       if (fr_sbuff_in_char(&sbuff, '"') < 0) return;
                                } else {
-                                       len = fr_pair_value_snprint(p, s, vp, 0);
-                                       p += len;
-                                       s -= len;
-                                       if (s <= 0) return;
+                                       if (fr_pair_print_value_quoted(&sbuff, vp, T_BARE_WORD) < 0) return;
                                }
                        }
 
-                       *p++ = ',';
-                       s -= 1;
-                       if (s <= 0) return;
+                       if (fr_sbuff_in_char(&sbuff, ',') < 0) return;
                }
        } else {
-               s -= conf->list_da_num;
-               if (s <= 0) return;
+               if (fr_sbuff_remaining(&sbuff) < (size_t)conf->list_da_num) return;
 
-               memset(p, ',', conf->list_da_num);
-               p += conf->list_da_num;
+               memset(fr_sbuff_current(&sbuff), ',', conf->list_da_num);
+               fr_sbuff_advance(&sbuff, conf->list_da_num);
+               *fr_sbuff_current(&sbuff) = '\0';
        }
 
-       *--p = '\0';
        fprintf(stdout , "%s\n", buffer);
 }
 
@@ -483,7 +461,7 @@ static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t
                        char vector[(RADIUS_AUTH_VECTOR_LENGTH * 2) + 1];
 
                        if (packet->vps) {
-                               fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
+                               fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da);
                                fr_pair_list_log(&default_log, packet->vps);
                        }
 
@@ -970,7 +948,7 @@ static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, fr_d
        fr_pair_cursor_init(&list_cursor, &last_match);
        fr_pair_cursor_init(&out_cursor, out);
        for (i = 0; i < num; i++) {
-               match = fr_pair_cursor_next_by_da(&list_cursor, da[i], TAG_ANY);
+               match = fr_pair_cursor_next_by_da(&list_cursor, da[i]);
                if (!match) {
                        fr_pair_cursor_init(&list_cursor, &last_match);
                        continue;
@@ -986,7 +964,7 @@ static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, fr_d
                        last_match = match;
 
                        count++;
-               } while ((match = fr_pair_cursor_next_by_da(&list_cursor, da[i], TAG_ANY)));
+               } while ((match = fr_pair_cursor_next_by_da(&list_cursor, da[i])));
        }
 
        return count;
@@ -1445,7 +1423,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                         *      Now verify the packet passes the attribute filter
                         */
                        if (conf->filter_response_vps) {
-                               fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
+                               fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da);
                                if (!fr_pair_validate_relaxed(NULL, conf->filter_response_vps, packet->vps)) {
                                        goto drop_response;
                                }
@@ -1570,7 +1548,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                                return;
                        }
 
-                       fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
+                       fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da);
                }
 
                /*
@@ -2032,7 +2010,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
        /*
         *      This allows efficient list comparisons later
         */
-       fr_pair_list_sort(out, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(out, fr_pair_cmp_by_da);
 
        return 0;
 }
index 56117977bd4a707b8304361a261e107a8675895a..cd3706af12b53401b63a5aaa0c388fc23b10e201 100644 (file)
@@ -494,7 +494,10 @@ static int radsnmp_get_response(int fd,
                        fr_strerror_printf("No %s found in response, or occurred out of order", type->name);
                        return -1;
                }
-               type_len = fr_pair_value_snprint(type_buff, sizeof(type_buff), type_vp, '\0');
+               slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(type_buff, sizeof(type_buff)), type_vp, T_BARE_WORD);
+               if (slen < 0) return -1;
+
+               type_len = (size_t)slen;
 
                /*
                 *      Build up the vector
@@ -523,17 +526,17 @@ static int radsnmp_get_response(int fd,
 
                default:
                        /*
-                        *      We call fr_value_box_snprint with a NULL da pointer
+                        *      We call fr_value_box_print with a NULL da pointer
                         *      because we always need return integer values not
                         *      value aliases.
                         */
-                       len = fr_value_box_snprint(value_buff, sizeof(value_buff), &vp->data, '\0');
-                       if (is_truncated(len, sizeof(value_buff))) {
+                       slen = fr_value_box_print(&FR_SBUFF_OUT(value_buff, sizeof(value_buff)), &vp->data, NULL);
+                       if (slen < 0) {
                                fr_strerror_printf("Insufficient fixed value buffer");
                                return -1;
                        }
                        io_vector[4].iov_base = value_buff;
-                       io_vector[4].iov_len = len;
+                       io_vector[4].iov_len = (size_t)slen;
                        break;
                }
                io_vector[5].iov_base = newline;
@@ -581,11 +584,11 @@ static int radsnmp_set_response(int fd, fr_dict_attr_t const *error, VALUE_PAIR
 {
        VALUE_PAIR      *vp;
        char            buffer[64];
-       size_t          len;
+       ssize_t         slen;
        struct iovec    io_vector[2];
        char            newline[] = "\n";
 
-       vp = fr_pair_find_by_da(head, error, TAG_NONE);
+       vp = fr_pair_find_by_da(head, error);
        if (!vp) {
                if (write(fd, "DONE\n", 5) < 0) {
                        fr_strerror_printf("Failed writing set response: %s", fr_syserror(errno));
@@ -594,14 +597,14 @@ static int radsnmp_set_response(int fd, fr_dict_attr_t const *error, VALUE_PAIR
                return 0;
        }
 
-       len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
-       if (is_truncated(len, sizeof(buffer))) {
+       slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vp, T_BARE_WORD);
+       if (slen < 0) {
                assert(0);
                return -1;
        }
 
        io_vector[0].iov_base = buffer;
-       io_vector[0].iov_len = len;
+       io_vector[0].iov_len = (size_t)slen;
        io_vector[1].iov_base = newline;
        io_vector[1].iov_len = 1;
 
index 36ea629b4d7331f791d95f601ea9861005ddd951..bb0792989fe2d5dab01aadb13b78c8b28a9b0a13 100644 (file)
@@ -75,7 +75,7 @@ do { \
                return (_len); \
        } while (0)
 
-#define RETURN_OK_WITH_ERROR(_len) \
+#define RETURN_OK_WITH_ERROR() \
        do { \
                result->rcode = RESULT_OK; \
                result->file = __FILE__; \
@@ -1010,21 +1010,21 @@ static size_t command_normalise_attribute(command_result_t *result, command_file
                                          char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
 {
        VALUE_PAIR      *head = NULL;
-       size_t          len;
+       size_t          slen;
 
        if (fr_pair_list_afrom_str(NULL, cc->active_dict ? cc->active_dict : cc->config->dict, in, &head) != T_EOL) {
                RETURN_OK_WITH_ERROR();
        }
 
-       len = fr_pair_snprint(data, COMMAND_OUTPUT_MAX, head);
+       slen = fr_pair_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), head);
        talloc_list_free(&head);
 
-       if (is_truncated(len, COMMAND_OUTPUT_MAX)) {
+       if (slen < 0) {
                fr_strerror_printf("Encoder output would overflow output buffer");
                RETURN_OK_WITH_ERROR();
        }
 
-       RETURN_OK(len);
+       RETURN_OK(slen);
 }
 
 /** Change the working directory
@@ -1168,7 +1168,6 @@ static size_t command_condition_normalise(command_result_t *result, command_file
                                          char *data, UNUSED size_t data_used, char *in, size_t inlen)
 {
        ssize_t                 dec_len;
-       char const              *error = NULL;
        fr_cond_t               *cond;
        CONF_SECTION            *cs;
        size_t                  len;
@@ -1181,9 +1180,10 @@ static size_t command_condition_normalise(command_result_t *result, command_file
        cf_filename_set(cs, cc->filename);
        cf_lineno_set(cs, cc->lineno);
 
-       dec_len = fr_cond_tokenize(cs, &cond, &error, cc->active_dict ? cc->active_dict : cc->config->dict, in, inlen);
+       dec_len = fr_cond_tokenize(cs, &cond,
+                                  cc->active_dict ? cc->active_dict : cc->config->dict, &FR_SBUFF_IN(in, inlen));
        if (dec_len <= 0) {
-               fr_strerror_printf("ERROR offset %d %s", (int) -dec_len, error);
+               fr_strerror_printf("ERROR offset %d %s", (int) -dec_len, fr_strerror());
 
        return_error:
                talloc_free(cs);
@@ -1192,8 +1192,8 @@ static size_t command_condition_normalise(command_result_t *result, command_file
 
        if (fr_debug_lvl > DEBUG_ENABLED3) {
                if (cond->type == COND_TYPE_MAP) {
-                       if (cond->data.map->lhs) tmpl_attr_debug(cond->data.map->lhs);
-                       if (cond->data.map->rhs) tmpl_attr_debug(cond->data.map->rhs);
+                       if (cond->data.map->lhs) tmpl_debug(cond->data.map->lhs);
+                       if (cond->data.map->rhs) tmpl_debug(cond->data.map->rhs);
                }
        }
 
@@ -1203,7 +1203,7 @@ static size_t command_condition_normalise(command_result_t *result, command_file
                goto return_error;
        }
 
-       len = cond_snprint(NULL, data, COMMAND_OUTPUT_MAX, cond);
+       len = cond_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), cond);
        talloc_free(cs);
 
        RETURN_OK(len);
@@ -1312,21 +1312,18 @@ static size_t command_decode_pair(command_result_t *result, command_file_ctx_t *
                for (vp = fr_cursor_head(&cursor);
                     vp;
                     vp = fr_cursor_next(&cursor)) {
-                       size_t len;
-
-                       len = fr_pair_snprint(p, end - p, vp);
-                       if (is_truncated(len, end - p)) {
+                       if ((slen = fr_pair_print(&FR_SBUFF_OUT(p, end), vp)) < 0) {
                        oob:
                                fr_strerror_printf("Out of output buffer space");
                                CLEAR_TEST_POINT(cc);
                                RETURN_COMMAND_ERROR();
                        }
-                       p += len;
+                       p += slen;
 
                        if (vp->next) {
-                               len = strlcpy(p, ", ", end - p);
-                               if (is_truncated(len, end - p)) goto oob;
-                               p += len;
+                               slen = strlcpy(p, ", ", end - p);
+                               if (is_truncated((size_t)slen, end - p)) goto oob;
+                               p += slen;
                        }
                }
                fr_pair_list_free(&head);
@@ -1416,21 +1413,18 @@ static size_t command_decode_proto(command_result_t *result, command_file_ctx_t
                for (vp = fr_cursor_head(&cursor);
                     vp;
                     vp = fr_cursor_next(&cursor)) {
-                       size_t len;
-
-                       len = fr_pair_snprint(p, end - p, vp);
-                       if (is_truncated(len, end - p)) {
+                       if ((slen = fr_pair_print(&FR_SBUFF_OUT(p, end), vp)) < 0) {
                        oob:
                                fr_strerror_printf("Out of output buffer space");
                                CLEAR_TEST_POINT(cc);
                                RETURN_COMMAND_ERROR();
                        }
-                       p += len;
+                       p += slen;
 
                        if (vp->next) {
-                               len = strlcpy(p, ", ", end - p);
-                               if (is_truncated(len, end - p)) goto oob;
-                               p += len;
+                               slen = strlcpy(p, ", ", end - p);
+                               if (is_truncated((size_t)slen, end - p)) goto oob;
+                               p += slen;
                        }
                }
                fr_pair_list_free(&head);
@@ -1518,7 +1512,6 @@ static size_t command_encode_dns_label(command_result_t *result, command_file_ct
 static size_t command_decode_dns_label(command_result_t *result, command_file_ctx_t *cc,
                                       char *data, UNUSED size_t data_used, char *in, size_t inlen)
 {
-       size_t len;
        ssize_t slen, total, i;
        char *out, *end;
        fr_value_box_t *box = talloc_zero(NULL, fr_value_box_t);
@@ -1535,6 +1528,7 @@ static size_t command_decode_dns_label(command_result_t *result, command_file_ct
        for (i = 0; i < total; i += slen) {
                slen = fr_dns_label_to_value_box(box, box, cc->buffer_start, total, cc->buffer_start + i, false);
                if (slen <= 0) {
+               error:
                        talloc_free(box);
                        RETURN_OK_WITH_ERROR();
                }
@@ -1547,8 +1541,9 @@ static size_t command_decode_dns_label(command_result_t *result, command_file_ct
                /*
                 *      We don't print it with quotes.
                 */
-               len = fr_value_box_snprint(out, end - out, box, '\0');
-               out += len;
+               slen = fr_value_box_print(&FR_SBUFF_OUT(out, end - out), box, NULL);
+               if (slen <= 0) goto error;
+               out += slen;
 
                fr_value_box_clear(box);
        }
@@ -1973,10 +1968,8 @@ static size_t command_pair(command_result_t *result, command_file_ctx_t *cc,
        for (vp = fr_cursor_init(&cursor, &head);
             vp != NULL;
             vp = fr_cursor_next(&cursor)) {
-               size_t len;
-
-               len = fr_pair_snprint(p, end - p, vp);
-               p += len;
+               if ((slen = fr_pair_print(&FR_SBUFF_OUT(p, end), vp)) <= 0) RETURN_OK_WITH_ERROR();
+               p += (size_t)slen;
 
                if (p >= end) break;
 
@@ -1987,9 +1980,7 @@ static size_t command_pair(command_result_t *result, command_file_ctx_t *cc,
        /*
         *      Delete the trailing ", ".
         */
-       if (p > data) {
-               p -= 2;
-       }
+       if (p > data) p -= 2;
        *p = 0;
 
        RETURN_OK(p - data);
@@ -2054,7 +2045,7 @@ static size_t command_value_box_normalise(command_result_t *result, UNUSED comma
        fr_value_box_t *box2;
        fr_type_t       type;
        size_t          match_len;
-       size_t          len;
+       ssize_t         slen;
        char            *p;
 
        /*
@@ -2068,6 +2059,7 @@ static size_t command_value_box_normalise(command_result_t *result, UNUSED comma
        fr_skip_whitespace(p);
 
        if (fr_value_box_from_str(box, box, &type, NULL, p, -1, '"', false) < 0) {
+       error:
                talloc_free(box);
                RETURN_OK_WITH_ERROR();
        }
@@ -2076,10 +2068,12 @@ static size_t command_value_box_normalise(command_result_t *result, UNUSED comma
         *      Don't print dates with enclosing quotation marks.
         */
        if (type != FR_TYPE_DATE) {
-               len = fr_value_box_snprint(data, COMMAND_OUTPUT_MAX, box, '"');
+               slen = fr_value_box_print_quoted(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), box,
+                                                T_DOUBLE_QUOTED_STRING);
        } else {
-               len = fr_value_box_snprint(data, COMMAND_OUTPUT_MAX, box, '\0');
+               slen = fr_value_box_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), box, NULL);
        }
+       if (slen <= 0) goto error;
 
        /*
         *      Behind the scenes, parse the data
@@ -2087,7 +2081,7 @@ static size_t command_value_box_normalise(command_result_t *result, UNUSED comma
         *      box as last time.
         */
        box2 = talloc_zero(NULL, fr_value_box_t);
-       if (fr_value_box_from_str(box2, box2, &type, NULL, data, len, '"', false) < 0) {
+       if (fr_value_box_from_str(box2, box2, &type, NULL, data, slen, '"', false) < 0) {
                talloc_free(box2);
                talloc_free(box);
                RETURN_OK_WITH_ERROR();
@@ -2107,7 +2101,7 @@ static size_t command_value_box_normalise(command_result_t *result, UNUSED comma
 
        talloc_free(box2);
        talloc_free(box);
-       RETURN_OK(len);
+       RETURN_OK(slen);
 }
 
 static size_t command_write(command_result_t *result, command_file_ctx_t *cc,
@@ -2143,37 +2137,28 @@ static size_t command_write(command_result_t *result, command_file_ctx_t *cc,
 static size_t command_xlat_normalise(command_result_t *result, command_file_ctx_t *cc,
                                     char *data, UNUSED size_t data_used, char *in, UNUSED size_t inlen)
 {
-       ssize_t         dec_len;
-       size_t          len;
-       char            *fmt;
-       xlat_exp_t      *head = NULL;
-       size_t          input_len = strlen(in), escaped_len;
-       char            buff[1024];
+       ssize_t                 dec_len;
+       xlat_exp_t              *head = NULL;
+       size_t                  input_len = strlen(in), escaped_len;
+       fr_sbuff_parse_rules_t  p_rules = { .escapes = &fr_value_unescape_double };
 
-       /*
-        *      Process special chars, octal escape sequences and hex sequences
-        */
-       len = fr_value_str_aunescape(NULL, &fmt, &FR_SBUFF_IN(in, input_len), SIZE_MAX, '\"');
-       fr_assert(fmt);
-       dec_len = xlat_tokenize(fmt, &head, fmt, len,
+       dec_len = xlat_tokenize(NULL, &head, NULL, &FR_SBUFF_IN(in, input_len), &p_rules,
                                &(tmpl_rules_t) { .dict_def = cc->active_dict ? cc->active_dict : cc->config->dict });
        if (dec_len <= 0) {
                fr_strerror_printf_push("ERROR offset %d", (int) -dec_len);
 
        return_error:
-               talloc_free(fmt);
+               TALLOC_FREE(head);
                RETURN_OK_WITH_ERROR();
        }
 
-       if (((size_t) dec_len != len) || (fmt[dec_len] != '\0')) {
+       if (((size_t) dec_len != input_len)) {
                fr_strerror_printf_push("offset %d 'Too much text'", (int) dec_len);
                goto return_error;
        }
 
-       len = xlat_snprint(buff, sizeof(buff), head);
-       escaped_len = fr_snprint(data, COMMAND_OUTPUT_MAX, buff, len, '"');
-       talloc_free(fmt);
-
+       escaped_len = xlat_print(&FR_SBUFF_OUT(data, COMMAND_OUTPUT_MAX), head, &fr_value_escape_double);
+       TALLOC_FREE(head);
        RETURN_OK(escaped_len);
 }
 
@@ -2192,7 +2177,8 @@ static size_t command_xlat_argv(command_result_t *result, command_file_ctx_t *cc
        size_t          input_len = strlen(in);
        char            buff[1024];
 
-       slen = xlat_tokenize_argv(cc->tmp_ctx, &head, in, input_len,
+       slen = xlat_tokenize_argv(cc->tmp_ctx, &head, NULL, &FR_SBUFF_IN(in, input_len),
+                                 NULL,
                                  &(tmpl_rules_t) { .dict_def = cc->active_dict ? cc->active_dict : cc->config->dict });
        if (slen <= 0) {
                fr_strerror_printf_push("ERROR offset %d", (int) -slen);
@@ -2206,7 +2192,7 @@ static size_t command_xlat_argv(command_result_t *result, command_file_ctx_t *cc
        }
 
        for (i = 0, p = data; i < argc; i++) {
-               (void)  xlat_snprint(buff, sizeof(buff), argv[i]);
+               (void)  xlat_print(&FR_SBUFF_OUT(buff, sizeof(buff)), argv[i], NULL);
 
                len = snprintf(p, data + COMMAND_OUTPUT_MAX - p, "[%d]{ %s }, ", i, buff);
                p += len;
@@ -2781,7 +2767,7 @@ static int line_ranges_parse(TALLOC_CTX *ctx, fr_dlist_head_t *out, fr_sbuff_t *
                         *      A bare '-' with no number means
                         *      run all remaining lines.
                         */
-                       if (!fr_sbuff_extend(in)) {
+                       if (fr_sbuff_extend(in) == 0) {
                                lr->end = UINT32_MAX;
                                return 0;
                        }
index a40204123d4a4ba402c56b6e87d861b4aae961a1..ab93dfed8ffbd89af3085abfd076a7571cc4f0ea 100644 (file)
@@ -140,7 +140,7 @@ static int process_file(char const *filename)
        }
 
        for (map = head; map != NULL; map = map->next) {
-               map_snprint(NULL, buffer + 1, sizeof(buffer) - 1, map);
+               map_print(&FR_SBUFF_OUT(buffer + 1, sizeof(buffer) - 1), map);
                puts(buffer);
        }
        printf("}\n");
index 1164014838b8b10615facb28ca3d9b4093243e2d..b05996e6509885cc866c7fb812b2c9c13d410145 100644 (file)
@@ -30,6 +30,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/module.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/rand.h>
+#include <freeradius-devel/util/value.h>
 
 #include <freeradius-devel/tls/base.h>
 
@@ -380,20 +381,23 @@ static bool do_xlats(char const *filename, FILE *fp)
                 *      Look for "xlat"
                 */
                if (strncmp(input, "xlat ", 5) == 0) {
-                       ssize_t slen;
-                       char *fmt = talloc_typed_strdup(NULL, input + 5);
-                       xlat_exp_t *head;
-
-                       slen = xlat_tokenize_ephemeral(fmt, &head, request, fmt, NULL);
+                       ssize_t                 slen;
+                       TALLOC_CTX              *xlat_ctx = talloc_init_const("xlat");
+                       char                    *fmt = talloc_typed_strdup(xlat_ctx, input + 5);
+                       xlat_exp_t              *head = NULL;
+                       fr_sbuff_parse_rules_t  p_rules = { .escapes = &fr_value_unescape_double };
+
+                       slen = xlat_tokenize_ephemeral(xlat_ctx, &head, NULL, request,
+                                                      &FR_SBUFF_IN(fmt, talloc_array_length(fmt) - 1), &p_rules, NULL);
                        if (slen <= 0) {
-                               talloc_free(fmt);
+                               talloc_free(xlat_ctx);
                                snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen,
                                         fr_strerror());
                                continue;
                        }
 
                        if (input[slen + 5] != '\0') {
-                               talloc_free(fmt);
+                               talloc_free(xlat_ctx);
                                snprintf(output, sizeof(output), "ERROR offset %d 'Too much text' ::%s::",
                                         (int) slen, input + slen + 5);
                                continue;
@@ -401,11 +405,12 @@ static bool do_xlats(char const *filename, FILE *fp)
 
                        len = xlat_eval_compiled(output, sizeof(output), request, head, NULL, NULL);
                        if (len < 0) {
+                               talloc_free(xlat_ctx);
                                snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror());
                                continue;
                        }
 
-                       TALLOC_FREE(fmt); /* also frees 'head' */
+                       TALLOC_FREE(xlat_ctx); /* also frees 'head' */
                        continue;
                }
 
@@ -796,6 +801,11 @@ int main(int argc, char *argv[])
                EXIT_WITH_FAILURE;
        }
 
+       /*
+        *      Initialise the interpreter, registering operations.
+        */
+       if (unlang_init() < 0) return -1;
+
        /*  Read the configuration files, BEFORE doing anything else.  */
        if (main_config_init(config) < 0) {
                EXIT_WITH_FAILURE;
@@ -821,11 +831,6 @@ int main(int argc, char *argv[])
                client_add(NULL, client);
        }
 
-       /*
-        *      Initialise the interpreter, registering operations.
-        */
-       if (unlang_init() < 0) return -1;
-
        if (server_init(config->root_cs) < 0) EXIT_WITH_FAILURE;
 
        if (!virtual_server_find("default")) {
index f62b5a916eac1a5cd5e20e66deab4a14b5dac17f..c39651f71e4d52aefb69645fe54c1ed9f8eff8e9 100644 (file)
@@ -406,7 +406,7 @@ rlm_rcode_t eap_virtual_server(REQUEST *request, eap_session_t *eap_session, cha
        rlm_rcode_t     rcode;
        VALUE_PAIR      *vp;
 
-       vp = fr_pair_find_by_da(request->control, attr_virtual_server, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_virtual_server);
        request->server_cs = vp ? virtual_server_find(vp->vp_strvalue) : virtual_server_find(virtual_server);
 
        if (request->server_cs) {
index 66a03a933ffaa9a4ca69f5fc222e1cd4fd47f354..121ad73692d8cd6662d05814538e9191263b18c0 100644 (file)
@@ -65,7 +65,7 @@ static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
         *      Set the response code.  Default to "fail" if none was
         *      specified.
         */
-       vp = fr_pair_find_by_da(request->control, attr_chbind_response_code, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_chbind_response_code);
        if (vp) {
                ptr[0] = vp->vp_uint32;
        } else {
index 2440d71aea0b02daa04a630a006b734c5441bd4f..cc45b83e85996bf77a3003f29f696e224a0d74ff 100644 (file)
@@ -239,7 +239,7 @@ rlm_rcode_t eap_compose(eap_session_t *eap_session)
         *      Don't add a Message-Authenticator if
         *      it's already there.
         */
-       vp = fr_pair_find_by_da(request->reply->vps, attr_message_authenticator, TAG_ANY);
+       vp = fr_pair_find_by_da(request->reply->vps, attr_message_authenticator);
        if (!vp) {
                static uint8_t auth_vector[RADIUS_AUTH_VECTOR_LENGTH] = { 0x00 };
 
@@ -302,7 +302,7 @@ int eap_start(REQUEST *request, rlm_eap_method_t const methods[], bool ignore_un
        VALUE_PAIR *vp;
        VALUE_PAIR *eap_msg;
 
-       eap_msg = fr_pair_find_by_da(request->packet->vps, attr_eap_message, TAG_ANY);
+       eap_msg = fr_pair_find_by_da(request->packet->vps, attr_eap_message);
        if (!eap_msg) {
                RDEBUG2("No EAP-Message, not doing EAP");
                return RLM_MODULE_NOOP;
@@ -312,7 +312,7 @@ int eap_start(REQUEST *request, rlm_eap_method_t const methods[], bool ignore_un
         *      Look for EAP-Type = None (FreeRADIUS specific attribute)
         *      this allows you to NOT do EAP for some users.
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_type);
        if (vp && vp->vp_uint32 == 0) {
                RDEBUG2("Found EAP-Message, but EAP-Type = None, so we're not doing EAP");
                return RLM_MODULE_NOOP;
index 97a71b027e9f93361c9e70fb8257b4632ad89899..f64b63486e9745d2c7bad33349456b709de2dbd6 100644 (file)
@@ -420,7 +420,7 @@ eap_session_t *eap_session_continue(void const *instance, eap_packet_raw_t **eap
         *      Type-Data field of the EAP-Response/Identity in the User-Name
         *      attribute in every subsequent Access-Request.
         */
-       user = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       user = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!user) {
                /*
                 *      NAS did not set the User-Name
index 562d8b5ad738928a3c61b56d5ec4a5f399de4ab1..e23ff855dcbeeb956077df95e1c92c0bc9da8ee1 100644 (file)
@@ -955,7 +955,7 @@ ssize_t fr_aka_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_
         *      It might be too big for putting into an
         *      EAP packet.
         */
-       vp = fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_SUBTYPE, TAG_ANY);
+       vp = fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_SUBTYPE);
        if (!vp) {
                REDEBUG("Missing subtype attribute");
                return PAIR_ENCODE_FATAL_ERROR;
@@ -965,13 +965,13 @@ ssize_t fr_aka_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_
        /*
         *      Group attributes with similar lineages together
         */
-       fr_pair_list_sort(&to_encode, fr_pair_cmp_by_parent_num_tag);
+       fr_pair_list_sort(&to_encode, fr_pair_cmp_by_parent_num);
        if (fr_cursor_init(&cursor, &to_encode) == vp) fr_cursor_next(&cursor); /* Skip subtype if it came out first */
 
        /*
         *      Will we need to generate a HMAC?
         */
-       if (fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_MAC, TAG_ANY)) do_hmac = true;
+       if (fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_MAC)) do_hmac = true;
 
        /*
         *      Fast path, we just need to encode a subtype
index db834ff3775da489d55137f0a2c6307de72d7a5e..c84b5324694f59179e654851a2a3707b74d55812 100644 (file)
@@ -274,7 +274,7 @@ static inline void client_error_debug(REQUEST *request, VALUE_PAIR *from_peer)
 {
        VALUE_PAIR *vp;
 
-       vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_client_error_code, TAG_ANY);
+       vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_client_error_code);
        if (!vp) {
                REDEBUG("Peer has not supplied a AT_ERROR_CODE");
        } else {
@@ -516,7 +516,7 @@ static rlm_rcode_t pseudonym_store_resume(module_ctx_t const *mctx, REQUEST *req
 
        pair_delete_request(attr_eap_aka_sim_next_pseudonym);
 
-       vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_reauth_id, TAG_ANY);
+       vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_reauth_id);
        if (vp) {
                /*
                 *      Generate a random fastauth string
@@ -590,7 +590,7 @@ static rlm_rcode_t pseudonym_store_resume(module_ctx_t const *mctx, REQUEST *req
                 *      state increment by 1, otherwise, add the
                 *      attribute and set to zero.
                 */
-               vp = fr_pair_find_by_da(request->state, attr_eap_aka_sim_counter, TAG_ANY);
+               vp = fr_pair_find_by_da(request->state, attr_eap_aka_sim_counter);
                if (vp) {
                        vp->vp_uint16++;
                /*
@@ -642,7 +642,7 @@ static rlm_rcode_t session_and_pseudonym_store(module_ctx_t const *mctx,
 
        request->rcode = RLM_MODULE_NOOP;       /* Needed because we may call resume functions directly */
 
-       vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_pseudonym, TAG_ANY);
+       vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_pseudonym);
        if (vp) {
                /*
                 *      Generate a random pseudonym string
@@ -931,7 +931,7 @@ static rlm_rcode_t common_failure_notification_send(module_ctx_t const *mctx,
         *      - FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED
         *        User has not subscribed to the requested service.
         */
-       notification_vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_notification, TAG_ANY);
+       notification_vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_notification);
 
        /*
         *      Change the failure notification depending where
@@ -1135,7 +1135,7 @@ static rlm_rcode_t common_reauthentication_request_compose(module_ctx_t const *m
         *      Not seen any doing this for re-authentication
         *      but you never know...
         */
-       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity);
        if (kdf_id) {
                identity_to_crypto_identity(request, eap_aka_sim_session,
                                            (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
@@ -1216,7 +1216,7 @@ static rlm_rcode_t common_reauthentication_request_compose(module_ctx_t const *m
         *
         *      Use our default, but allow user override too.
         */
-       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind);
        if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
 
        /*
@@ -1322,7 +1322,7 @@ static rlm_rcode_t aka_challenge_request_compose(module_ctx_t const *mctx,
         *      implement RFC 4187 correctly and use the
         *      wrong identity as input the the PRF/KDF.
         */
-       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity);
        if (kdf_id) {
                identity_to_crypto_identity(request, eap_aka_sim_session,
                                            (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
@@ -1336,7 +1336,7 @@ static rlm_rcode_t aka_challenge_request_compose(module_ctx_t const *mctx,
                 *      Copy the network name the user specified for
                 *      key derivation purposes.
                 */
-               vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_kdf_input, TAG_ANY);
+               vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_kdf_input);
                if (vp) {
                        talloc_free(eap_aka_sim_session->keys.network);
                        eap_aka_sim_session->keys.network = talloc_memdup(eap_aka_sim_session,
@@ -1405,7 +1405,7 @@ static rlm_rcode_t aka_challenge_request_compose(module_ctx_t const *mctx,
         *
         *      Use our default, but allow user override too.
         */
-       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind);
        if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
 
        /*
@@ -1413,7 +1413,7 @@ static rlm_rcode_t aka_challenge_request_compose(module_ctx_t const *mctx,
         *      If so include AT_BIDDING with the correct
         *      value.
         */
-       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_bidding, TAG_ANY);
+       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_bidding);
        if (vp) {
                eap_aka_sim_session->send_at_bidding_prefer_prime =
                        (vp->vp_uint16 == FR_BIDDING_VALUE_PREFER_AKA_PRIME);
@@ -1525,7 +1525,7 @@ static rlm_rcode_t sim_challenge_request_compose(module_ctx_t const *mctx,
         *      implement RFC 4187 correctly and use the
         *      wrong identity as input the the PRF/KDF.
         */
-       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+       kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity);
        if (kdf_id) {
                identity_to_crypto_identity(request, eap_aka_sim_session,
                                            (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
@@ -1555,7 +1555,7 @@ static rlm_rcode_t sim_challenge_request_compose(module_ctx_t const *mctx,
         *
         *      Use our default, but allow user override too.
         */
-       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+       vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind);
        if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
 
        /*
@@ -1712,7 +1712,7 @@ static rlm_rcode_t sim_start_request_send(module_ctx_t const *mctx,
         *      If the user provided no versions, then
         *      just add the default (1).
         */
-       if (!(fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_version_list, TAG_ANY))) {
+       if (!(fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_version_list))) {
                MEM(pair_add_reply(&vp, attr_eap_aka_sim_version_list) >= 0);
                vp->vp_uint16 = EAP_SIM_VERSION;
        }
@@ -2273,7 +2273,7 @@ static rlm_rcode_t aka_challenge_enter(module_ctx_t const *mctx,
                 *      and send it to the peer.
                 */
                if (inst->network_name &&
-                   !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_kdf_input, TAG_ANY)) {
+                   !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_kdf_input)) {
                        MEM(pair_add_reply(&vp, attr_eap_aka_sim_kdf_input) >= 0);
                        fr_pair_value_bstrdup_buffer(vp, inst->network_name, false);
                }
@@ -2285,7 +2285,7 @@ static rlm_rcode_t aka_challenge_enter(module_ctx_t const *mctx,
                 *      Use the default bidding value we have configured
                 */
                if (eap_aka_sim_session->send_at_bidding_prefer_prime &&
-                   !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_bidding, TAG_ANY)) {
+                   !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_bidding)) {
                        MEM(pair_add_reply(&vp, attr_eap_aka_sim_bidding) >= 0);
                        vp->vp_uint16 = FR_BIDDING_VALUE_PREFER_AKA_PRIME;
                }
@@ -2297,7 +2297,7 @@ static rlm_rcode_t aka_challenge_enter(module_ctx_t const *mctx,
         *      Set the defaults for protected result indicator
         */
        if (eap_aka_sim_session->send_result_ind &&
-           !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+           !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind)) {
                MEM(pair_add_reply(&vp, attr_eap_aka_sim_result_ind) >= 0);
                vp->vp_bool = true;
        }
@@ -2350,7 +2350,7 @@ static rlm_rcode_t sim_challenge_enter(module_ctx_t const *mctx,
         *      Set the defaults for protected result indicator
         */
        if (eap_aka_sim_session->send_result_ind &&
-           !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+           !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind)) {
                MEM(pair_add_reply(&vp, attr_eap_aka_sim_result_ind) >= 0);
                vp->vp_bool = true;
        }
@@ -2498,7 +2498,7 @@ static rlm_rcode_t common_reauthentication_response_process(module_ctx_t const *
        VALUE_PAIR              *mac, *checkcode;
        VALUE_PAIR              *from_peer = request->packet->vps;
 
-       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac);
        if (!mac) {
                REDEBUG("Missing AT_MAC attribute");
        failure:
@@ -2537,7 +2537,7 @@ static rlm_rcode_t common_reauthentication_response_process(module_ctx_t const *
         *      means they don't support it, and we can't validate
         *      their view of the identity packets.
         */
-       checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode, TAG_ANY);
+       checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode);
        if (checkcode) {
                if (checkcode->vp_length != eap_aka_sim_session->checkcode_len) {
                        REDEBUG("Received checkcode's length (%zu) does not match calculated checkcode's length (%zu)",
@@ -2568,7 +2568,7 @@ static rlm_rcode_t common_reauthentication_response_process(module_ctx_t const *
         *      clear out reauth information and enter the
         *      challenge state.
         */
-       if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_counter_too_small, TAG_ANY)) {
+       if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_counter_too_small)) {
                RWDEBUG("Peer sent AT_COUNTER_TOO_SMALL (indicating our AT_COUNTER value (%u) wasn't fresh)",
                        eap_aka_sim_session->keys.reauth.counter);
 
@@ -2586,13 +2586,13 @@ static rlm_rcode_t common_reauthentication_response_process(module_ctx_t const *
         *      RFC 4187 Section #6.2. Result Indications
         */
        if (eap_aka_sim_session->send_result_ind) {
-               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                        RDEBUG("We wanted to use protected result indications, but peer does not");
                        eap_aka_sim_session->send_result_ind = false;
                } else {
                        return common_success_notification_enter(mctx, request, eap_session);
                }
-       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                RDEBUG("Peer wanted to use protected result indications, but we do not");
        }
 
@@ -2616,7 +2616,7 @@ static rlm_rcode_t aka_challenge_response_process(module_ctx_t const *mctx,
        VALUE_PAIR              *vp = NULL, *mac, *checkcode;
        VALUE_PAIR              *from_peer = request->packet->vps;
 
-       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac);
        if (!mac) {
                REDEBUG("Missing AT_MAC attribute");
        failure:
@@ -2655,7 +2655,7 @@ static rlm_rcode_t aka_challenge_response_process(module_ctx_t const *mctx,
         *      their view of the identity packets.
         */
        if (eap_aka_sim_session->checkcode_md) {
-               checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode, TAG_ANY);
+               checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode);
                if (checkcode) {
                        if (checkcode->vp_length != eap_aka_sim_session->checkcode_len) {
                                REDEBUG("Received checkcode's length (%zu) does not match "
@@ -2682,7 +2682,7 @@ static rlm_rcode_t aka_challenge_response_process(module_ctx_t const *mctx,
                }
        }
 
-       vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_res, TAG_ANY);
+       vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_res);
        if (!vp) {
                REDEBUG("AT_RES missing from challenge response");
                goto failure;
@@ -2714,13 +2714,13 @@ static rlm_rcode_t aka_challenge_response_process(module_ctx_t const *mctx,
         *      RFC 4187 Section #6.2. Result Indications
         */
        if (eap_aka_sim_session->send_result_ind) {
-               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                        RDEBUG("We wanted to use protected result indications, but peer does not");
                        eap_aka_sim_session->send_result_ind = false;
                } else {
                        return common_success_notification_enter(mctx, request, eap_session);
                }
-       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                RDEBUG("Peer wanted to use protected result indications, but we do not");
        }
 
@@ -2751,7 +2751,7 @@ static rlm_rcode_t sim_challenge_response_process(module_ctx_t const *mctx,
        p += AKA_SIM_VECTOR_GSM_SRES_SIZE;
        memcpy(p, eap_aka_sim_session->keys.gsm.vector[2].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
 
-       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+       mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac);
        if (!mac) {
                REDEBUG("Missing AT_MAC attribute");
        failure:
@@ -2792,13 +2792,13 @@ static rlm_rcode_t sim_challenge_response_process(module_ctx_t const *mctx,
         *      notification, otherwise send a normal EAP-Success.
         */
        if (eap_aka_sim_session->send_result_ind) {
-               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+               if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                        RDEBUG("We wanted to use protected result indications, but peer does not");
                        eap_aka_sim_session->send_result_ind = false;
                } else {
                        return common_success_notification_enter(mctx, request, eap_session);
                }
-       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+       } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind)) {
                RDEBUG("Peer wanted to use protected result indications, but we do not");
        }
 
@@ -2875,7 +2875,7 @@ static rlm_rcode_t aka_identity_response_process(module_ctx_t const *mctx,
         *      If the identity looks like a fast re-auth id
         *      run fast re-auth, otherwise do fullauth.
         */
-       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type);
        if (identity_type) switch (identity_type->vp_uint32) {
        case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
                return common_reauthentication_enter(mctx, request, eap_session);
@@ -2912,7 +2912,7 @@ static int sim_start_selected_version_check(REQUEST *request, VALUE_PAIR *from_p
        /*
         *      Check that we got an AT_SELECTED_VERSION
         */
-       selected_version_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version, TAG_ANY);
+       selected_version_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version);
        if (!selected_version_vp) {
                REDEBUG("EAP-Response/SIM/Start does not contain AT_SELECTED_VERSION");
                return -1;
@@ -2966,7 +2966,7 @@ static int sim_start_nonce_mt_check(REQUEST *request, VALUE_PAIR *from_peer,
        /*
         *      Copy nonce_mt to the keying material
         */
-       nonce_mt_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt, TAG_ANY);
+       nonce_mt_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt);
        if (!nonce_mt_vp) {
                REDEBUG("EAP-Response/SIM/Start does not contain AT_NONCE_MT");
                return -1;
@@ -3045,7 +3045,7 @@ static rlm_rcode_t sim_start_response_process(module_ctx_t const *mctx,
         *      If the identity looks like a fast re-auth id
         *      run fast re-auth, otherwise do fullauth.
         */
-       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type);
        if (identity_type) switch (identity_type->vp_uint32) {
        case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
                /*
@@ -3055,7 +3055,7 @@ static rlm_rcode_t sim_start_response_process(module_ctx_t const *mctx,
                 *  with a fast re-authentication identity is present for fast
                 *  re-authentication
                 */
-               if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt, TAG_ANY)) {
+               if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt)) {
                        REDEBUG("AT_NONCE_MT is not allowed in EAP-Response/SIM-Reauthentication messages");
                        return common_failure_notification_enter(mctx, request, eap_session);
                }
@@ -3067,7 +3067,7 @@ static rlm_rcode_t sim_start_response_process(module_ctx_t const *mctx,
                 *  AT_IDENTITY attribute with a fast re-authentication identity is
                 *  present for fast re-authentication.
                 */
-               if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version, TAG_ANY)) {
+               if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version)) {
                        REDEBUG("AT_SELECTED_VERSION is not allowed in EAP-Response/SIM-Reauthentication messages");
                        return common_failure_notification_enter(mctx, request, eap_session);
                }
@@ -3267,7 +3267,7 @@ static rlm_rcode_t aka_synchronization_failure_recv_resume(module_ctx_t const *m
         *      We couldn't generate an SQN and the user didn't provide one,
         *      so we need to fail.
         */
-       vp = fr_pair_find_by_da(request->control, attr_sim_sqn, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_sim_sqn);
        if (!vp) {
                REDEBUG("No &control:SQN value provided after resynchronisation, cannot continue");
                goto failure;
@@ -3361,7 +3361,7 @@ static rlm_rcode_t common_decode(VALUE_PAIR **subtype_vp, VALUE_PAIR **vps,
                log_request_pair_list(L_DBG_LVL_2, request, aka_vps, NULL);
        }
 
-       *subtype_vp = fr_pair_find_by_da(aka_vps, attr_eap_aka_sim_subtype, TAG_ANY);
+       *subtype_vp = fr_pair_find_by_da(aka_vps, attr_eap_aka_sim_subtype);
        if (!*subtype_vp) {
                REDEBUG("Missing AT_SUBTYPE");
                goto failure;
@@ -3577,7 +3577,7 @@ static rlm_rcode_t aka_challenge(module_ctx_t const *mctx, REQUEST *request)
 
                eap_aka_sim_session->allow_encrypted = false;
 
-               vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_auts, TAG_ANY);
+               vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_auts);
                if (!vp) {
                        REDEBUG("EAP-Response/AKA-Synchronisation-Failure missing AT_AUTS");
                failure:
@@ -3746,7 +3746,7 @@ static rlm_rcode_t aka_identity(module_ctx_t const *mctx, REQUEST *request)
                VALUE_PAIR              *id;
                fr_aka_sim_id_type_t    type;
 
-               id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity, TAG_ANY);
+               id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity);
                if (!id) {
                        /*
                         *  9.2.  EAP-Response/Identity
@@ -3851,7 +3851,7 @@ static rlm_rcode_t sim_start(module_ctx_t const *mctx, REQUEST *request)
                VALUE_PAIR              *id;
                fr_aka_sim_id_type_t    type;
 
-               id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity, TAG_ANY);
+               id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity);
                if (!id) {
                        /*
                         *  RFC 4186 Section #9.2
@@ -3946,10 +3946,10 @@ static rlm_rcode_t common_eap_identity_resume(module_ctx_t const *mctx, REQUEST
         *      This must be done before we enter
         *      the submodule.
         */
-       eap_type = fr_pair_find_by_da(request->control, attr_eap_type, TAG_ANY);
+       eap_type = fr_pair_find_by_da(request->control, attr_eap_type);
        if (eap_type) RWDEBUG("Ignoring &control:EAP-Type, this must be set *before* the EAP module is called");
 
-       method = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_method_hint, TAG_ANY);
+       method = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_method_hint);
 
        /*
         *      Set default configuration, we may allow these
@@ -4065,7 +4065,7 @@ static rlm_rcode_t common_eap_identity_resume(module_ctx_t const *mctx, REQUEST
         *      If the identity looks like a fast re-auth id
         *      run fast re-auth, otherwise do a fullauth.
         */
-       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+       identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type);
        if (identity_type) switch (identity_type->vp_uint32) {
        case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
                return common_reauthentication_enter(mctx, request, eap_session);
index 1c8cd0d53e1254cc89f89744f7ce447c34b24ef5..8cf2ad06aa52ed67b00b823f406a13372337c23c 100644 (file)
@@ -45,7 +45,7 @@ static int vector_opc_from_op(REQUEST *request, uint8_t const **out, uint8_t opc
        VALUE_PAIR      *opc_vp;
        VALUE_PAIR      *op_vp;
 
-       opc_vp = fr_pair_find_by_da(list, attr_sim_opc, TAG_ANY);
+       opc_vp = fr_pair_find_by_da(list, attr_sim_opc);
        if (opc_vp) {
                if (opc_vp->vp_length != MILENAGE_OPC_SIZE) {
                        REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
@@ -56,7 +56,7 @@ static int vector_opc_from_op(REQUEST *request, uint8_t const **out, uint8_t opc
                return 0;
        }
 
-       op_vp = fr_pair_find_by_da(list, attr_sim_op, TAG_ANY);
+       op_vp = fr_pair_find_by_da(list, attr_sim_op);
        if (op_vp) {
                if (op_vp->vp_length != MILENAGE_OP_SIZE) {
                        REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
@@ -86,7 +86,7 @@ static int vector_gsm_from_ki(REQUEST *request, VALUE_PAIR *vps, int idx, fr_aka
        /*
         *      Generate a new RAND value, and derive Kc and SRES from Ki
         */
-       ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY);
+       ki_vp = fr_pair_find_by_da(vps, attr_sim_ki);
        if (!ki_vp) {
                RDEBUG3("No &control:%sfound, not generating triplets locally", attr_sim_ki->name);
                return 1;
@@ -100,7 +100,7 @@ static int vector_gsm_from_ki(REQUEST *request, VALUE_PAIR *vps, int idx, fr_aka
         *      Check to see if we have a Ki for the IMSI, this allows us to generate the rest
         *      of the triplets.
         */
-       version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
+       version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version);
        if (!version_vp) {
                if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;
                version = opc_p ? FR_SIM_ALGO_VERSION_VALUE_COMP128_4 : FR_SIM_ALGO_VERSION_VALUE_COMP128_3;
@@ -403,7 +403,7 @@ static int vector_umts_from_ki(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_key
        /*
         *      Select the algorithm (default to Milenage)
         */
-       version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
+       version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version);
        if (version_vp) version = version_vp->vp_uint32;
 
        /*
@@ -438,7 +438,7 @@ static int vector_umts_from_ki(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_key
        /*
         *      Find the Ki VP and check its length
         */
-       ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY);
+       ki_vp = fr_pair_find_by_da(vps, attr_sim_ki);
        if (!ki_vp) {
                RDEBUG3("No &control:%s found, not generating quintuplets locally", attr_sim_ki->name);
                return 1;
@@ -451,13 +451,13 @@ static int vector_umts_from_ki(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_key
        /*
         *      Find the Sequence Number VP or default to SQN = 2
         */
-       sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY);
+       sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn);
        keys->sqn = sqn_vp ? sqn_vp->vp_uint64 : 2;     /* 2 is the lowest valid SQN on our side */
 
        /*
         *      Check if we have an AMF value
         */
-       amf_vp = fr_pair_find_by_da(vps, attr_sim_amf, TAG_ANY);
+       amf_vp = fr_pair_find_by_da(vps, attr_sim_amf);
        if (amf_vp) {
                if (amf_vp->vp_length != amf_size) {
                        REDEBUG("&control:%s has incorrect length, expected %zu bytes got %zu bytes",
@@ -597,7 +597,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch AUTN
         */
-       autn_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_autn, TAG_ANY);
+       autn_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_autn);
        if (!autn_vp) {
                RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_autn->name);
                return 1;
@@ -613,7 +613,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch CK
         */
-       ck_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ck, TAG_ANY);
+       ck_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ck);
        if (!ck_vp) {
                RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_ck->name);
                return 1;
@@ -629,7 +629,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch IK
         */
-       ik_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ik, TAG_ANY);
+       ik_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ik);
        if (!ik_vp) {
                RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_ik->name);
                return 1;
@@ -645,7 +645,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch RAND
         */
-       rand_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_rand, TAG_ANY);
+       rand_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_rand);
        if (!rand_vp) {
                RDEBUG3("No &control:%s attribute found, not using quintuplet derivation", attr_eap_aka_sim_rand->name);
                return 1;
@@ -660,7 +660,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch XRES
         */
-       xres_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_xres, TAG_ANY);
+       xres_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_xres);
        if (!xres_vp) {
                RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_xres->name);
                return 1;
@@ -676,7 +676,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch (optional) AK
         */
-       ak_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ak, TAG_ANY);
+       ak_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ak);
        if (ak_vp && (ak_vp->vp_length != MILENAGE_AK_SIZE)) {
                REDEBUG("&control:%s incorrect length.  Expected "
                        STRINGIFY(MILENAGE_AK_SIZE) " bytes, got %zu bytes",
@@ -687,7 +687,7 @@ static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_ak
        /*
         *      Fetch (optional) SQN
         */
-       sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY);
+       sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn);
        if (sqn_vp && (sqn_vp->vp_length != MILENAGE_SQN_SIZE)) {
                REDEBUG("&control:%s incorrect length.  Expected "
                        STRINGIFY(MILENAGE_AK_SIZE) " bytes, got %zu bytes",
@@ -829,7 +829,7 @@ int fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(REQUEST *request, VALUE_P
         *      This is the *old* counter value increment
         *      by 1 to get the *new* counter value
         */
-       counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter, TAG_ANY);
+       counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter);
        if (!counter_vp) {
                RDEBUG2("No &session-state:%s attribute found, can't calculate re-auth keys",
                        attr_eap_aka_sim_counter->name);
@@ -837,8 +837,8 @@ int fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(REQUEST *request, VALUE_P
        }
        counter_vp->vp_uint16++;
 
-       mk_vp = fr_pair_find_by_da(vps, attr_session_data, TAG_ANY);
-       if (!mk_vp) mk_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_mk, TAG_ANY);
+       mk_vp = fr_pair_find_by_da(vps, attr_session_data);
+       if (!mk_vp) mk_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_mk);
        if (!mk_vp) {
                RDEBUG2("Neither &session-state:%s or &session-state:%s attributes found, "
                        "can't calculate re-auth keys", attr_session_data->name, attr_eap_aka_sim_mk->name);
@@ -879,7 +879,7 @@ int fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(REQUEST *request, VALUE_PAIR
         *      This is the *old* counter value increment
         *      by 1 to get the *new* counter value
         */
-       counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter, TAG_ANY);
+       counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter);
        if (!counter_vp) {
                RDEBUG2("No &session-state:%s attribute found, can't calculate re-auth keys",
                        attr_eap_aka_sim_counter->name);
@@ -887,8 +887,8 @@ int fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(REQUEST *request, VALUE_PAIR
        }
        counter_vp->vp_uint16++;
 
-       k_re_vp = fr_pair_find_by_da(vps, attr_session_data, TAG_ANY);
-       if (!k_re_vp) k_re_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_k_re, TAG_ANY);
+       k_re_vp = fr_pair_find_by_da(vps, attr_session_data);
+       if (!k_re_vp) k_re_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_k_re);
        if (!k_re_vp) {
                RDEBUG2("Neither &session-state:%s or &session-sate:%s attributes found, "
                        "can't calculate re-auth keys", attr_session_data->name, attr_eap_aka_sim_k_re->name);
index 8eae3a5a1c312c627ffac6a9f9504cef2f783142..8cb1ff2dd12c0746338867b7693d749c2f372eee 100644 (file)
@@ -41,7 +41,7 @@ static ssize_t aka_sim_xlat_id_method_xlat(TALLOC_CTX *ctx, char **out, UNUSED s
                                           UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
                                           REQUEST *request, char const *fmt)
 {
-       tmpl_t                  *vpt;
+       tmpl_t                          *vpt;
        TALLOC_CTX                      *our_ctx = talloc_init_const("aka_sim_xlat");
        ssize_t                         slen, id_len;
        char const                      *p = fmt, *id, *method;
@@ -53,7 +53,8 @@ static ssize_t aka_sim_xlat_id_method_xlat(TALLOC_CTX *ctx, char **out, UNUSED s
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -128,7 +129,8 @@ static ssize_t aka_sim_xlat_id_type_xlat(TALLOC_CTX *ctx, char **out, UNUSED siz
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -201,7 +203,8 @@ static ssize_t aka_sim_3gpp_pseudonym_key_index_xlat(TALLOC_CTX *ctx, char **out
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -257,7 +260,8 @@ static ssize_t aka_sim_3gpp_pseudonym_decrypt_xlat(TALLOC_CTX *ctx, char **out,
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &id_vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &id_vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -276,7 +280,8 @@ static ssize_t aka_sim_3gpp_pseudonym_decrypt_xlat(TALLOC_CTX *ctx, char **out,
        }
        p++;
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &key_vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &key_vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -372,7 +377,8 @@ static ssize_t aka_sim_3gpp_pseudonym_encrypt_xlat(TALLOC_CTX *ctx, char **out,
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &id_vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &id_vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -391,7 +397,8 @@ static ssize_t aka_sim_3gpp_pseudonym_encrypt_xlat(TALLOC_CTX *ctx, char **out,
        }
        p++;
 
-       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &key_vpt, p, -1,
+       slen = tmpl_afrom_attr_substr(our_ctx, NULL, &key_vpt, &FR_SBUFF_IN(p, strlen(p)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -481,7 +488,7 @@ static ssize_t aka_sim_3gpp_pseudonym_encrypt_xlat(TALLOC_CTX *ctx, char **out,
        } else if ((id_len >= AKA_SIM_IMSI_MIN_LEN) && (id_len <= AKA_SIM_IMSI_MAX_LEN)) {
                VALUE_PAIR *eap_type;
 
-               eap_type = fr_pair_find_by_da(request->packet->vps, attr_eap_type, TAG_ANY);
+               eap_type = fr_pair_find_by_da(request->packet->vps, attr_eap_type);
                if (!eap_type) {
                        REDEBUG("SIM ID does not contain method hint, and no &control:EAP-Type found.  "
                                "Don't know what tag to prepend to encrypted identity");
index ef05c66deac2b0d2ee9fd9d66b7037c5435d89ff..b19d662c0929d04113687c797663dd458dc4f03d 100644 (file)
@@ -45,10 +45,10 @@ char const *fr_app_io_socket_name(TALLOC_CTX *ctx, fr_app_io_t const *app_io,
                        strlcpy(dst_buf, "::", sizeof(dst_buf));
                }
        } else {
-               fr_value_box_snprint(dst_buf, sizeof(dst_buf), fr_box_ipaddr(*dst_ipaddr), 0);
+               fr_value_box_print(&FR_SBUFF_OUT(dst_buf, sizeof(dst_buf)), fr_box_ipaddr(*dst_ipaddr), NULL);
        }
 
-       if (src_ipaddr) fr_value_box_snprint(src_buf, sizeof(src_buf), fr_box_ipaddr(*src_ipaddr), 0);
+       if (src_ipaddr) fr_value_box_print(&FR_SBUFF_OUT(src_buf, sizeof(src_buf)), fr_box_ipaddr(*src_ipaddr), NULL);
 
        if (!interface) {
                if (!src_ipaddr) {
index b244ad8e13a70b40f8883b270aed489b271b0dad..ecfd632a1bf3c137205ca6d6abe295a7a3ec19eb 100644 (file)
@@ -821,11 +821,13 @@ static void get_inst(fr_listen_t *li, fr_io_instance_t const **inst, fr_io_threa
 
 static RADCLIENT *radclient_alloc(TALLOC_CTX *ctx, int ipproto, fr_io_address_t *address)
 {
-       RADCLIENT *radclient;
+       RADCLIENT       *radclient;
+       char            *shortname;
 
        MEM(radclient = talloc_zero(ctx, RADCLIENT));
 
-       radclient->longname = radclient->shortname = fr_value_box_asprint(radclient, fr_box_ipaddr(address->src_ipaddr), '\0');
+       fr_value_box_aprint(radclient, &shortname, fr_box_ipaddr(address->src_ipaddr), NULL);
+       radclient->longname = radclient->shortname = shortname;
 
        radclient->secret = radclient->nas_type = talloc_strdup(radclient, "");
 
index 8370acef797cc77596bf873fa4f37a7101d42bd4..d8f08ddd530366795a1697a62435e09a6655e504 100644 (file)
@@ -198,7 +198,7 @@ json_object *json_object_from_value_box(TALLOC_CTX *ctx, fr_value_box_t const *d
                char            *p;
                json_object     *obj;
 
-               p = fr_value_box_asprint(ctx, data, '\0');
+               fr_value_box_aprint(ctx, &p, data, NULL);
                if (!p) return NULL;
 
                obj = json_object_new_string(p);
@@ -289,31 +289,29 @@ char *fr_json_from_string(TALLOC_CTX *ctx, char const *s, bool include_quotes)
  */
 size_t fr_json_from_pair(char *out, size_t outlen, VALUE_PAIR const *vp)
 {
-       size_t len, freespace = outlen;
+       size_t slen, freespace = outlen;
 
-       if (!vp->da->flags.has_tag) {
-               switch (vp->vp_type) {
-               case FR_TYPE_UINT32:
-                       if (vp->da->flags.has_value) break;
+       switch (vp->vp_type) {
+       case FR_TYPE_UINT32:
+               if (vp->da->flags.has_value) break;
 
-                       return snprintf(out, freespace, "%u", vp->vp_uint32);
+               return snprintf(out, freespace, "%u", vp->vp_uint32);
 
-               case FR_TYPE_UINT16:
-                       if (vp->da->flags.has_value) break;
+       case FR_TYPE_UINT16:
+               if (vp->da->flags.has_value) break;
 
-                       return snprintf(out, freespace, "%u", (unsigned int) vp->vp_uint16);
+               return snprintf(out, freespace, "%u", (unsigned int) vp->vp_uint16);
 
-               case FR_TYPE_UINT8:
-                       if (vp->da->flags.has_value) break;
+       case FR_TYPE_UINT8:
+               if (vp->da->flags.has_value) break;
 
-                       return snprintf(out, freespace, "%u", (unsigned int) vp->vp_uint8);
+               return snprintf(out, freespace, "%u", (unsigned int) vp->vp_uint8);
 
-               case FR_TYPE_INT32:
-                       return snprintf(out, freespace, "%d", vp->vp_int32);
+       case FR_TYPE_INT32:
+               return snprintf(out, freespace, "%d", vp->vp_int32);
 
-               default:
-                       break;
-               }
+       default:
+               break;
        }
 
        if (vp->vp_type == FR_TYPE_STRING) {
@@ -321,13 +319,13 @@ size_t fr_json_from_pair(char *out, size_t outlen, VALUE_PAIR const *vp)
 
                /* Indicate truncation */
                if (!tmp) return outlen + 1;
-               len = strlen(tmp);
-               if (freespace <= len) return outlen + 1;
+               slen = strlen(tmp);
+               if (freespace <= slen) return outlen + 1;
 
                strcpy(out, tmp);
                talloc_free(tmp);
 
-               return len;
+               return slen;
        }
 
        /* Indicate truncation */
@@ -335,10 +333,11 @@ size_t fr_json_from_pair(char *out, size_t outlen, VALUE_PAIR const *vp)
        *out++ = '"';
        freespace--;
 
-       len = fr_pair_value_snprint(out, freespace, vp, 0);
-       if (is_truncated(len, freespace)) return (outlen - freespace) + len;
-       out += len;
-       freespace -= len;
+       slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(out, freespace), vp, T_BARE_WORD);
+       if (slen < 0) return slen;
+
+       out += (size_t)slen;
+       freespace -= (size_t)slen;
 
        /* Indicate truncation */
        if (freespace < 2) return outlen + 1;
index 87aa40ccf41639d297e223d39adde8b51ba2f70d..fdafe8774ec96546874fa9946c4c0e65ffccff15 100644 (file)
@@ -196,7 +196,7 @@ int fr_ldap_control_add_session_tracking(fr_ldap_connection_t *conn, REQUEST *re
                if (fr_dict_attr_is_top_level(vp->da)) switch (vp->da->attr) {
                case FR_NAS_IP_ADDRESS:
                case FR_NAS_IPV6_ADDRESS:
-                       fr_pair_value_snprint(ipaddress, sizeof(ipaddress), vp, '\0');
+                       fr_pair_print_value_quoted(&FR_SBUFF_OUT(ipaddress, sizeof(ipaddress)), vp, T_BARE_WORD);
                        break;
 
                case FR_USER_NAME:
index d2fcbc5d1b2e70cb2ca4bc9e76accdf7150774bb..d61900e0aa2f5e5ce3796d45361865f248f6fdf9 100644 (file)
@@ -288,7 +288,7 @@ int fr_redis_reply_to_map(TALLOC_CTX *ctx, vp_map_t **out, REQUEST *request,
        RDEBUG3("Got op    : %s", op->str);
        RDEBUG3("Got value : %pV", fr_box_strvalue_len(value->str, value->len));
 
-       map = talloc_zero(ctx, vp_map_t);
+       MEM(map = talloc_zero(ctx, vp_map_t));
        slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, key->str, &(tmpl_rules_t){ .dict_def = request->dict });
        if (slen < 0) {
                REMARKER(key->str, -slen, "%s", fr_strerror());
@@ -358,17 +358,18 @@ int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[
        char            key_buf[256];
        char            *key;
        size_t          key_len;
-       size_t          need;
+       ssize_t         slen;
 
        fr_assert(tmpl_is_attr(map->lhs));
        fr_assert(tmpl_is_data(map->rhs));
 
-       key_len = tmpl_snprint(&need, key_buf, sizeof(key_buf), map->lhs);
-       if (need) {
+       slen = tmpl_print(&FR_SBUFF_OUT(key_buf, sizeof(key_buf)), map->lhs, TMPL_ATTR_REF_PREFIX_NO, NULL);
+       if (slen < 0) {
                fr_strerror_printf("Key too long.  Must be < " STRINGIFY(sizeof(key_buf)) " "
-                                  "bytes, got %zu bytes", key_len);
+                                  "bytes, got %zu bytes", (size_t)(slen * -1));
                return -1;
        }
+       key_len = (size_t)slen;
        key = talloc_bstrndup(pool, key_buf, key_len);
        if (!key) return -1;
 
@@ -383,7 +384,7 @@ int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[
         *      For everything else we get the string representation
         */
        default:
-               new = fr_value_box_asprint(pool, tmpl_value(map->rhs), '\0');
+               fr_value_box_aprint(pool, &new, tmpl_value(map->rhs), NULL);
                if (!new) {
                        talloc_free(key);
                        return -1;
index 33a8085da2d63aa2ab120589e45d29c9f0adb939..a826c2ce964ad7d4a1996e5c016be88e96c0dc34 100644 (file)
@@ -53,12 +53,12 @@ rlm_rcode_t rad_virtual_server(REQUEST *request)
        RDEBUG("Virtual server %s received request", cf_section_name2(request->server_cs));
        log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, NULL);
 
-       username = fr_pair_find_by_num(request->packet->vps, 0, FR_STRIPPED_USER_NAME, TAG_ANY);
-       if (!username) username = fr_pair_find_by_num(request->packet->vps, 0, FR_USER_NAME, TAG_ANY);
+       username = fr_pair_find_by_num(request->packet->vps, 0, FR_STRIPPED_USER_NAME);
+       if (!username) username = fr_pair_find_by_num(request->packet->vps, 0, FR_USER_NAME);
 
        if (request->parent) {
-               parent_username = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_STRIPPED_USER_NAME, TAG_ANY);
-               if (!parent_username) parent_username = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_USER_NAME, TAG_ANY);
+               parent_username = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_STRIPPED_USER_NAME);
+               if (!parent_username) parent_username = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_USER_NAME);
        }
 
        /*
@@ -69,7 +69,7 @@ rlm_rcode_t rad_virtual_server(REQUEST *request)
                 *      Look at the full User-Name with realm.
                 */
                if (parent_username->da->attr == FR_STRIPPED_USER_NAME) {
-                       vp = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_USER_NAME, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->parent->packet->vps, 0, FR_USER_NAME);
                        if (!vp) goto runit;
                } else {
                        vp = parent_username;
index 1c314d564e606ce68b0467ef1992b5f79fb0afea..b457563b9d83988513217b3596cc3d14a135803c 100644 (file)
@@ -32,12 +32,12 @@ RCSID("$Id$")
 
 #include <freeradius-devel/server/cf_file.h>
 #include <freeradius-devel/server/cf_priv.h>
-#include <freeradius-devel/server/log.h>
 #include <freeradius-devel/server/cond.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/log.h>
 #include <freeradius-devel/server/util.h>
-
+#include <freeradius-devel/server/virtual_servers.h>
 #include <freeradius-devel/util/cursor.h>
+#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/misc.h>
 #include <freeradius-devel/util/syserror.h>
 
@@ -1169,13 +1169,11 @@ static int cf_file_fill(cf_stack_t *stack);
 
 static CONF_ITEM *process_if(cf_stack_t *stack)
 {
-       ssize_t slen = 0;
-       char const *error = NULL;
-       fr_cond_t *cond = NULL;
-       CONF_DATA const *cd;
-       fr_dict_t const *dict = NULL;
-       CONF_SECTION *cs;
-       char *p;
+       ssize_t         slen = 0;
+       fr_cond_t       *cond = NULL;
+       fr_dict_t const *dict = NULL;
+       CONF_SECTION    *cs;
+       char            *p;
        char const      *ptr = stack->ptr;
        cf_stack_frame_t *frame = &stack->frame[stack->depth];
        CONF_SECTION    *parent = frame->current;
@@ -1187,12 +1185,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack)
        buff[1] = stack->buff[1];
        buff[2] = stack->buff[2];
 
-       cd = cf_data_find_in_parent(parent, fr_dict_t **, "dictionary");
-       if (!cd) {
-               dict = fr_dict_internal();      /* HACK - To fix policy sections */
-       } else {
-               dict = *((fr_dict_t **)cf_data_value(cd));
-       }
+       dict = virtual_server_namespace_by_ci(cf_section_to_item(parent));
 
        /*
         *      fr_cond_tokenize needs the current section, so we
@@ -1211,7 +1204,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack)
         *      Skip (...) to find the {
         */
        while (true) {
-               slen = fr_cond_tokenize(cs, &cond, &error, dict, ptr, strlen(ptr));
+               slen = fr_cond_tokenize(cs, &cond, dict, &FR_SBUFF_IN(ptr, strlen(ptr)));
                if (slen < 0) {
                        ssize_t end = -slen;
 
@@ -1255,7 +1248,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack)
 
                cf_log_err(cs, "Parse error in condition");
                cf_log_err(cs, "%s", text);
-               cf_log_err(cs, "%s^ %s", spaces, error);
+               cf_log_err(cs, "%s^ %s", spaces, fr_strerror());
 
                talloc_free(spaces);
                talloc_free(text);
@@ -2370,7 +2363,7 @@ int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
                if (c) {
                        char buffer[1024];
 
-                       cond_snprint(NULL, buffer, sizeof(buffer), c);
+                       cond_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), c);
                        fprintf(fp, "(%s)", buffer);
 
                } else {        /* dump the string as-is */
index 6ebdc16c017087aebd27cbcdb72a24332af3ab01..314f528d85305a7b438035b7c636fd48821598c7 100644 (file)
@@ -32,9 +32,9 @@ RCSID("$Id$")
 #include <freeradius-devel/server/cf_parse.h>
 #include <freeradius-devel/server/cf_priv.h>
 #include <freeradius-devel/server/log.h>
-#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/server/tmpl.h>
-
+#include <freeradius-devel/server/virtual_servers.h>
+#include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/inet.h>
 #include <freeradius-devel/util/misc.h>
 #include <freeradius-devel/util/types.h>
@@ -174,7 +174,12 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
        }
 
        if (tmpl) {
-               tmpl_t *vpt;
+               tmpl_t                  *vpt;
+               static tmpl_rules_t     rules = {
+                                               .allow_unknown = true,
+                                               .allow_unresolved = true,
+                                               .allow_foreign = true
+                                       };
 
                if (!cp->printed) cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, cf_pair_attr(cp), cp->value);
 
@@ -187,18 +192,12 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                 *      xlat strings.
                 */
                if (attribute) {
-                       slen = tmpl_afrom_attr_str(cp, NULL, &vpt, cf_pair_value(cp),
-                                                  &(tmpl_rules_t){
-                                                       .allow_unknown = true,
-                                                       .allow_unresolved = true
-                                                  });
+                       slen = tmpl_afrom_attr_str(cp, NULL, &vpt, cf_pair_value(cp), &rules);
                } else {
-                       slen = tmpl_afrom_str(cp, &vpt, cf_pair_value(cp), strlen(cf_pair_value(cp)),
-                                             cf_pair_value_quote(cp),
-                                             &(tmpl_rules_t){
-                                                       .allow_unknown = true,
-                                                       .allow_unresolved = true
-                                             }, false);
+                       slen = tmpl_afrom_substr(cp, &vpt, &FR_SBUFF_IN(cf_pair_value(cp), strlen(cf_pair_value(cp))),
+                                                cf_pair_value_quote(cp),
+                                                tmpl_parse_rules_unquoted[cf_pair_value_quote(cp)],
+                                                &rules);
                }
 
                if (slen < 0) {
@@ -464,7 +463,7 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
 
                if (!cp->printed) {
                        char *p;
-                       p = fr_value_box_asprint(NULL, fr_box_time_delta(delta), 0);
+                       fr_value_box_aprint(NULL, &p, fr_box_time_delta(delta), NULL);
                        cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, cf_pair_attr(cp), p);
                        talloc_free(p);
                }
@@ -885,7 +884,7 @@ static int CC_HINT(nonnull(4,5)) cf_pair_parse_internal(TALLOC_CTX *ctx, void *o
  *                     Should be one of the following ``data`` types,
  *                     and one or more of the following ``flag`` types or'd together:
  *     - ``data`` #FR_TYPE_TMPL                - @copybrief FR_TYPE_TMPL
- *                                               Feeds the value into #tmpl_afrom_str. Value can be
+ *                                               Feeds the value into #tmpl_afrom_substr. Value can be
  *                                               obtained when processing requests, with #tmpl_expand or #tmpl_aexpand.
  *     - ``data`` #FR_TYPE_BOOL                - @copybrief FR_TYPE_BOOL
  *     - ``data`` #FR_TYPE_UINT32              - @copybrief FR_TYPE_UINT32
@@ -1311,10 +1310,15 @@ static int cf_parse_tmpl_pass2(CONF_SECTION *cs, tmpl_t **out, CONF_PAIR *cp, fr
        ssize_t slen;
        tmpl_t *vpt;
 
-       slen = tmpl_afrom_str(cs, &vpt, cp->value, talloc_array_length(cp->value) - 1,
-                             cf_pair_value_quote(cp),
-                             &(tmpl_rules_t){ .allow_unknown = true, .allow_unresolved = true },
-                             true);
+       slen = tmpl_afrom_substr(cs, &vpt,
+                                &FR_SBUFF_IN(cp->value, talloc_array_length(cp->value) - 1),
+                                cf_pair_value_quote(cp),
+                                tmpl_parse_rules_quoted[cf_pair_value_quote(cp)],
+                                &(tmpl_rules_t){
+                                       .allow_unknown = true,
+                                       .allow_unresolved = true,
+                                       .allow_foreign = true
+                                });
        if (slen < 0) {
                char *spaces, *text;
 
@@ -1360,13 +1364,16 @@ static int cf_parse_tmpl_pass2(CONF_SECTION *cs, tmpl_t **out, CONF_PAIR *cp, fr
        case TMPL_TYPE_LIST:
        case TMPL_TYPE_DATA:
        case TMPL_TYPE_EXEC:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
+       case TMPL_TYPE_EXEC_UNRESOLVED:
        case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_XLAT_UNRESOLVED:
                break;
 
        case TMPL_TYPE_UNINITIALISED:
-       case TMPL_TYPE_REGEX_UNRESOLVED:
        case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
        case TMPL_TYPE_NULL:
        case TMPL_TYPE_MAX:
                fr_assert(0);
@@ -1399,6 +1406,7 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                CONF_PARSER     *rule;
                void            *data;
                int             type;
+               fr_dict_t const *dict = NULL;
 
                rule = cf_data_value(rule_cd);
 
@@ -1473,6 +1481,12 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                        continue;
                }
 
+               /*
+                *      Search for dictionary data somewhere in the virtual
+                *      server.
+                */
+               dict = virtual_server_namespace_by_ci(cf_section_to_item(cs));
+
                /*
                 *      Parse (and throw away) the xlat string (for validation).
                 *
@@ -1489,7 +1503,14 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                        /*
                         *      xlat expansions should be parseable.
                         */
-                       slen = xlat_tokenize(cs, &xlat, cp->value, talloc_array_length(cp->value) - 1, NULL);
+                       slen = xlat_tokenize(cs, &xlat, NULL,
+                                            &FR_SBUFF_IN(cp->value, talloc_array_length(cp->value) - 1), NULL,
+                                            &(tmpl_rules_t){
+                                               .dict_def = dict,
+                                               .allow_unknown = false,
+                                               .allow_unresolved = false,
+                                               .allow_foreign = (dict == NULL)
+                                            });
                        if (slen < 0) {
                                char *spaces, *text;
 
index d514f477cd048c4d9923f0bc4969158cbc93e1e8..5b50a7970e2348e2ff7d139409ec0ecfe90ced39 100644 (file)
@@ -1862,16 +1862,20 @@ void _cf_log_perr(fr_log_type_t type, CONF_ITEM const *ci, char const *file, int
        {
                char const      *e, *p;
                int             len;
-               char            *msg;
+               char            *msg = NULL;
 
                truncate_filename(&e, &p, &len, ci->filename);
 
-               va_start(ap, fmt);
-               msg = fr_vasprintf(NULL, fmt, ap);
-               va_end(ap);
+               if (fmt) {
+                       va_start(ap, fmt);
+                       msg = fr_vasprintf(NULL, fmt, ap);
+                       va_end(ap);
+               }
 
-               fr_log_perror(LOG_DST, type, file, line, "%s%.*s[%d]: %s", e, len, p, ci->lineno, msg);
-               talloc_free(msg);
+               fr_log_perror(LOG_DST, type, file, line, "%s%.*s[%d]%s%s", e, len, p, ci->lineno,
+                             msg ? ": " : "", msg ? msg : "");
+
+               if (fmt) talloc_free(msg);
        }
 }
 
index bb30a220a460f07c7e0fd2e1512fcf1426778284..2d4cf7fb19aa5ea16fe84c827fe2628170100177 100644 (file)
@@ -955,7 +955,7 @@ RADCLIENT *client_afrom_request(TALLOC_CTX *ctx, REQUEST *request)
 
        if (!request) return NULL;
 
-       fr_value_box_snprint(src_buf, sizeof(src_buf), fr_box_ipaddr(request->packet->src_ipaddr), 0);
+       fr_value_box_print(&FR_SBUFF_OUT(src_buf, sizeof(src_buf)), fr_box_ipaddr(request->packet->src_ipaddr), NULL);
 
        snprintf(buffer, sizeof(buffer), "dynamic_%i_%s", cnt++, src_buf);
 
index cf21693f1317139a78bec67c5f05886fd9a073a2..1a70e3d87adc6dc973b8fc994a5b4c1ac66d83b7 100644 (file)
@@ -45,11 +45,18 @@ typedef enum {
        COND_OR = '|'
 } fr_cond_op_t;
 
+extern fr_table_num_sorted_t const cond_logical_op_table[];
+extern size_t cond_logical_op_table_len;
+
+extern fr_table_num_sorted_t const cond_cmp_op_table[];
+extern size_t cond_cmp_op_table_len;
+
 typedef enum {
        COND_TYPE_INVALID = 0,
        COND_TYPE_TRUE,
        COND_TYPE_FALSE,
        COND_TYPE_EXISTS,
+       COND_TYPE_RCODE,
        COND_TYPE_MAP,
        COND_TYPE_CHILD
 } fr_cond_type_t;
@@ -73,25 +80,29 @@ typedef enum {
 struct fr_cond_s {
        fr_cond_type_t          type;
 
-       CONF_ITEM const         *ci;
+       CONF_ITEM const         *ci;            //!< Configuration item this condition
+                                               ///< is derived from.
+
        union {
-               vp_map_t                *map;
-               tmpl_t          *vpt;
-               fr_cond_t               *child;
+               vp_map_t                *map;           //!< Binary expression.
+               tmpl_t                  *vpt;           //!< Unary expression.
+               fr_cond_t               *child;         //!< Nested condition.
+               rlm_rcode_t             rcode;          //!< Rcode check.   We handle this outside of
+                                                       ///< tmpls as it doesn't apply anywhere else.
        } data;
 
-       bool                    negate;
+       bool                    negate;         //!< Invert the result of the expression.
        fr_cond_pass2_t         pass2_fixup;
 
-       fr_dict_attr_t const    *cast;
+       fr_dict_attr_t const    *cast;          //!< Legacy - Should be removed.
 
        fr_cond_op_t            next_op;
        fr_cond_t               *next;
 };
 
-ssize_t fr_cond_tokenize(CONF_SECTION *cs, fr_cond_t **head, char const **error,
-                        fr_dict_t const *dict, char const *in, size_t inlen) CC_HINT(nonnull);
-size_t cond_snprint(size_t *need, char *buffer, size_t bufsize, fr_cond_t const *c);
+ssize_t        fr_cond_tokenize(CONF_SECTION *cs, fr_cond_t **head, fr_dict_t const *dict, fr_sbuff_t *in) CC_HINT(nonnull(1,2,4));
+
+ssize_t        cond_print(fr_sbuff_t *out, fr_cond_t const *c);
 
 bool fr_cond_walk(fr_cond_t *head, bool (*callback)(fr_cond_t *cond, void *uctx), void *uctx);
 
index 531105e320557c55eeb0bcb3cfb36e3cbf865d25..3836b52538e25d69f58855bd6a4134df9b0469bd 100644 (file)
@@ -44,27 +44,20 @@ RCSID("$Id$")
 /** Map keywords to #pair_list_t values
  */
 static fr_table_num_sorted_t const cond_type_table[] = {
-       { L("child"),   COND_TYPE_CHILD                 },
-       { L("exists"),  COND_TYPE_EXISTS                },
-       { L("false"),   COND_TYPE_FALSE                 },
-       { L("invalid"), COND_TYPE_INVALID               },
-       { L("map"),     COND_TYPE_MAP                   },
-       { L("true"),    COND_TYPE_TRUE                  },
+       { L("child"),           COND_TYPE_CHILD         },
+       { L("exists"),          COND_TYPE_EXISTS        },
+       { L("false"),           COND_TYPE_FALSE         },
+       { L("invalid"),         COND_TYPE_INVALID       },
+       { L("map"),             COND_TYPE_MAP           },
+       { L("true"),            COND_TYPE_TRUE          },
 };
 static size_t cond_type_table_len = NUM_ELEMENTS(cond_type_table);
 
-static fr_table_num_sorted_t const cond_op_table[] = {
-       { L("none"),    COND_NONE                       },
-       { L("&&"),              COND_AND                        },
-       { L("||"),              COND_OR                         },
-};
-static size_t cond_op_table_len = NUM_ELEMENTS(cond_op_table);
-
 static fr_table_num_sorted_t const cond_pass2_table[] = {
-       { L("none"),    PASS2_FIXUP_NONE                },
-       { L("attr"),    PASS2_FIXUP_ATTR                },
-       { L("type"),    PASS2_FIXUP_TYPE                },
-       { L("paircompre"),      PASS2_PAIRCOMPARE               },
+       { L("none"),            PASS2_FIXUP_NONE        },
+       { L("attr"),            PASS2_FIXUP_ATTR        },
+       { L("type"),            PASS2_FIXUP_TYPE        },
+       { L("paircompre"),      PASS2_PAIRCOMPARE       },
 };
 static size_t cond_pass2_table_len = NUM_ELEMENTS(cond_pass2_table);
 
@@ -88,28 +81,38 @@ static bool all_digits(char const *string)
  */
 void cond_debug(fr_cond_t const *cond)
 {
-       fr_cond_t const *next;
+       fr_cond_t const *c;
 
-       for (next = cond; next; next = next->next) {
-               INFO("cond %s (%p)", fr_table_str_by_value(cond_type_table, cond->type, "<INVALID>"), cond);
-               INFO("\tnegate : %s", cond->negate ? "true" : "false");
-               INFO("\tcast   : %s", cond->cast ? fr_table_str_by_value(fr_value_box_type_table,
-                                                                        cond->cast->type, "<INVALID>") : "none");
-               INFO("\top     : %s", fr_table_str_by_value(cond_op_table, cond->next_op, "<INVALID>"));
-               INFO("\tfixup  : %s", fr_table_str_by_value(cond_pass2_table, cond->pass2_fixup, "<INVALID>"));
+       for (c = cond; c; c =c->next) {
+               INFO("cond %s (%p)", fr_table_str_by_value(cond_type_table, c->type, "<INVALID>"), cond);
+               INFO("\tnegate : %s", c->negate ? "true" : "false");
+               INFO("\tcast   : %s", c->cast ? fr_table_str_by_value(fr_value_box_type_table,
+                                                                     c->cast->type, "<INVALID>") : "none");
+               INFO("\top     : %s", fr_table_str_by_value(cond_logical_op_table, c->next_op, "<INVALID>"));
+               INFO("\tfixup  : %s", fr_table_str_by_value(cond_pass2_table, c->pass2_fixup, "<INVALID>"));
 
-               switch (cond->type) {
+               switch (c->type) {
                case COND_TYPE_MAP:
-                       tmpl_attr_debug(cond->data.map->lhs);
-                       tmpl_attr_debug(cond->data.map->rhs);
+                       INFO("lhs (");
+                       tmpl_debug(c->data.map->lhs);
+                       INFO(")");
+                       INFO("rhs (");
+                       tmpl_debug(c->data.map->rhs);
+                       INFO(")");
+                       break;
+
+               case COND_TYPE_RCODE:
+                       INFO("\trcode  : %s", fr_table_str_by_value(rcode_table, c->data.rcode, ""));
                        break;
 
                case COND_TYPE_EXISTS:
-                       tmpl_attr_debug(cond->data.vpt);
+                       tmpl_debug(c->data.vpt);
                        break;
 
                case COND_TYPE_CHILD:
-                       cond_debug(cond->next);
+                       INFO("child (");
+                       cond_debug(c->data.child);
+                       INFO(")");
                        break;
 
                default:
@@ -123,7 +126,6 @@ void cond_debug(fr_cond_t const *cond)
  * Converts a tmpl_t to a boolean value.
  *
  * @param[in] request the REQUEST
- * @param[in] modreturn the previous module return code
  * @param[in] depth of the recursion (only used for debugging)
  * @param[in] vpt the template to evaluate
  * @return
@@ -131,29 +133,19 @@ void cond_debug(fr_cond_t const *cond)
  *     - 0 for "no match".
  *     - 1 for "match".
  */
-int cond_eval_tmpl(REQUEST *request, int modreturn, UNUSED int depth, tmpl_t const *vpt)
+int cond_eval_tmpl(REQUEST *request, UNUSED int depth, tmpl_t const *vpt)
 {
        int rcode = -1;
-       int modcode;
 
        switch (vpt->type) {
        case TMPL_TYPE_UNRESOLVED:
-               modcode = fr_table_value_by_str(rcode_table, vpt->name, RLM_MODULE_UNKNOWN);
-               if (modcode != RLM_MODULE_UNKNOWN) {
-                       rcode = (modcode == modreturn);
-                       break;
-               }
-
                /*
-                *      Else it's a literal string.  Empty string is
-                *      false, non-empty string is true.
+                *      Empty string is false, non-empty string
+                *      is true.
                 *
                 *      @todo: Maybe also check for digits?
-                *
-                *      The VPT *doesn't* have a "bare word" type,
-                *      which arguably it should.
                 */
-               rcode = (*vpt->name != '\0');
+               rcode = (*vpt->data.unescaped != '\0');
                break;
 
        case TMPL_TYPE_ATTR:
@@ -166,14 +158,11 @@ int cond_eval_tmpl(REQUEST *request, int modreturn, UNUSED int depth, tmpl_t con
                break;
 
        case TMPL_TYPE_XLAT:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_EXEC:
        {
                char    *p;
                ssize_t slen;
 
-               if (!*vpt->name) return false;
-
                slen = tmpl_aexpand(request, &p, request, vpt, NULL, NULL);
                if (slen < 0) {
                        EVAL_DEBUG("FAIL %d", __LINE__);
@@ -188,8 +177,10 @@ int cond_eval_tmpl(REQUEST *request, int modreturn, UNUSED int depth, tmpl_t con
        /*
         *      Can't have a bare ... (/foo/) ...
         */
-       case TMPL_TYPE_REGEX_UNRESOLVED:
        case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
                fr_assert(0 == 1);
                FALL_THROUGH;
 
@@ -230,14 +221,14 @@ static int cond_do_regex(REQUEST *request, fr_cond_t const *c,
 
        switch (map->rhs->type) {
        case TMPL_TYPE_REGEX: /* pre-compiled to a regex */
-               preg = tmpl_preg(map->rhs);
+               preg = tmpl_regex(map->rhs);
                break;
 
        default:
                if (!fr_cond_assert(rhs && rhs->type == FR_TYPE_STRING)) return -1;
                if (!fr_cond_assert(rhs && rhs->vb_strvalue)) return -1;
                slen = regex_compile(request, &rreg, rhs->vb_strvalue, rhs->datum.length,
-                                    &tmpl_regex_flags(map->rhs), true, true);
+                                    tmpl_regex_flags(map->rhs), true, true);
                if (slen <= 0) {
                        REMARKER(rhs->vb_strvalue, -slen, "%s", fr_strerror());
                        EVAL_DEBUG("FAIL %d", __LINE__);
@@ -436,15 +427,10 @@ static int cond_normalise_and_cmp(REQUEST *request, fr_cond_t const *c, fr_value
        fr_value_box_t          lhs_cast = { .type = FR_TYPE_INVALID };
        fr_value_box_t          rhs_cast = { .type = FR_TYPE_INVALID };
 
-       xlat_escape_legacy_t            escape = NULL;
+       xlat_escape_legacy_t    escape = NULL;
 
        /*
         *      Cast operand to correct type.
-        *
-        *      With hack for strings that look like integers, to cast them
-        *      to 64 bit unsigned integers.
-        *
-        * @fixme For things like this it'd be useful to have a 64bit signed type.
         */
 #define CAST(_s) \
 do {\
@@ -453,7 +439,7 @@ do {\
                           fr_table_str_by_value(fr_value_box_type_table, _s->type, "<INVALID>"),\
                           fr_table_str_by_value(fr_value_box_type_table, cast_type, "<INVALID>"));\
                if (fr_value_box_cast(request, &_s ## _cast, cast_type, cast, _s) < 0) {\
-                       RPEDEBUG("Failed casting " #_s " operand");\
+                       if (request) RPEDEBUG("Failed casting " #_s " operand");\
                        rcode = -1;\
                        goto finish;\
                }\
@@ -567,8 +553,8 @@ do {\
         */
        case TMPL_TYPE_UNRESOLVED:
        case TMPL_TYPE_EXEC:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_REGEX_XLAT:
        {
                ssize_t ret;
                fr_value_box_t data;
@@ -584,7 +570,7 @@ do {\
                        }
                        fr_value_box_bstrndup_shallow(&data, NULL, p, ret, false);
                } else {
-                       fr_value_box_bstrndup_shallow(&data, NULL, map->rhs->name, map->rhs->len, false);
+                       fr_value_box_bstrdup_buffer_shallow(NULL, &data, NULL, map->rhs->data.unescaped, false);
                }
                rhs = &data;
 
@@ -612,7 +598,10 @@ do {\
        case TMPL_TYPE_LIST:
        case TMPL_TYPE_UNINITIALISED:
        case TMPL_TYPE_ATTR_UNRESOLVED:
-       case TMPL_TYPE_REGEX_UNRESOLVED:        /* Should now be a TMPL_TYPE_REGEX or TMPL_TYPE_XLAT */
+       case TMPL_TYPE_XLAT_UNRESOLVED:         /* should now be a TMPL_TYPE_XLAT */
+       case TMPL_TYPE_EXEC_UNRESOLVED:         /* should now be a TMPL_TYPE_EXEC */
+       case TMPL_TYPE_REGEX_UNCOMPILED:        /* should now be a TMPL_TYPE_REGEX */
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:   /* Should now be a TMPL_TYPE_REGEX_XLAT */
        case TMPL_TYPE_MAX:
                fr_assert(0);
                rcode = -1;
@@ -630,7 +619,6 @@ finish:
 /** Evaluate a map
  *
  * @param[in] request the REQUEST
- * @param[in] modreturn the previous module return code
  * @param[in] depth of the recursion (only used for debugging)
  * @param[in] c the condition to evaluate
  * @return
@@ -638,7 +626,7 @@ finish:
  *     - 0 for "no match".
  *     - 1 for "match".
  */
-int cond_eval_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c)
+int cond_eval_map(REQUEST *request, UNUSED int depth, fr_cond_t const *c)
 {
        int rcode = 0;
 
@@ -690,7 +678,6 @@ int cond_eval_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_c
 
        case TMPL_TYPE_UNRESOLVED:
        case TMPL_TYPE_EXEC:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT:
        {
                char            *p = NULL;
@@ -706,7 +693,7 @@ int cond_eval_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_c
 
                        fr_value_box_bstrndup_shallow(&data, NULL, p, ret, false);
                } else {
-                       fr_value_box_bstrndup_shallow(&data, NULL, map->lhs->name, map->lhs->len, false);
+                       fr_value_box_bstrdup_buffer_shallow(NULL, &data, NULL, map->lhs->data.unescaped, false);
                }
 
                rcode = cond_normalise_and_cmp(request, c, &data);
@@ -720,8 +707,12 @@ int cond_eval_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_c
        case TMPL_TYPE_NULL:
        case TMPL_TYPE_ATTR_UNRESOLVED:
        case TMPL_TYPE_UNINITIALISED:
-       case TMPL_TYPE_REGEX_UNRESOLVED:                /* should now be a TMPL_TYPE_REGEX or TMPL_TYPE_XLAT */
-       case TMPL_TYPE_REGEX:   /* not allowed as LHS */
+       case TMPL_TYPE_EXEC_UNRESOLVED:         /* should now be a TMPL_TYPE_EXEC */
+       case TMPL_TYPE_XLAT_UNRESOLVED:         /* should now be a TMPL_TYPE_XLAT */
+       case TMPL_TYPE_REGEX_UNCOMPILED:        /* should now be a TMPL_TYPE_REGEX */
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:   /* should now be a TMPL_TYPE_REGEX_XLAT */
+       case TMPL_TYPE_REGEX:                   /* not allowed as LHS */
+       case TMPL_TYPE_REGEX_XLAT:              /* not allowed as LHS */
        case TMPL_TYPE_MAX:
                fr_assert(0);
                rcode = -1;
@@ -745,26 +736,30 @@ int cond_eval_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_c
  *     - 0 for "no match".
  *     - 1 for "match".
  */
-int cond_eval(REQUEST *request, int modreturn, int depth, fr_cond_t const *c)
+int cond_eval(REQUEST *request, rlm_rcode_t modreturn, int depth, fr_cond_t const *c)
 {
        int rcode = -1;
 #ifdef WITH_EVAL_DEBUG
        char buffer[1024];
 
-       cond_snprint(NULL, buffer, sizeof(buffer), c);
+       cond_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), c);
        EVAL_DEBUG("%s", buffer);
 #endif
 
        while (c) {
                switch (c->type) {
                case COND_TYPE_EXISTS:
-                       rcode = cond_eval_tmpl(request, modreturn, depth, c->data.vpt);
+                       rcode = cond_eval_tmpl(request, depth, c->data.vpt);
                        /* Existence checks are special, because we expect them to fail */
                        if (rcode < 0) rcode = 0;
                        break;
 
+               case COND_TYPE_RCODE:
+                       rcode = (c->data.rcode == modreturn);
+                       break;
+
                case COND_TYPE_MAP:
-                       rcode = cond_eval_map(request, modreturn, depth, c);
+                       rcode = cond_eval_map(request, depth, c);
                        break;
 
                case COND_TYPE_CHILD:
index 259c1a5ef80135cb3351b9ddee8958cdeb3f209d..b509afaab1f2ca0b1e9044c2386adcec1d7c06bf 100644 (file)
@@ -38,9 +38,9 @@ typedef struct fr_cond_s fr_cond_t;
 
 void   cond_debug(fr_cond_t const *cond);
 
-int    cond_eval_tmpl(REQUEST *request, int modreturn, int depth, tmpl_t const *vpt);
-int    cond_eval_map(REQUEST *request, int modreturn, int depth, fr_cond_t const *c);
-int    cond_eval(REQUEST *request, int modreturn, int depth, fr_cond_t const *c);
+int    cond_eval_tmpl(REQUEST *request, int depth, tmpl_t const *vpt);
+int    cond_eval_map(REQUEST *request, int depth, fr_cond_t const *c);
+int    cond_eval(REQUEST *request, rlm_rcode_t modreturn, int depth, fr_cond_t const *c);
 
 #ifdef __cplusplus
 }
index 94e4be7ba01e82b0a30d11e056b146566d13fd97..90f4643761731ae4aa40bf0c3c70766b84dbbbda 100644 (file)
@@ -32,121 +32,125 @@ RCSID("$Id$")
 #include <ctype.h>
 
 static fr_table_num_sorted_t const allowed_return_codes[] = {
-       { L("fail"),       1 },
-       { L("handled"),    1 },
-       { L("invalid"),    1 },
-       { L("noop"),       1 },
-       { L("notfound"),   1 },
-       { L("ok"),              1 },
-       { L("reject"),     1 },
-       { L("updated"),    1 },
-       { L("disallow"),   1 }
+       { L("fail"),            1 },
+       { L("handled"),         1 },
+       { L("invalid"),         1 },
+       { L("noop"),            1 },
+       { L("notfound"),        1 },
+       { L("ok"),              1 },
+       { L("reject"),          1 },
+       { L("updated"),         1 },
+       { L("disallow"),        1 }
 };
 static size_t allowed_return_codes_len = NUM_ELEMENTS(allowed_return_codes);
 
+fr_table_num_sorted_t const cond_quote_table[] = {
+       { L("\""),      T_DOUBLE_QUOTED_STRING  },      /* Don't re-order, backslash throws off ordering */
+       { L("'"),       T_SINGLE_QUOTED_STRING  },
+       { L("/"),       T_SOLIDUS_QUOTED_STRING },
+       { L("`"),       T_BACK_QUOTED_STRING    }
+};
+size_t cond_quote_table_len = NUM_ELEMENTS(cond_quote_table);
+
+fr_table_num_sorted_t const cond_logical_op_table[] = {
+       { L("&&"),      COND_AND                },
+       { L("||"),      COND_OR                 }
+};
+size_t cond_logical_op_table_len = NUM_ELEMENTS(cond_logical_op_table);
+
+fr_table_num_sorted_t const cond_cmp_op_table[] = {
+       { L("!*"),      T_OP_CMP_FALSE          },
+       { L("!="),      T_OP_NE                 },
+       { L("!~"),      T_OP_REG_NE             },
+       { L(":="),      T_OP_SET                },
+       { L("<"),       T_OP_LT                 },
+       { L("<="),      T_OP_LE                 },
+       { L("="),       T_OP_EQ                 },
+       { L("=*"),      T_OP_CMP_TRUE           },
+       { L("=="),      T_OP_CMP_EQ             },
+       { L("=~"),      T_OP_REG_EQ             },
+       { L(">"),       T_OP_GT                 },
+       { L(">="),      T_OP_GE                 }
+};
+size_t cond_cmp_op_table_len = NUM_ELEMENTS(cond_cmp_op_table);
+
 /*
  *     This file shouldn't use any functions from the server core.
  */
-
-size_t cond_snprint(size_t *need, char *out, size_t outlen, fr_cond_t const *in)
+ssize_t cond_print(fr_sbuff_t *out, fr_cond_t const *in)
 {
-       size_t          len;
-       char            *p = out;
-       char            *end = out + outlen - 1;
-       fr_cond_t const *c = in;
-       size_t          our_need;
-
-       if (!need) need = &our_need;
-
-       RETURN_IF_NO_SPACE_INIT(need, 1, p, out, end);
+       fr_sbuff_t              our_out = FR_SBUFF_NO_ADVANCE(out);
+       fr_cond_t const         *c = in;
 
-next:
-       if (!c) {
-               p[0] = '\0';
-               return 0;
-       }
-
-       if (c->negate) {
-               *(p++) = '!';   /* FIXME: only allow for child? */
-       }
-
-       switch (c->type) {
-       case COND_TYPE_EXISTS:
-               fr_assert(c->data.vpt != NULL);
-               if (c->cast) {
-                       len = snprintf(p, end - p, "<%s>", fr_table_str_by_value(fr_value_box_type_table,
-                                      c->cast->type, "??"));
-                       RETURN_IF_TRUNCATED(need, len, p, out, end);
-               }
+       while (c) {
+               if (c->negate) FR_SBUFF_IN_CHAR_RETURN(&our_out, '!');
 
-               len = tmpl_snprint(need, p, end - p, c->data.vpt);
-               if (*need) return len;
-               p += len;
-               break;
+               switch (c->type) {
+               case COND_TYPE_EXISTS:
+                       fr_assert(c->data.vpt != NULL);
+                       if (c->cast) {
+                               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
+                                                          fr_table_str_by_value(fr_value_box_type_table,
+                                                                                c->cast->type, "??"));
+                       }
+                       FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, c->data.vpt, TMPL_ATTR_REF_PREFIX_YES);
+                       break;
 
-       case COND_TYPE_MAP:
-               fr_assert(c->data.map != NULL);
-#if 0
-               *(p++) = '[';   /* for extra-clear debugging */
-#endif
-               if (c->cast) {
-                       len = snprintf(p, end - p, "<%s>", fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "??"));
-                       RETURN_IF_TRUNCATED(need, len, p, out, end);
-               }
+               case COND_TYPE_RCODE:
+                       fr_assert(c->data.rcode != RLM_MODULE_UNKNOWN);
+                       FR_SBUFF_IN_STRCPY_RETURN(&our_out, fr_table_str_by_value(rcode_table, c->data.rcode, ""));
+                       break;
 
-               len = map_snprint(need, p, end - p, c->data.map);
-               if (*need) return len;
-               p += len;
-#if 0
-               *(p++) = ']';
-#endif
-               break;
+               case COND_TYPE_MAP:
+                       if (c->cast) {
+                               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
+                                                          fr_table_str_by_value(fr_value_box_type_table,
+                                                                                c->cast->type, "??"));
+                       }
+                       FR_SBUFF_RETURN(map_print, &our_out, c->data.map);
+                       break;
 
-       case COND_TYPE_CHILD:
-               fr_assert(c->data.child != NULL);
-               *(p++) = '(';
-               len = cond_snprint(need, p, (end - p) - 1, c->data.child);      /* -1 for proceeding ')' */
-               if (*need) return len;
-               if (len >= (outlen - 1)) return len;
-               p += len;
-               *(p++) = ')';
-               break;
+               case COND_TYPE_CHILD:
+                       FR_SBUFF_IN_CHAR_RETURN(&our_out, '(');
+                       FR_SBUFF_RETURN(cond_print, &our_out, c->data.child);
+                       FR_SBUFF_IN_CHAR_RETURN(&our_out, ')');
+                       break;
 
-       case COND_TYPE_TRUE:
-               len = strlcpy(out, "true", outlen);
-               RETURN_IF_TRUNCATED(need, len, p, out, end);
-               return p - out;
+               case COND_TYPE_TRUE:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "true");
+                       break;
 
-       case COND_TYPE_FALSE:
-               len = strlcpy(out, "false", outlen);
-               RETURN_IF_TRUNCATED(need, len, p, out, end);
-               return p - out;
+               case COND_TYPE_FALSE:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "false");
+                       break;
 
-       default:
-               *out = '\0';
-               return 0;
-       }
+               default:
+                       break;
+               }
 
-       if (c->next_op == COND_NONE) {
-               fr_assert(c->next == NULL);
-               *p = '\0';
-               return p - out;
-       }
+               if (c->next_op == COND_NONE) {
+                       fr_assert(c->next == NULL);
+                       goto done;
+               }
 
-       if (c->next_op == COND_AND) {
-               len = strlcpy(p, " && ", end - p);
-               RETURN_IF_TRUNCATED(need, len, p, out, end);
+               switch (c->next_op) {
+               case COND_AND:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " && ");
+                       break;
 
-       } else if (c->next_op == COND_OR) {
-               len = strlcpy(p, " || ", end - p);
-               RETURN_IF_TRUNCATED(need, len, p, out, end);
+               case COND_OR:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " || ");
+                       break;
 
-       } else {
-               fr_assert(0 == 1);
+               default:
+                       fr_assert(0);
+               }
+               c = c->next;
        }
 
-       c = c->next;
-       goto next;
+done:
+       fr_sbuff_terminate(&our_out);
+       return fr_sbuff_set(out, &our_out);
 }
 
 
@@ -243,7 +247,7 @@ static bool cond_type_check(fr_cond_t *c, fr_type_t lhs_type)
 #define return_SLEN goto return_slen
 
 static ssize_t cond_check_cast(fr_cond_t *c, char const *start,
-                              char const *lhs, char const *rhs, char const **error)
+                              char const *lhs, char const *rhs)
 {
        if (tmpl_is_attr(c->data.map->rhs) &&
            (c->cast->type != tmpl_da(c->data.map->rhs)->type)) {
@@ -251,13 +255,15 @@ static ssize_t cond_check_cast(fr_cond_t *c, char const *start,
                        return 1;
                }
 
-               *error = "Attribute comparisons must be of the same data type";
+               fr_strerror_printf("Cannot compare types '%s' (cast) and '%s' (attr)",
+                                  fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "<INVALID>"),
+                                  fr_table_str_by_value(fr_value_box_type_table, tmpl_da(c->data.map->rhs)->type, "<INVALID>"));
                return 0;
        }
 
 #ifdef HAVE_REGEX
-       if (tmpl_is_regex_unresolved(c->data.map->rhs)) {
-               *error = "Cannot use cast with regex comparison";
+       if (tmpl_contains_regex(c->data.map->rhs)) {
+               fr_strerror_printf("Cannot use cast with regex comparison");
                return -(rhs - start);
        }
 #endif
@@ -268,7 +274,7 @@ static ssize_t cond_check_cast(fr_cond_t *c, char const *start,
         */
        if (tmpl_is_unresolved(c->data.map->lhs) &&
            (tmpl_cast_in_place(c->data.map->lhs, c->cast->type, c->cast) < 0)) {
-               *error = "Failed to parse field";
+               fr_strerror_printf("Failed to parse field");
                return -(lhs - start);
        }
 
@@ -279,7 +285,7 @@ static ssize_t cond_check_cast(fr_cond_t *c, char const *start,
        if ((tmpl_is_data(c->data.map->lhs)) &&
            (tmpl_is_unresolved(c->data.map->rhs)) &&
            (tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast) < 0)) {
-               *error = "Failed to parse field";
+               fr_strerror_printf("Failed to parse field");
                return -(rhs - start);
        }
 
@@ -345,7 +351,7 @@ static ssize_t cond_check_cast(fr_cond_t *c, char const *start,
                 */
                if ((dict_attr_sizes[c->cast->type][1] < dict_attr_sizes[tmpl_da(c->data.map->lhs)->type][0]) ||
                    (dict_attr_sizes[c->cast->type][0] > dict_attr_sizes[tmpl_da(c->data.map->lhs)->type][1])) {
-                       *error = "Cannot cast to attribute of incompatible size";
+                       fr_strerror_printf("Cannot cast to attribute of incompatible size");
                        return 0;
                }
        }
@@ -368,66 +374,82 @@ cast_ok:
 /*
  *     See if two attribute comparisons are OK.
  */
-static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
-                              char const *lhs, fr_token_t lhs_type,
-                               char const *rhs, fr_token_t rhs_type,
-                               char const **error)
+static ssize_t cond_check_attrs(fr_cond_t *c, fr_sbuff_marker_t *m_lhs, fr_sbuff_marker_t *m_rhs)
 {
-       tmpl_t *vpt;
+       tmpl_t  *attr, *data, *xlat, *unresolved, *xlat_unresolved, *exec, *vpt;
+       tmpl_t  *lhs = c->data.map->lhs, *rhs = c->data.map->rhs;
+       fr_token_t      op = c->data.map->op;
+
+/** True if one operand is of _type_a and the other of _type_b
+ */
+#define TMPL_OF_TYPE_A_B(_type_a, _type_b) \
+       ((((tmpl_is_##_type_a(lhs) && (_type_a = lhs)) && (tmpl_is_##_type_b(rhs) && (_type_b = rhs)))) || \
+       (((tmpl_is_##_type_a(rhs) && (_type_a = rhs)) && (tmpl_is_##_type_b(lhs) && (_type_b = lhs)))))
+
+/** True if one operand is of _type_a and the other is not of _type_a
+ *
+ */
+#define TMPL_OF_TYPE_A_NOT_A(_type_a, _out_b) \
+       ((((tmpl_is_##_type_a(lhs) && (_type_a = lhs)) && (!tmpl_is_##_type_a(rhs) && (_out_b = rhs)))) || \
+       (((tmpl_is_##_type_a(rhs) && (_type_a = rhs)) && (!tmpl_is_##_type_a(lhs) && (_out_b = lhs)))))
+
+/** True if both operands are of _type_a
+ *
+ */
+#define TMPL_OF_TYPE_A_A(_type_a) \
+       ((tmpl_is_##_type_a(lhs)) && (tmpl_is_##_type_a(rhs)))
+
+#define TMPL_RETURN(_vpt) return -((_vpt) == lhs ? fr_sbuff_used(m_lhs) : fr_sbuff_used(m_rhs))
 
        /*
-        *      Two attributes?  They must be of the same type
+        *      Attribute comparison with a box
         */
-       if (tmpl_is_attr(c->data.map->rhs) &&
-           tmpl_is_attr(c->data.map->lhs) &&
-           (tmpl_da(c->data.map->lhs)->type != tmpl_da(c->data.map->rhs)->type)) {
-               if (cond_type_check(c, tmpl_da(c->data.map->lhs)->type)) {
-                       return 1;
+       if (TMPL_OF_TYPE_A_B(attr, data)) {
+               fr_type_t               type = tmpl_da(attr)->type;
+
+               /*
+                *      Most of the time the box type takes
+                *      precedence, except in the case of a few types
+                *      like IP prefixes.
+                */
+               switch (tmpl_value_type(data)) {
+               case FR_TYPE_IPV4_PREFIX:
+               case FR_TYPE_IPV6_PREFIX:
+                       type = tmpl_value_type(data);
+                       break;
+
+               default:
+                       break;
                }
 
-               *error = "Attribute comparisons must be of the same data type";
-               return 0;
+               if (tmpl_cast_in_place(data, type, tmpl_da(attr)) < 0) {
+                       fr_strerror_printf_push("Failed casting data to match attribute");
+                       return -(data == lhs ? fr_sbuff_used(m_lhs) : fr_sbuff_used(m_rhs));
+               }
        }
 
        /*
-        *      Invalid: User-Name == bob
-        *      Valid:   User-Name == "bob"
-        *
-        *      There's no real reason for
-        *      this, other than consistency.
+        *      Two attributes?  They must be of the same type
         */
-       if (tmpl_is_attr(c->data.map->lhs) &&
-           !tmpl_is_attr(c->data.map->rhs) &&
-           (tmpl_da(c->data.map->lhs)->type == FR_TYPE_STRING) &&
-           (c->data.map->op != T_OP_CMP_TRUE) &&
-           (c->data.map->op != T_OP_CMP_FALSE) &&
-           (c->data.map->rhs->quote == T_BARE_WORD)) {
-               return_rhs("Comparison value must be a quoted string");
-       }
+       if (TMPL_OF_TYPE_A_A(attr) && (tmpl_da(lhs)->type != tmpl_da(rhs)->type)) {
+               if (cond_type_check(c, tmpl_da(lhs)->type)) return 1;   /* Or be mungeable to the same type */
 
-       /*
-        *      Quotes around non-string
-        *      attributes mean that it's
-        *      either xlat, or an exec.
-        */
-       if (tmpl_is_attr(c->data.map->lhs) &&
-           !tmpl_is_attr(c->data.map->rhs) &&
-           (tmpl_da(c->data.map->lhs)->type != FR_TYPE_STRING) &&
-           (tmpl_da(c->data.map->lhs)->type != FR_TYPE_OCTETS) &&
-           (tmpl_da(c->data.map->lhs)->type != FR_TYPE_DATE) &&
-           (rhs_type == T_SINGLE_QUOTED_STRING)) {
-               *error = "Comparison value must be an unquoted string";
-       return_rhs:
-               return -(rhs - start);
+               fr_strerror_printf("Cannot compare attributes of type '%s' and '%s'",
+                                  fr_table_str_by_value(fr_value_box_type_table, tmpl_da(lhs)->type, "<INVALID>"),
+                                  fr_table_str_by_value(fr_value_box_type_table, tmpl_da(rhs)->type, "<INVALID>"));
+               return 0;
        }
 
        /*
         *      The LHS has been cast to a data type, and the RHS is a
         *      literal.  Cast the RHS to the type of the cast.
+        *
+        *      FIXME - We should revisit this when RHS casting is supported.
         */
-       if (c->cast && tmpl_is_unresolved(c->data.map->rhs) &&
-           (tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast) < 0)) {
-               return_rhs("Failed to parse field");
+       if (c->cast && tmpl_is_unresolved(rhs) &&
+           (tmpl_cast_in_place(rhs, c->cast->type, c->cast) < 0)) {
+               fr_strerror_printf("Failed to parse field");
+               return -fr_sbuff_used(m_rhs);
        }
 
        /*
@@ -439,21 +461,38 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
         *
         *      This allows Framed-IP-Address < 192.168.0.0./24
         */
-       if (tmpl_is_attr(c->data.map->lhs) &&
-           (tmpl_is_unresolved(c->data.map->rhs) ||
-            tmpl_is_data(c->data.map->rhs))) {
-               fr_type_t type = tmpl_da(c->data.map->lhs)->type;
+       unresolved = NULL;
+       if (TMPL_OF_TYPE_A_B(attr, unresolved) || TMPL_OF_TYPE_A_B(attr, data)) {
+               fr_type_t type = tmpl_da(attr)->type;
+
+               vpt = unresolved ? unresolved : data;
+
+               /*
+                *      Invalid: User-Name == bob
+                *      Valid:   User-Name == "bob"
+                *
+                *      There's no real reason for
+                *      this, other than consistency.
+                */
+               if ((unresolved || (data && tmpl_value_type(data) == FR_TYPE_STRING)) &&
+                   (tmpl_da(attr)->type == FR_TYPE_STRING) &&
+                   (op != T_OP_CMP_TRUE) &&
+                   (op != T_OP_CMP_FALSE) &&
+                   (vpt->quote == T_BARE_WORD)) {
+                       fr_strerror_printf("Comparison value must be a quoted string");
+                       TMPL_RETURN(vpt);
+               }
 
-               switch (tmpl_da(c->data.map->lhs)->type) {
+               switch (tmpl_da(attr)->type) {
                case FR_TYPE_IPV4_ADDR:
-                       if (strchr(c->data.map->rhs->name, '/') != NULL) {
+                       if (strchr(vpt->name, '/') != NULL) {
                                type = FR_TYPE_IPV4_PREFIX;
                                c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_CAST_BASE + type);
                        }
                        break;
 
                case FR_TYPE_IPV6_ADDR:
-                       if (strchr(c->data.map->rhs->name, '/') != NULL) {
+                       if (strchr(vpt->name, '/') != NULL) {
                                type = FR_TYPE_IPV6_PREFIX;
                                c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_CAST_BASE + type);
                        }
@@ -466,12 +505,12 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
                /*
                 *      Do not pass LHS as enumv if we're casting
                 *      as that means there's now a type mismatch between
-                *      lhs and rhs, which means the enumerations
+                *      attr and vpt, which means the enumerations
                 *      can never match.
                 */
-               if (tmpl_cast_in_place(c->data.map->rhs, type,
-                                      c->cast ? NULL : tmpl_da(c->data.map->lhs)) < 0) {
-                       fr_dict_attr_t const *da = tmpl_da(c->data.map->lhs);
+               if (tmpl_cast_in_place(vpt, type,
+                                      c->cast ? NULL : tmpl_da(attr)) < 0) {
+                       fr_dict_attr_t const *da = tmpl_da(attr);
 
                        switch (da->attr) {
                        case FR_AUTH_TYPE:
@@ -483,17 +522,19 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
                                break;
 
                        default:
-                               if (!c->data.map->lhs->data.attribute.was_oid) {
-                                       return_rhs("Failed to parse value for attribute");
+                               if (!attr->data.attribute.was_oid) {
+                                       fr_strerror_printf("Failed to parse value for attribute");
+                                       TMPL_RETURN(vpt);
                                }
                                /*
-                                *      Convert the lhs to a raw type and
+                                *      Convert the attr to a raw type and
                                 *      try the cast again.
                                 */
-                               tmpl_attr_to_raw(c->data.map->lhs);
-                               if (tmpl_cast_in_place(c->data.map->rhs, tmpl_da(c->data.map->lhs)->type,
-                                                      c->cast ? NULL : tmpl_da(c->data.map->lhs)) < 0) {
-                                       return_rhs("Failed to parse value for attribute");
+                               tmpl_attr_to_raw(attr);
+                               if (tmpl_cast_in_place(vpt, tmpl_da(attr)->type,
+                                                      c->cast ? NULL : tmpl_da(attr)) < 0) {
+                                       fr_strerror_printf("Failed to parse value for attribute");
+                                       TMPL_RETURN(vpt);
                                }
                                break;
                        }
@@ -504,28 +545,33 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
                 *      Cast the LHS to the
                 *      type of the RHS.
                 */
-               if (tmpl_da(c->data.map->lhs)->type == FR_TYPE_COMBO_IP_ADDR) {
-                       if (tmpl_attr_abstract_to_concrete(c->data.map->lhs,
-                                                              tmpl_value_type(c->data.map->rhs)) < 0) {
-                               return_rhs("Cannot find type for attribute");
+               if (tmpl_da(attr)->type == FR_TYPE_COMBO_IP_ADDR) {
+                       if (tmpl_attr_abstract_to_concrete(attr, tmpl_value_type(vpt)) < 0) {
+                               fr_strerror_printf("Cannot find type for attribute");
+                               TMPL_RETURN(attr);
                        }
                }
        } /* attr to literal comparison */
 
+       /*
+        *      If one side is unresolved, and the other is data,
+        *      we can use the data type to attempt a cast on the
+        *      unresolved side.
+        */
+       if (TMPL_OF_TYPE_A_B(data, unresolved) &&
+           (tmpl_cast_in_place(unresolved, tmpl_value_type(data), NULL) < 0)) TMPL_RETURN(unresolved);
+
        /*
         *      The RHS will turn into... something.  Allow for prefixes
         *      there, too.
         */
-       if (tmpl_is_attr(c->data.map->lhs) &&
-           (tmpl_is_xlat_unresolved(c->data.map->rhs) ||
-            tmpl_is_xlat(c->data.map->rhs) ||
-            tmpl_is_exec(c->data.map->rhs))) {
-               if (tmpl_da(c->data.map->lhs)->type == FR_TYPE_IPV4_ADDR) {
+       if (TMPL_OF_TYPE_A_B(attr, xlat_unresolved) || TMPL_OF_TYPE_A_B(attr, xlat) || TMPL_OF_TYPE_A_B(attr, exec)) {
+               if (tmpl_da(attr)->type == FR_TYPE_IPV4_ADDR) {
                        c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
                                                            FR_CAST_BASE + FR_TYPE_IPV4_PREFIX);
                }
 
-               if (tmpl_da(c->data.map->lhs)->type == FR_TYPE_IPV6_ADDR) {
+               if (tmpl_da(attr)->type == FR_TYPE_IPV6_ADDR) {
                        c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
                                                            FR_CAST_BASE + FR_TYPE_IPV6_PREFIX);
                }
@@ -541,7 +587,7 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
         *      and do no parsing until after all of the modules
         *      are loaded.  But that has issues, too.
         */
-       if (tmpl_is_unresolved(c->data.map->lhs) && (lhs_type == T_BARE_WORD)) {
+       if (tmpl_is_unresolved(lhs) && (lhs->quote == T_BARE_WORD)) {
                int hyphens = 0;
                bool may_be_attr = true;
                size_t i;
@@ -551,13 +597,13 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
                 *      Backwards compatibility: Allow Foo-Bar,
                 *      e.g. LDAP-Group and SQL-Group.
                 */
-               for (i = 0; i < c->data.map->lhs->len; i++) {
-                       if (!fr_dict_attr_allowed_chars[(uint8_t) c->data.map->lhs->name[i]]) {
+               for (i = 0; i < lhs->len; i++) {
+                       if (!fr_dict_attr_allowed_chars[(uint8_t) lhs->name[i]]) {
                                may_be_attr = false;
                                break;
                        }
 
-                       if (c->data.map->lhs->name[i] == '-') {
+                       if (lhs->name[i] == '-') {
                                hyphens++;
                        }
                }
@@ -565,14 +611,13 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
                if (!hyphens || (hyphens > 3)) may_be_attr = false;
 
                if (may_be_attr) {
-                       attr_slen = tmpl_afrom_attr_str(c->data.map, NULL, &vpt, lhs,
+                       attr_slen = tmpl_afrom_attr_str(c->data.map, NULL, &vpt, fr_sbuff_current(m_lhs),
                                                        &(tmpl_rules_t){
                                                                .allow_unknown = true,
                                                                .allow_unresolved = true
                                                        });
-                       if ((attr_slen > 0) && (vpt->len == c->data.map->lhs->len)) {
-                               talloc_free(c->data.map->lhs);
-                               c->data.map->lhs = vpt;
+                       if ((attr_slen > 0) && (vpt->len == lhs->len)) {
+                               talloc_free(lhs);
                                c->pass2_fixup = PASS2_FIXUP_ATTR;
                        }
                }
@@ -584,6 +629,7 @@ static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
 /*
  *     Like tmpl_preparse(), but expands variables.
  */
+#if 0
 static ssize_t cond_preparse(TALLOC_CTX *ctx, char const **out, size_t *outlen, char const *in, size_t inlen,
                             fr_token_t *type, char const **error,
                             fr_dict_attr_t const **castda, bool require_regex,
@@ -601,7 +647,7 @@ static ssize_t cond_preparse(TALLOC_CTX *ctx, char const **out, size_t *outlen,
         *      conditions don't use filenames.
         */
        if (!require_regex && (*in == '/')) {
-               *error = "Unexpected regular expression";
+               fr_strerror_printf("Unexpected regular expression");
                return 0;
        }
 
@@ -622,7 +668,7 @@ static ssize_t cond_preparse(TALLOC_CTX *ctx, char const **out, size_t *outlen,
 
        if (!cf_expand_variables(cf_filename(cs), cf_lineno(cs), cf_item_to_section(cf_parent(cs)),
                                 buffer, sizeof(buffer), in, slen, NULL)) {
-               *error = "Failed expanding configuration variable";
+               fr_strerror_printf("Failed expanding configuration variable");
                return -1;
        }
 
@@ -642,979 +688,1040 @@ static ssize_t cond_preparse(TALLOC_CTX *ctx, char const **out, size_t *outlen,
         */
        expanded = talloc_strndup(ctx, *out, *outlen);
        if (!expanded) {
-               *error = "Failed allocating memory";
+               fr_strerror_printf("Failed allocating memory");
                return -1;
        }
 
        *out = expanded;
        return slen;            /* NOT my_slen */
 }
+#endif
 
-
-/** Tokenize a conditional check
- *
- *  @param[in] ctx     talloc ctx
- *  @param[in] cs      our configuration section
- *  @param[out] pcond  pointer to the returned condition structure
- *  @param[out] error  the parse error (if any)
- *  @param[in] in      the start of the string to process.  Should be "(..."
- *  @param[in] inlen   the length of the string to process
- *  @param[in] brace   look for a closing brace (how many deep we are)
- *  @param[in] rules   for attribute parsing
- *  @return
- *     - Length of the string skipped.
- *     - < 0 (the offset to the offending error) on error.
- */
-static ssize_t cond_tokenize(TALLOC_CTX *ctx, CONF_SECTION *cs,
-                            fr_cond_t **pcond, char const **error,
-                            char const *in, size_t inlen, int brace,
-                            tmpl_rules_t const *rules)
+static int cond_normalise(TALLOC_CTX *ctx, fr_token_t lhs_type, fr_cond_t **c_out)
 {
-       ssize_t                 slen, tlen;
-       char const              *p = in, *end = in + inlen;
-       char const              *lhs, *rhs;
-       fr_cond_t               *c;
-       size_t                  lhs_len, rhs_len;
-       fr_token_t              op, lhs_type, rhs_type;
-       bool                    regex = false;
-       tmpl_rules_t            parse_rules;
+       fr_cond_t *c = *c_out;
 
        /*
-        *      We allow unknown and undefined attributes here
+        *      Normalize the condition before returning.
+        *
+        *      We collapse multiple levels of braces to one.  Then
+        *      convert maps to literals.  Then literals to true/false
+        *      statements.  Then true/false ||/&& followed by other
+        *      conditions to just conditions.
+        *
+        *      Order is important.  The more complex cases are
+        *      converted to simpler ones, from the most complex cases
+        *      to the simplest ones.
         */
-       parse_rules = *rules;
-       parse_rules.allow_unknown = true;
-       parse_rules.allow_unresolved = true;
-
-       c = talloc_zero(ctx, fr_cond_t);
-
-       fr_assert(c != NULL);
-       lhs_type = rhs_type = T_INVALID;
-
-       fr_skip_whitespace(p);
-
-       if (!*p) {
-               return_P("Empty condition is invalid");
-       }
 
        /*
-        *      !COND
+        *      (FOO)     --> FOO
+        *      (FOO) ... --> FOO ...
         */
-       if (*p == '!') {
-               p++;
-               c->negate = true;
-               fr_skip_whitespace(p);
+       if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
+               fr_cond_t *child;
 
-               /*
-                *  Just for stupidity
-                */
-               if (*p == '!') {
-                       return_P("Double negation is invalid");
-               }
-       }
+               child = talloc_steal(ctx, c->data.child);
+               c->data.child = NULL;
 
-       /*
-        *      (COND)
-        */
-       if (*p == '(') {
-               p++;
+               child->next = talloc_steal(child, c->next);
+               c->next = NULL;
+
+               child->next_op = c->next_op;
 
                /*
-                *      We've already eaten one layer of
-                *      brackets.  Go recurse to get more.
+                *      Set the negation properly
                 */
-               c->type = COND_TYPE_CHILD;
-               c->ci = cf_section_to_item(cs);
-               slen = cond_tokenize(c, cs, &c->data.child, error, p, end - p, brace + 1, rules);
-               if (slen <= 0) return_SLEN;
-
-               if (!c->data.child) {
-                       return_P("Empty condition is invalid");
+               if ((c->negate && !child->negate) ||
+                   (!c->negate && child->negate)) {
+                       child->negate = true;
+               } else {
+                       child->negate = false;
                }
 
-               p += slen;
-               fr_skip_whitespace(p);
-               goto closing_brace;
+               talloc_free(c);
+               c = child;
        }
 
        /*
-        *      We didn't see anything special.  The condition must be one of
+        *      (FOO ...) --> FOO ...
         *
-        *      FOO
-        *      FOO OP BAR
-        */
-
-       /*
-        *      Grab the LHS
-        */
-       slen = cond_preparse(c, &lhs, &lhs_len, p, end - p, &lhs_type, error, &c->cast, false, cs);
-       if (slen <= 0) {
-               return_SLEN;
-       }
-
-       /*
-        *      We may (or not) have an operator
+        *      But don't do !(FOO || BAR) --> !FOO || BAR
+        *      Because that's different.
         */
+       if ((c->type == COND_TYPE_CHILD) &&
+           !c->next && !c->negate) {
+               fr_cond_t *child;
 
-       /*
-        *      If the LHS is 0xabcdef, then automatically case it to octets.
-        */
-       if (!c->cast && (lhs_type == T_BARE_WORD) &&
-           (lhs[0] == '0') && (lhs[1] == 'x') &&
-           ((slen & 0x01) == 0)) {
-               if (slen == 2) {
-                       return_P("Empty octet string is invalid");
-               }
+               child = talloc_steal(ctx, c->data.child);
+               c->data.child = NULL;
 
-               c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
-                                                           FR_CAST_BASE + FR_TYPE_OCTETS);
+               talloc_free(c);
+               c = child;
        }
 
-       p += slen;
-       fr_skip_whitespace(p);
-
        /*
-        *      (FOO)
+        *      Convert maps to literals.  Convert one form of map to
+        *      a standardized form.  This doesn't make any
+        *      theoretical difference, but it does mean that the
+        *      run-time evaluation has fewer cases to check.
         */
-       if (*p == ')') {
-               if (slen == 0) {
-                       return_P("Empty string is invalid");
+       if (c->type == COND_TYPE_MAP) do {
+               /*
+                *      !FOO !~ BAR --> FOO =~ BAR
+                */
+               if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
+                       c->negate = false;
+                       c->data.map->op = T_OP_REG_EQ;
                }
 
                /*
-                *      don't skip the brace.  We'll look for it later.
+                *      FOO !~ BAR --> !FOO =~ BAR
                 */
-               goto exists;
+               if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
+                       c->negate = true;
+                       c->data.map->op = T_OP_REG_EQ;
+               }
 
                /*
-                *      FOO
+                *      !FOO != BAR --> FOO == BAR
                 */
-       } else if (!*p) {
-               if (brace) {
-                       return_P("No closing brace at end of string");
+               if (c->negate && (c->data.map->op == T_OP_NE)) {
+                       c->negate = false;
+                       c->data.map->op = T_OP_CMP_EQ;
                }
 
-               goto exists;
-
                /*
-                *      FOO && ...
+                *      This next one catches "LDAP-Group != foo",
+                *      which doesn't work as-is, but this hack fixes
+                *      it.
+                *
+                *      FOO != BAR --> !FOO == BAR
                 */
-       } else if (((p[0] == '&') && (p[1] == '&')) ||
-                  ((p[0] == '|') && (p[1] == '|'))) {
-
-       exists:
-               if (c->cast) {
-                       *error = "Cannot do cast for existence check";
-               return_0:
-                       talloc_free(c);
-                       return 0;
+               if (!c->negate && (c->data.map->op == T_OP_NE)) {
+                       c->negate = true;
+                       c->data.map->op = T_OP_CMP_EQ;
                }
 
-               c->type = COND_TYPE_EXISTS;
-               c->ci = cf_section_to_item(cs);
+               /*
+                *      FOO =* BAR --> FOO
+                *      FOO !* BAR --> !FOO
+                *
+                *      FOO may be a string, or a delayed attribute
+                *      reference.
+                */
+               if ((c->data.map->op == T_OP_CMP_TRUE) ||
+                   (c->data.map->op == T_OP_CMP_FALSE)) {
+                       tmpl_t *vpt;
 
-               tlen = tmpl_afrom_str(c, &c->data.vpt,
-                                     lhs, lhs_len, lhs_type, &parse_rules, true);
-               if (tlen < 0) {
-                       p = lhs - tlen;
-                       return_P(fr_strerror());
-               }
+                       vpt = talloc_steal(c, c->data.map->lhs);
+                       c->data.map->lhs = NULL;
 
-               fr_assert(!tmpl_is_regex_unresolved(c->data.vpt));
+                       /*
+                        *      Invert the negation bit.
+                        */
+                       if (c->data.map->op == T_OP_CMP_FALSE) {
+                               c->negate = !c->negate;
+                       }
 
-               if (tmpl_unknown_attr_add(c->data.vpt) < 0) {
-                       p = lhs - tlen;
-                       return_P("Failed defining attribute");
-               }
+                       TALLOC_FREE(c->data.map);
 
-               if (tmpl_is_attr_unresolved(c->data.vpt)) {
-                       c->pass2_fixup = PASS2_FIXUP_ATTR;
+                       c->type = COND_TYPE_EXISTS;
+                       c->data.vpt = vpt;
+                       break;  /* it's no longer a map */
                }
 
-       } else { /* it's an operator */
-               vp_map_t *map;
-
                /*
-                *      The next thing should now be a comparison operator.
+                *      Both are data (IP address, integer, etc.)
+                *
+                *      We can do the evaluation here, so that it
+                *      doesn't need to be done at run time
                 */
-               c->type = COND_TYPE_MAP;
-               c->ci = cf_section_to_item(cs);
-
-               switch (*p) {
-               default:
-                       return_P("Invalid text. Expected comparison operator");
-
-               case '!':
-                       if (p[1] == '=') {
-                               op = T_OP_NE;
-                               p += 2;
-
-#ifdef HAVE_REGEX
-                       } else if (p[1] == '~') {
-                               regex = true;
-
-                               op = T_OP_REG_NE;
-                               p += 2;
-#endif
-
-                       } else if (p[1] == '*') {
-                               if (lhs_type != T_BARE_WORD) {
-                                       return_P("Cannot use !* on a string");
-                               }
-
-                               op = T_OP_CMP_FALSE;
-                               p += 2;
+               if (tmpl_is_data(c->data.map->lhs) &&
+                   tmpl_is_data(c->data.map->rhs)) {
+                       int rcode;
 
+                       rcode = cond_eval_map(NULL, 0, c);
+                       TALLOC_FREE(c->data.map);
+                       c->cast = NULL;
+                       if (rcode) {
+                               c->type = COND_TYPE_TRUE;
                        } else {
-                               goto invalid_operator;
+                               c->type = COND_TYPE_FALSE;
                        }
-                       break;
-
-               case '=':
-                       if (p[1] == '=') {
-                               op = T_OP_CMP_EQ;
-                               p += 2;
-
-#ifdef HAVE_REGEX
-                       } else if (p[1] == '~') {
-                               regex = true;
-
-                               op = T_OP_REG_EQ;
-                               p += 2;
-#endif
 
-                       } else if (p[1] == '*') {
-                               if (lhs_type != T_BARE_WORD) {
-                                       return_P("Cannot use =* on a string");
-                               }
-
-                               op = T_OP_CMP_TRUE;
-                               p += 2;
-
-                       } else {
-                       invalid_operator:
-                               return_P("Invalid operator");
-                       }
+                       break;  /* it's no longer a map */
+               }
 
-                       break;
+               /*
+                *      Both are literal strings.  They're not parsed
+                *      as TMPL_TYPE_DATA because there's no cast to an
+                *      attribute.
+                *
+                *      We can do the evaluation here, so that it
+                *      doesn't need to be done at run time
+                */
+               if (tmpl_is_unresolved(c->data.map->rhs) &&
+                   tmpl_is_unresolved(c->data.map->lhs) &&
+                   !c->pass2_fixup) {
+                       int rcode;
 
-               case '<':
-                       if (p[1] == '=') {
-                               op = T_OP_LE;
-                               p += 2;
+                       fr_assert(c->cast == NULL);
 
+                       rcode = cond_eval_map(NULL, 0, c);
+                       if (rcode) {
+                               c->type = COND_TYPE_TRUE;
                        } else {
-                               op = T_OP_LT;
-                               p++;
+                               DEBUG4("OPTIMIZING (%s %s %s) --> FALSE",
+                                      c->data.map->lhs->name,
+                                      fr_table_str_by_value(fr_tokens_table, c->data.map->op, "??"),
+                                      c->data.map->rhs->name);
+                               c->type = COND_TYPE_FALSE;
                        }
-                       break;
 
-               case '>':
-                       if (p[1] == '=') {
-                               op = T_OP_GE;
-                               p += 2;
-
-                       } else {
-                               op = T_OP_GT;
-                               p++;
-                       }
+                       /*
+                        *      Free map after using it above.
+                        */
+                       TALLOC_FREE(c->data.map);
                        break;
                }
 
-               fr_skip_whitespace(p);
-
-               if (!*p) {
-                       return_P("Expected text after operator");
-               }
-
-               slen = cond_preparse(c, &rhs, &rhs_len, p, end - p, &rhs_type, error, NULL, regex, cs);
-               if (slen <= 0) {
-                       return_SLEN;
-               }
-
                /*
-                *      Duplicate map_from_fields here, as we
-                *      want to separate parse errors in the
-                *      LHS from ones in the RHS.
+                *      <ipaddr>"foo" CMP &Attribute-Name The cast may
+                *      not be necessary, and we can re-write it so
+                *      that the attribute reference is on the LHS.
                 */
-               c->data.map = map = talloc_zero(c, vp_map_t);
+               if (c->cast &&
+                   tmpl_is_attr(c->data.map->rhs) &&
+                   (c->cast->type == tmpl_da(c->data.map->rhs)->type) &&
+                   !tmpl_is_attr(c->data.map->lhs)) {
+                       tmpl_t *tmp;
 
-               tlen = tmpl_afrom_str(map, &map->lhs, lhs, lhs_len,
-                                     lhs_type, &parse_rules, true);
-               if (tlen < 0) {
-                       p = lhs - tlen;
-                       return_P(fr_strerror());
-               }
+                       tmp = c->data.map->rhs;
+                       c->data.map->rhs = c->data.map->lhs;
+                       c->data.map->lhs = tmp;
 
-               if (tmpl_unknown_attr_add(map->lhs) < 0) {
-                       *error = "Failed defining attribute";
-               return_lhs:
-                       talloc_free(c);
-                       return -(lhs - in);
-               }
+                       c->cast = NULL;
 
-               map->op = op;
+                       switch (c->data.map->op) {
+                       case T_OP_CMP_EQ:
+                               /* do nothing */
+                               break;
 
-               /*
-                *      This code converts '&Attr-1.2.3.4 == 0xabcdef'
-                *      into "&Known-Attribure == value"
-                *
-                *      So that the debug output makes more sense, AND
-                *      so that the comparisons are done to known attributes.
-                */
-               if (tmpl_is_attr(map->lhs) &&
-                   (tmpl_da(map->lhs)->flags.is_raw || map->lhs->data.attribute.was_oid) &&
-                   map_cast_from_hex(map, rhs_type, rhs)) {
-                       /* do nothing */
+                       case T_OP_LE:
+                               c->data.map->op = T_OP_GE;
+                               break;
 
-               } else {
-                       /*
-                        *      If the RHS is 0xabcdef... automatically cast it to octets
-                        *      unless the LHS is an attribute of type octets, or an
-                        *      integer type.
-                        */
-                       if (!c->cast && (rhs_type == T_BARE_WORD) &&
-                           (rhs[0] == '0') && (rhs[1] == 'x') &&
-                           ((slen & 0x01) == 0)) {
-                               if (slen == 2) {
-                                       return_P("Empty octet string is invalid");
-                               }
+                       case T_OP_LT:
+                               c->data.map->op = T_OP_GT;
+                               break;
 
-                               if ((map->lhs->type != TMPL_TYPE_ATTR) ||
-                                   !((tmpl_da(map->lhs)->type == FR_TYPE_OCTETS) ||
-                                     (tmpl_da(map->lhs)->type == FR_TYPE_UINT8) ||
-                                     (tmpl_da(map->lhs)->type == FR_TYPE_UINT16) ||
-                                     (tmpl_da(map->lhs)->type == FR_TYPE_UINT32) ||
-                                     (tmpl_da(map->lhs)->type == FR_TYPE_UINT64))) {
-                                       c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
-                                                                           FR_CAST_BASE + FR_TYPE_OCTETS);
-                               }
-                       }
+                       case T_OP_GE:
+                               c->data.map->op = T_OP_LE;
+                               break;
 
-                       tlen = tmpl_afrom_str(map, &map->rhs, rhs, rhs_len, rhs_type,
-                                             &parse_rules, true);
-                       if (tlen < 0) {
-                               p = rhs - tlen;
-                               return_P(fr_strerror());
+                       case T_OP_GT:
+                               c->data.map->op = T_OP_LT;
+                               break;
+
+                       default:
+                               fr_strerror_printf("Internal sanity check failed 1");
+                               return -1;
                        }
-               }
 
-               if (tmpl_unknown_attr_add(map->rhs) < 0) {
-                       *error = "Failed defining attribute";
-               return_rhs:
-                       talloc_free(c);
-                       return -(rhs - in);
+                       /*
+                        *      This must have been parsed into TMPL_TYPE_DATA.
+                        */
+                       fr_assert(!tmpl_is_unresolved(c->data.map->rhs));
                }
 
-               /*
-                *      Unknown attributes get marked up for pass2.
-                */
-               if (tmpl_is_attr_unresolved(c->data.map->lhs) ||
-                   tmpl_is_attr_unresolved(c->data.map->rhs)) {
-                       c->pass2_fixup = PASS2_FIXUP_ATTR;
-               }
+       } while (0);
+
+       /*
+        *      Existence checks.  We short-circuit static strings,
+        *      too.
+        *
+        *      FIXME: the data types should be in the template, too.
+        *      So that we know where a literal came from.
+        *
+        *      "foo" is NOT the same as 'foo' or a bare foo.
+        */
+       if (c->type == COND_TYPE_EXISTS) {
+               switch (c->data.vpt->type) {
+               case TMPL_TYPE_XLAT:
+               case TMPL_TYPE_XLAT_UNRESOLVED:
+               case TMPL_TYPE_ATTR:
+               case TMPL_TYPE_ATTR_UNRESOLVED:
+               case TMPL_TYPE_LIST:
+               case TMPL_TYPE_EXEC:
+                       break;
 
-#ifdef HAVE_REGEX
                /*
-                *      Parse the regex flags
+                *      'true' and 'false' are special strings
+                *      which mean themselves.
+                *
+                *      For integers, 0 is false, all other
+                *      integers are true.
+                *
+                *      For strings, '' and "" are false.
+                *      'foo' and "foo" are true.
+                *
+                *      The str2tmpl function takes care of
+                *      marking "%{foo}" as TMPL_TYPE_XLAT_UNRESOLVED, so
+                *      the strings here are fixed at compile
+                *      time.
+                *
+                *      `exec` and "%{...}" are left alone.
+                *
+                *      Bare words must be module return
+                *      codes.
                 */
-               if (regex) {
-                       int     err;
-                       ssize_t flen;
-                       fr_regex_flags_t        regex_flags;
+               case TMPL_TYPE_UNRESOLVED:
+                       if (!*c->data.vpt->name) {
+                               c->type = COND_TYPE_FALSE;
+                               TALLOC_FREE(c->data.vpt);
 
-                       memset(&regex_flags, 0, sizeof(regex_flags));
+                       } else if ((lhs_type == T_SINGLE_QUOTED_STRING) ||
+                                  (lhs_type == T_DOUBLE_QUOTED_STRING)) {
+                               c->type = COND_TYPE_TRUE;
+                               TALLOC_FREE(c->data.vpt);
 
-                       if (!tmpl_is_regex_unresolved(c->data.map->rhs)) {
-                               return_rhs("Expected regex");
-                       }
+                       } else if (lhs_type == T_BARE_WORD) {
+                               int rcode;
+                               bool zeros = true;
+                               char const *q;
+
+                               for (q = c->data.vpt->name;
+                                    *q != '\0';
+                                    q++) {
+                                       if (!isdigit((int) *q)) {
+                                               break;
+                                       }
+                                       if (*q != '0') zeros = false;
+                               }
 
-                       /*
-                        *      Parse the flags after the *input* string, as "rhs" may have been
-                        *      dynamically expanded.
-                        */
-                       flen = regex_flags_parse(&err, &regex_flags, p + slen, strlen(p + slen), true);
-                       switch (err) {
                                /*
-                                *      Got flags all the way to the end of the string
+                                *      It's all digits, and therefore
+                                *      'false' if zero, and 'true' otherwise.
                                 */
-                       case 0:
-                               fr_assert(flen >= 0);
-                               slen += (size_t)flen;
-                               break;
+                               if (!*q) {
+                                       if (zeros) {
+                                               c->type = COND_TYPE_FALSE;
+                                       } else {
+                                               c->type = COND_TYPE_TRUE;
+                                       }
+                                       TALLOC_FREE(c->data.vpt);
+                                       break;
+                               }
 
                                /*
-                                *      Found non-flag, this is OK.
+                                *      Allow &Foo-Bar where Foo-Bar is an attribute
+                                *      defined by a module.
                                 */
-                       case -1:
-                               fr_assert(flen <= 0);
-                               fr_strerror(); /* Clear out the error buffer */
-                               slen += (size_t)(-flen);
-                               break;
+                               if (c->pass2_fixup == PASS2_FIXUP_ATTR) {
+                                       break;
+                               }
 
-                       case -2:
-                               fr_assert(flen <= 0);
-                               p = rhs + rhs_len + 1;
-                               p += (size_t)(-flen);
-                               return_P("Duplicate flag");
+                               rcode = fr_table_value_by_str(allowed_return_codes, c->data.vpt->name, 0);
+                               if (!rcode) {
+                                       fr_strerror_printf("Expected a module return code");
+                                       return -1;
+                               }
                        }
 
-                       tmpl_regex_flags(c->data.map->rhs) = regex_flags;
-               }
-#endif
+                       /*
+                        *      Else lhs_type==T_INVALID, and this
+                        *      node was made by promoting a child
+                        *      which had already been normalized.
+                        */
+                       break;
 
-               /*
-                *      Save the current config section for later.
-                */
-               c->data.map->ci = cf_section_to_item(cs);
+               case TMPL_TYPE_DATA:
+               {
+                       fr_value_box_t res;
 
-               /*
-                *      We cannot compare lists to anything.
-                */
-               if (tmpl_is_list(c->data.map->lhs)) {
-                       return_lhs("Cannot use list references in condition");
+                       if (fr_value_box_cast(NULL, &res, FR_TYPE_BOOL, NULL, tmpl_value(c->data.vpt)) < 0) return -1;
+                       c->type = res.vb_bool ? COND_TYPE_TRUE : COND_TYPE_FALSE;
+                       TALLOC_FREE(c->data.vpt);
                }
+                       break;
 
-               if (tmpl_is_list(c->data.map->rhs)) {
-                       return_rhs("Cannot use list references in condition");
+               default:
+                       fr_assert_fail("Internal sanity check failed 2");
+                       return -1;
                }
+       }
 
-               /*
-                *      Check cast type.  We can have the RHS
-                *      a string if the LHS has a cast.  But
-                *      if the RHS is an attr, it MUST be the
-                *      same type as the LHS.
-                */
-               if (c->cast) {
-                       tlen = cond_check_cast(c, in, lhs, rhs, error);
-               } else {
-                       tlen = cond_check_attrs(c, in, lhs, lhs_type, rhs, rhs_type, error);
-               }
-               if (tlen <= 0) {
-                       talloc_free(c);
-                       return tlen;
+       /*
+        *      !TRUE -> FALSE
+        */
+       if (c->type == COND_TYPE_TRUE) {
+               if (c->negate) {
+                       c->negate = false;
+                       c->type = COND_TYPE_FALSE;
                }
+       }
 
-               p += slen;
-               fr_skip_whitespace(p);
-       } /* parse OP RHS */
-
-closing_brace:
        /*
-        *      ...COND)
+        *      !FALSE -> TRUE
         */
-       if (*p == ')') {
-               if (!brace) {
-                       return_P("Unexpected closing brace");
+       if (c->type == COND_TYPE_FALSE) {
+               if (c->negate) {
+                       c->negate = false;
+                       c->type = COND_TYPE_TRUE;
                }
-
-               p++;
-               fr_skip_whitespace(p);
-               goto done;
        }
 
        /*
-        *      End of string is allowed, unless we're still looking
-        *      for closing braces.
+        *      true && FOO --> FOO
         */
-       if (!*p) {
-               if (brace) {
-                       return_P("No closing brace at end of string");
-               }
+       if ((c->type == COND_TYPE_TRUE) &&
+           (c->next_op == COND_AND)) {
+               fr_cond_t *next;
 
-               goto done;
+               next = talloc_steal(ctx, c->next);
+               c->next = NULL;
+
+               talloc_free(c);
+               c = next;
        }
 
        /*
-        *      We've parsed all of the condition, stop.
+        *      false && FOO --> false
         */
-       if (brace == 0) {
-               if (isspace((int) *p)) goto done;
+       if ((c->type == COND_TYPE_FALSE) &&
+           (c->next_op == COND_AND)) {
 
-               /*
-                *      Open a section, it's OK to be done.
-                */
-               if (*p == '{') goto done;
+               talloc_free(c->next);
+               c->next = NULL;
+               c->next_op = COND_NONE;
        }
 
        /*
-        *      Allow ((a == b) && (b == c))
+        *      false || FOO --> FOO
         */
-       if (!(((p[0] == '&') && (p[1] == '&')) ||
-             ((p[0] == '|') && (p[1] == '|')))) {
-               *error = "Unexpected text after condition";
-       return_p:
+       if ((c->type == COND_TYPE_FALSE) &&
+           (c->next_op == COND_OR)) {
+               fr_cond_t *next;
+
+               next = talloc_steal(ctx, c->next);
+               c->next = NULL;
+
                talloc_free(c);
-               return -(p - in);
+               c = next;
        }
 
        /*
-        *      Recurse to parse the next condition.
+        *      true || FOO --> true
         */
-       c->next_op = p[0];
-       p += 2;
+       if ((c->type == COND_TYPE_TRUE) &&
+           (c->next_op == COND_OR)) {
 
-       /*
-        *      May still be looking for a closing brace.
-        */
-       slen = cond_tokenize(c, cs, &c->next, error, p, end - p, brace, rules);
-       if (slen <= 0) {
-       return_slen:
-               talloc_free(c);
-               return slen - (p - in);
+               talloc_free(c->next);
+               c->next = NULL;
+               c->next_op = COND_NONE;
        }
-       p += slen;
 
-done:
+       *c_out = c;
+
+       return 0;
+}
+
+static ssize_t cond_tokenize_operand(TALLOC_CTX *ctx, tmpl_t **out,
+                                    fr_sbuff_marker_t *opd_start, fr_sbuff_t *in,
+                                    tmpl_rules_t const *rules)
+{
+       fr_sbuff_term_t const           bareword_terminals =
+                                       FR_SBUFF_TERMS(
+                                               L("\t"),
+                                               L("\n"),
+                                               L(" "),
+                                               L("!*"),
+                                               L("!="),
+                                               L("!~"),
+                                               L("&&"),                /* Logical operator */
+                                               L(")"),                 /* Close condition/sub-condition */
+                                               L("+="),
+                                               L("-="),
+                                               L(":="),
+                                               L("<"),
+                                               L("<="),
+                                               L("=*"),
+                                               L("=="),
+                                               L("=~"),
+                                               L(">"),
+                                               L(">="),
+                                               L("||"),                /* Logical operator */
+                                       );
+
+       fr_sbuff_t                      our_in = FR_SBUFF_NO_ADVANCE(in);
+       fr_sbuff_marker_t               m;
+       tmpl_t                          *vpt;
+       fr_token_t                      type;
+       fr_type_t                       cast = FR_TYPE_INVALID;
+       fr_sbuff_parse_rules_t const    *p_rules;
+       ssize_t                         slen;
+
+       *out = NULL;
+
        /*
-        *      Normalize the condition before returning.
-        *
-        *      We collapse multiple levels of braces to one.  Then
-        *      convert maps to literals.  Then literals to true/false
-        *      statements.  Then true/false ||/&& followed by other
-        *      conditions to just conditions.
-        *
-        *      Order is important.  The more complex cases are
-        *      converted to simpler ones, from the most complex cases
-        *      to the simplest ones.
+        *      Parse (optional) cast
         */
+       slen = tmpl_cast_from_substr(&cast, &our_in);
+       if (slen < 0) return slen;
+
+       fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+       fr_sbuff_marker(&m, &our_in);
 
        /*
-        *      (FOO)     --> FOO
-        *      (FOO) ... --> FOO ...
+        *      Check for quoting
         */
-       if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
-               fr_cond_t *child;
+       fr_sbuff_out_by_longest_prefix(&slen, &type, cond_quote_table, &our_in, T_BARE_WORD);
+       switch (type) {
+       default:
+       case T_BARE_WORD:
+               p_rules = &(fr_sbuff_parse_rules_t){
+                       .terminals = &bareword_terminals,
+                       .escapes = NULL
+               };
+               break;
 
-               child = talloc_steal(ctx, c->data.child);
-               c->data.child = NULL;
+       case T_BACK_QUOTED_STRING:
+       case T_DOUBLE_QUOTED_STRING:
+       case T_SINGLE_QUOTED_STRING:
+#ifdef HAVE_REGEX
+       case T_SOLIDUS_QUOTED_STRING:
+#endif
+               p_rules = tmpl_parse_rules_quoted[type];
+               break;
+#ifndef HAVE_REGEX
+       case T_SOLIDUS_QUOTED_STRING:
+               fr_strerror_printf("Compiled without support for regexes");
+               fr_sbuff_set(&our_in, &m);
+               fr_sbuff_advance(&our_in, 1);
+               goto error;
+#endif
+       }
 
-               child->next = talloc_steal(child, c->next);
-               c->next = NULL;
+       slen = tmpl_afrom_substr(ctx, &vpt, &our_in, type, p_rules, rules);
+       if (!vpt) {
+               fr_sbuff_advance(&our_in, slen * -1);
 
-               child->next_op = c->next_op;
+       error:
+               talloc_free(vpt);
+               return -(fr_sbuff_used_total(&our_in));
+       }
+
+       if ((type != T_BARE_WORD) && !fr_sbuff_next_if_char(&our_in, fr_token_quote[type])) { /* Quoting */
+               fr_strerror_printf("Unterminated string");
+               fr_sbuff_set(&our_in, &m);
+               fr_sbuff_advance(&our_in, 1);
+               goto error;
+       }
+
+#ifdef HAVE_REGEX
+       /*
+        *      Parse the regex flags
+        *
+        *      The quote parsing we performed for the RHS
+        *      earlier means out buffer should be sitting
+        *      at the start of the flags.
+        */
+       if (type == T_SOLIDUS_QUOTED_STRING) {
+               if (!tmpl_contains_regex(vpt)) {
+                       fr_strerror_printf("Expected regex");
+                       fr_sbuff_set(&our_in, &m);
+                       goto error;
+               }
+
+               slen = tmpl_regex_flags_substr(vpt, &our_in, &bareword_terminals);
+               if (slen < 0) {
+                       fr_sbuff_advance(&our_in, slen * -1);
+                       goto error;
+               }
 
                /*
-                *      Set the negation properly
+                *      We've now got the expressions and
+                *      the flags.  Try to compile the
+                *      regex.
                 */
-               if ((c->negate && !child->negate) ||
-                   (!c->negate && child->negate)) {
-                       child->negate = true;
-               } else {
-                       child->negate = false;
+               if (tmpl_is_regex_uncompiled(vpt)) {
+                       slen = tmpl_regex_compile(vpt, true, false);
+                       if (slen < 0) {
+                               fr_sbuff_set(&our_in, &m);      /* Reset to start of expression */
+                               fr_sbuff_advance(&our_in, slen * -1);
+                               goto error;
+                       }
                }
-
-               talloc_free(c);
-               c = child;
        }
+#endif
 
        /*
-        *      (FOO ...) --> FOO ...
-        *
-        *      But don't do !(FOO || BAR) --> !FOO || BAR
-        *      Because that's different.
+        *      Sanity check for nested types
         */
-       if ((c->type == COND_TYPE_CHILD) &&
-           !c->next && !c->negate) {
-               fr_cond_t *child;
+       if (tmpl_is_attr(vpt)) {
+               switch (tmpl_da(vpt)->type) {
+               case FR_TYPE_VALUE:
+                       break;
 
-               child = talloc_steal(ctx, c->data.child);
-               c->data.child = NULL;
+               default:
+                       fr_strerror_printf("Nesting types such as groups or TLVs may not "
+                                          "be compared in conditions");
+                       fr_sbuff_set(&our_in, &m);
+                       goto error;
+               }
 
-               talloc_free(c);
-               c = child;
+               if (tmpl_attr_unknown_add(vpt) < 0) {
+                       fr_strerror_printf("Failed defining attribute");
+                       fr_sbuff_set(&our_in, &m);
+                       goto error;
+               }
        }
 
+       if (tmpl_cast_set(vpt, cast) < 0) {
+               fr_sbuff_set(&our_in, &m);      /* Reset to start of cast */
+               goto error;
+       }
+
+       *out = vpt;
+
+       fr_sbuff_marker(opd_start, in);
+       fr_sbuff_set(opd_start, &m);
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Tokenize a conditional check
+ *
+ *  @param[in] ctx     talloc ctx
+ *  @param[in] cs      our configuration section
+ *  @param[out] out    pointer to the returned condition structure
+ *  @param[in] in      the start of the string to process.  Should be "(..."
+ *  @param[in] brace   look for a closing brace (how many deep we are)
+ *  @param[in] t_rules for attribute parsing
+ *  @return
+ *     - Length of the string skipped.
+ *     - < 0 (the offset to the offending error) on error.
+ */
+static ssize_t cond_tokenize(TALLOC_CTX *ctx, fr_cond_t **out,
+                            CONF_SECTION *cs, fr_sbuff_t *in, int brace,
+                            tmpl_rules_t const *t_rules)
+{
+       fr_sbuff_t              our_in = FR_SBUFF_NO_ADVANCE(in);
+       ssize_t                 slen;
+       fr_cond_t               *c;
+
+       tmpl_t                  *lhs = NULL;
+       fr_token_t              op;
+       fr_cond_op_t            cond_op;
+
+       fr_sbuff_marker_t       m_lhs, m_lhs_cast, m_op, m_rhs, m_rhs_cast;
+       tmpl_rules_t            our_t_rules;
+
        /*
-        *      Convert maps to literals.  Convert one form of map to
-        *      a standardized form.  This doesn't make any
-        *      theoretical difference, but it does mean that the
-        *      run-time evaluation has fewer cases to check.
+        *      We allow unknown and undefined attributes here
         */
-       if (c->type == COND_TYPE_MAP) do {
-#if 0
-                       ifprintf(stderr, "LHS %s %d\n",
-                                c->data.map->lhs->name,
-                                c->datag
-#endif
+       our_t_rules = *t_rules;
+       our_t_rules.allow_unknown = true;
+       our_t_rules.allow_unresolved = true;
 
+       MEM(c = talloc_zero(ctx, fr_cond_t));
 
-               /*
-                *      !FOO !~ BAR --> FOO =~ BAR
-                */
-               if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
-                       c->negate = false;
-                       c->data.map->op = T_OP_REG_EQ;
-               }
+       fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+       if (!fr_sbuff_extend(&our_in)) {
+               fr_strerror_printf("Empty condition is invalid");
+       error:
+               talloc_free(c);
+               return -(fr_sbuff_used_total(&our_in));
+       }
 
-               /*
-                *      FOO !~ BAR --> !FOO =~ BAR
-                */
-               if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
-                       c->negate = true;
-                       c->data.map->op = T_OP_REG_EQ;
-               }
+       /*
+        *      !COND
+        */
+       if (fr_sbuff_next_if_char(&our_in, '!')) {
+               c->negate = true;
+               fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
 
                /*
-                *      !FOO != BAR --> FOO == BAR
+                *  Just for stupidity
                 */
-               if (c->negate && (c->data.map->op == T_OP_NE)) {
-                       c->negate = false;
-                       c->data.map->op = T_OP_CMP_EQ;
+               if (fr_sbuff_is_char(&our_in, '!')) {
+                       fr_strerror_printf("Double negation is invalid");
+                       goto error;
                }
+       }
 
-               /*
-                *      This next one catches "LDAP-Group != foo",
-                *      which doesn't work as-is, but this hack fixes
-                *      it.
-                *
-                *      FOO != BAR --> !FOO == BAR
-                */
-               if (!c->negate && (c->data.map->op == T_OP_NE)) {
-                       c->negate = true;
-                       c->data.map->op = T_OP_CMP_EQ;
-               }
+       /*
+        *      (COND)
+        */
+       if (fr_sbuff_next_if_char(&our_in, '(')) {
 
                /*
-                *      FOO =* BAR --> FOO
-                *      FOO !* BAR --> !FOO
-                *
-                *      FOO may be a string, or a delayed attribute
-                *      reference.
+                *      We've already eaten one layer of
+                *      brackets.  Go recurse to get more.
                 */
-               if ((c->data.map->op == T_OP_CMP_TRUE) ||
-                   (c->data.map->op == T_OP_CMP_FALSE)) {
-                       tmpl_t *vpt;
-
-                       vpt = talloc_steal(c, c->data.map->lhs);
-                       c->data.map->lhs = NULL;
-
-                       /*
-                        *      Invert the negation bit.
-                        */
-                       if (c->data.map->op == T_OP_CMP_FALSE) {
-                               c->negate = !c->negate;
-                       }
-
-                       TALLOC_FREE(c->data.map);
+               c->type = COND_TYPE_CHILD;
+               c->ci = cf_section_to_item(cs);
 
-                       c->type = COND_TYPE_EXISTS;
-                       c->data.vpt = vpt;
-                       break;  /* it's no longer a map */
+               slen = cond_tokenize(c, &c->data.child, cs, &our_in, brace + 1, &our_t_rules);
+               if (slen <= 0) {
+                       fr_sbuff_advance(&our_in, slen * -1);
+                       goto error;
                }
 
-               /*
-                *      Both are data (IP address, integer, etc.)
-                *
-                *      We can do the evaluation here, so that it
-                *      doesn't need to be done at run time
-                */
-               if (tmpl_is_data(c->data.map->lhs) &&
-                   tmpl_is_data(c->data.map->rhs)) {
-                       int rcode;
+               if (!c->data.child) {
+                       fr_strerror_printf("Empty condition is invalid");
+                       goto error;
+               }
 
-                       fr_assert(c->cast != NULL);
+               fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+               goto closing_brace;
+       }
 
-                       rcode = cond_eval_map(NULL, 0, 0, c);
-                       TALLOC_FREE(c->data.map);
-                       c->cast = NULL;
-                       if (rcode) {
-                               c->type = COND_TYPE_TRUE;
-                       } else {
-                               c->type = COND_TYPE_FALSE;
-                       }
+       /*
+        *      We didn't see anything special.  The condition must be one of
+        *
+        *      FOO
+        *      FOO OP BAR
+        */
 
-                       break;  /* it's no longer a map */
-               }
+       /*
+        *      Grab the LHS
+        */
+       fr_sbuff_marker(&m_lhs_cast, &our_in);
+       slen = cond_tokenize_operand(c, &lhs, &m_lhs, &our_in, &our_t_rules);
+       if (!lhs) {
+               fr_sbuff_advance(&our_in, slen * -1);
+               goto error;
+       }
+       if (tmpl_is_attr_unresolved(lhs)) c->pass2_fixup = PASS2_FIXUP_ATTR;
 
-               /*
-                *      Both are literal strings.  They're not parsed
-                *      as TMPL_TYPE_DATA because there's no cast to an
-                *      attribute.
-                *
-                *      We can do the evaluation here, so that it
-                *      doesn't need to be done at run time
-                */
-               if (tmpl_is_unresolved(c->data.map->rhs) &&
-                   tmpl_is_unresolved(c->data.map->lhs) &&
-                   !c->pass2_fixup) {
-                       int rcode;
+       /*
+        *      Hack...
+        */
+       if (lhs->cast != FR_TYPE_INVALID) {
+               c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_CAST_BASE + lhs->cast);
+       }
 
-                       fr_assert(c->cast == NULL);
+       /*
+        *      We may (or not) have an operator
+        */
+       fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
 
-                       rcode = cond_eval_map(NULL, 0, 0, c);
-                       if (rcode) {
-                               c->type = COND_TYPE_TRUE;
-                       } else {
-                               DEBUG4("OPTIMIZING (%s %s %s) --> FALSE",
-                                      c->data.map->lhs->name,
-                                      fr_table_str_by_value(fr_tokens_table, c->data.map->op, "??"),
-                                      c->data.map->rhs->name);
-                               c->type = COND_TYPE_FALSE;
-                       }
+       /*
+        *      What's found directly after the LHS token determines
+        *      what type of expression this is.
+        */
 
-                       /*
-                        *      Free map after using it above.
-                        */
-                       TALLOC_FREE(c->data.map);
-                       break;
+       /*
+        *      Closing curly brace - end of sub-expression
+        */
+       if (fr_sbuff_is_char(&our_in, ')')) {
+               if (fr_sbuff_used_total(&our_in) == 0) {
+                       fr_strerror_printf("Empty string is invalid");
+                       goto error;
                }
 
                /*
-                *      <ipaddr>"foo" CMP &Attribute-Name The cast may
-                *      not be necessary, and we can re-write it so
-                *      that the attribute reference is on the LHS.
+                *      don't skip the brace.  We'll look for it later.
                 */
-               if (c->cast &&
-                   tmpl_is_attr(c->data.map->rhs) &&
-                   (c->cast->type == tmpl_da(c->data.map->rhs)->type) &&
-                   !tmpl_is_attr(c->data.map->lhs)) {
-                       tmpl_t *tmp;
-
-                       tmp = c->data.map->rhs;
-                       c->data.map->rhs = c->data.map->lhs;
-                       c->data.map->lhs = tmp;
-
-                       c->cast = NULL;
-
-                       switch (c->data.map->op) {
-                       case T_OP_CMP_EQ:
-                               /* do nothing */
-                               break;
+               goto unary;
 
-                       case T_OP_LE:
-                               c->data.map->op = T_OP_GE;
-                               break;
+       /*
+        *      FOO - Existence check
+        */
+       } else if (!fr_sbuff_extend(&our_in)) {
+               if (brace) {
+                       fr_strerror_printf("Missing closing brace");
+                       goto error;
+               }
 
-                       case T_OP_LT:
-                               c->data.map->op = T_OP_GT;
-                               break;
+               goto unary;
+       }
 
-                       case T_OP_GE:
-                               c->data.map->op = T_OP_LE;
-                               break;
+       /*
+        *      FOO && ... - Logical operator (existence check)
+        */
+       fr_sbuff_out_by_longest_prefix(&slen, &cond_op, cond_logical_op_table, &FR_SBUFF_NO_ADVANCE(&our_in), COND_NONE);
+       if ((cond_op == COND_AND) || (cond_op == COND_OR)) {
+       unary:
+               if (c->cast) {
+                       fr_strerror_printf("Cannot do cast for existence check");
+                       fr_sbuff_set(&our_in, &m_lhs_cast);
+                       goto error;
+               }
 
-                       case T_OP_GT:
-                               c->data.map->op = T_OP_LT;
-                               break;
+               if (tmpl_contains_regex(lhs)) {
+                       fr_strerror_printf("Unexpected regular expression");
+                       fr_sbuff_set(&our_in, &m_lhs);
+                       goto error;
+               }
 
-                       default:
-                               return_0("Internal sanity check failed 1");
+               /*
+                *      Check to see if this is an rcode operand.
+                *      These are common enough and specific enough
+                *      to conditions that we handle them in the
+                *      condition code specifically.
+                *
+                *      Unary barewords can only be rcodes, so
+                *      anything that's not a rcode an rcode
+                *      is an error.
+                */
+               if (tmpl_is_unresolved(lhs) && (lhs->quote == T_BARE_WORD)) {
+                       rlm_rcode_t rcode;
+
+                       rcode = fr_table_value_by_str(rcode_table, lhs->data.unescaped, RLM_MODULE_UNKNOWN);
+                       if (rcode == RLM_MODULE_UNKNOWN) {
+                               fr_strerror_printf("Expected a module return code");
+                               fr_sbuff_set(&our_in, &m_lhs);
+                               goto error;
                        }
+                       TALLOC_FREE(lhs);
 
-                       /*
-                        *      This must have been parsed into TMPL_TYPE_DATA.
-                        */
-                       fr_assert(!tmpl_is_unresolved(c->data.map->rhs));
+                       c->type = COND_TYPE_RCODE;
+                       c->ci = cf_section_to_item(cs);
+                       c->data.rcode = rcode;
+
+                       goto closing_brace;
                }
 
-       } while (0);
+               c->type = COND_TYPE_EXISTS;
+               c->ci = cf_section_to_item(cs);
+               c->data.vpt = lhs;
+
+               goto closing_brace;
+       }
 
        /*
-        *      Existence checks.  We short-circuit static strings,
-        *      too.
-        *
-        *      FIXME: the data types should be in the template, too.
-        *      So that we know where a literal came from.
-        *
-        *      "foo" is NOT the same as 'foo' or a bare foo.
+        *      Check for any other operator
         */
-       if (c->type == COND_TYPE_EXISTS) {
-               switch (c->data.vpt->type) {
-               case TMPL_TYPE_XLAT_UNRESOLVED:
-               case TMPL_TYPE_ATTR:
-               case TMPL_TYPE_ATTR_UNRESOLVED:
-               case TMPL_TYPE_LIST:
-               case TMPL_TYPE_EXEC:
-                       break;
+       fr_sbuff_marker(&m_op, &our_in);
+       fr_sbuff_out_by_longest_prefix(&slen, &op, cond_cmp_op_table, &our_in, 0);
+       if (slen == 0) {
+               fr_strerror_printf("Invalid operator");
+               goto error;
+       }
+       fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
 
-                       /*
-                        *      'true' and 'false' are special strings
-                        *      which mean themselves.
-                        *
-                        *      For integers, 0 is false, all other
-                        *      integers are true.
-                        *
-                        *      For strings, '' and "" are false.
-                        *      'foo' and "foo" are true.
-                        *
-                        *      The str2tmpl function takes care of
-                        *      marking "%{foo}" as TMPL_TYPE_XLAT_UNRESOLVED, so
-                        *      the strings here are fixed at compile
-                        *      time.
-                        *
-                        *      `exec` and "%{...}" are left alone.
-                        *
-                        *      Bare words must be module return
-                        *      codes.
-                        */
-               case TMPL_TYPE_UNRESOLVED:
-                       if ((strcmp(c->data.vpt->name, "true") == 0) ||
-                           (strcmp(c->data.vpt->name, "1") == 0)) {
-                               c->type = COND_TYPE_TRUE;
-                               TALLOC_FREE(c->data.vpt);
+       {
+               vp_map_t        *map;
+               tmpl_t  *rhs;
 
-                       } else if ((strcmp(c->data.vpt->name, "false") == 0) ||
-                                  (strcmp(c->data.vpt->name, "0") == 0)) {
-                               c->type = COND_TYPE_FALSE;
-                               TALLOC_FREE(c->data.vpt);
+               /*
+                *      The next thing should now be a comparison operator.
+                */
+               c->type = COND_TYPE_MAP;
+               c->ci = cf_section_to_item(cs);
 
-                       } else if (!*c->data.vpt->name) {
-                               c->type = COND_TYPE_FALSE;
-                               TALLOC_FREE(c->data.vpt);
+               switch (op) {
+#ifdef HAVE_REGEX
+               case T_OP_REG_NE:
+               case T_OP_REG_EQ:
+                       break;
+#endif
 
-                       } else if ((lhs_type == T_SINGLE_QUOTED_STRING) ||
-                                  (lhs_type == T_DOUBLE_QUOTED_STRING)) {
-                               c->type = COND_TYPE_TRUE;
-                               TALLOC_FREE(c->data.vpt);
+               case T_OP_CMP_FALSE:
+               case T_OP_CMP_TRUE:
+                       if (lhs->quote != T_BARE_WORD) {
+                               fr_strerror_printf("Cannot use %s on a string",
+                                                  fr_table_str_by_value(cond_cmp_op_table, op, "<INVALID>"));
+                               fr_sbuff_set(&our_in, &m_op);
+                               goto error;
+                       }
+                       break;
+               default:
+                       break;
+               }
 
-                       } else if (lhs_type == T_BARE_WORD) {
-                               int rcode;
-                               bool zeros = true;
-                               char const *q;
+               if (!fr_sbuff_extend(&our_in)) {
+                       fr_strerror_printf("Expected text after operator");
+                       goto error;
+               }
 
-                               for (q = c->data.vpt->name;
-                                    *q != '\0';
-                                    q++) {
-                                       if (!isdigit((int) *q)) {
-                                               break;
-                                       }
-                                       if (*q != '0') zeros = false;
-                               }
+               MEM(c->data.map = map = talloc(c, vp_map_t));
 
-                               /*
-                                *      It's all digits, and therefore
-                                *      'false' if zero, and 'true' otherwise.
-                                */
-                               if (!*q) {
-                                       if (zeros) {
-                                               c->type = COND_TYPE_FALSE;
-                                       } else {
-                                               c->type = COND_TYPE_TRUE;
-                                       }
-                                       TALLOC_FREE(c->data.vpt);
-                                       break;
-                               }
+               /*
+                *      Grab the RHS
+                */
+               fr_sbuff_marker(&m_rhs_cast, &our_in);
+               slen = cond_tokenize_operand(c, &rhs, &m_rhs, &our_in, &our_t_rules);
+               if (!rhs) {
+                       fr_sbuff_advance(&our_in, slen * -1);
+                       goto error;
+               }
+               if (tmpl_is_attr_unresolved(rhs)) c->pass2_fixup = PASS2_FIXUP_ATTR;
+
+               *map = (vp_map_t) {
+                       .ci = cf_section_to_item(cs),
+                       .lhs = lhs,
+                       .op = op,
+                       .rhs = rhs
+               };
+
+               if (rhs->cast != FR_TYPE_INVALID) {
+                       fr_strerror_printf("Unexpected cast");
+                       fr_sbuff_set(&our_in, &m_rhs_cast);
+                       goto error;
+               }
 
-                               /*
-                                *      Allow &Foo-Bar where Foo-Bar is an attribute
-                                *      defined by a module.
-                                */
-                               if (c->pass2_fixup == PASS2_FIXUP_ATTR) {
-                                       break;
-                               }
+               /*
+                *      Binary expressions can't contain lists
+                */
+               if (tmpl_is_list(lhs)) {
+                       fr_strerror_printf("Cannot use list references in condition");
+                       fr_sbuff_set(&our_in, &m_lhs);
+                       goto error;
+               }
 
-                               rcode = fr_table_value_by_str(allowed_return_codes,
-                                                  c->data.vpt->name, 0);
-                               if (!rcode) {
-                                       return_0("Expected a module return code");
-                               }
-                       }
+               if (tmpl_is_list(rhs)) {
+                       fr_strerror_printf("Cannot use list references in condition");
+                       fr_sbuff_set(&our_in, &m_rhs);
+                       goto error;
+               }
 
-                       /*
-                        *      Else lhs_type==T_INVALID, and this
-                        *      node was made by promoting a child
-                        *      which had already been normalized.
-                        */
-                       break;
+               if (((op == T_OP_REG_EQ) || (op == T_OP_REG_NE)) &&
+                   (!tmpl_contains_regex(lhs) && !tmpl_contains_regex(rhs))) {
+                       fr_strerror_printf("Expected regular expression");
+                       fr_sbuff_set(&our_in, &m_rhs);
+                       goto error;
+               }
 
-               case TMPL_TYPE_DATA:
-                       return_0("Cannot use data here");
+               if (((op != T_OP_REG_EQ) && (op != T_OP_REG_NE)) &&
+                   (tmpl_contains_regex(lhs) || tmpl_contains_regex(rhs))) {
+                       fr_strerror_printf("Unexpected regular expression");    /* Fixme should point to correct operand */
+                       fr_sbuff_set(&our_in, &m_rhs);
+                       goto error;
+               }
 
-               default:
-                       return_0("Internal sanity check failed 2");
+               fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+
+               /*
+                *      Check cast type.  We can have the RHS
+                *      a string if the LHS has a cast.  But
+                *      if the RHS is an attr, it MUST be the
+                *      same type as the LHS.
+                */
+               if (c->cast) {
+                       slen = cond_check_cast(c, fr_sbuff_start(&our_in), m_lhs.p, m_rhs.p);
+                       if (slen <= 0) {
+                               fr_sbuff_set(&our_in, our_in.start + (slen * -1));
+                               goto error;
+                       }
+               } else {
+                       slen = cond_check_attrs(c, &m_lhs, &m_rhs);
+                       if (slen <= 0) {
+                               fr_sbuff_set(&our_in, our_in.start + (slen * -1));
+                               goto error;
+                       }
                }
-       }
+       } /* parse OP RHS */
 
+closing_brace:
        /*
-        *      !TRUE -> FALSE
+        *      ...COND)
         */
-       if (c->type == COND_TYPE_TRUE) {
-               if (c->negate) {
-                       c->negate = false;
-                       c->type = COND_TYPE_FALSE;
+       if (fr_sbuff_is_char(&our_in, ')')) {
+               if (!brace) {
+                       fr_strerror_printf("Unexpected closing brace");
+                       goto error;
                }
+               fr_sbuff_advance(&our_in, 1);
+               fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+               goto done;
        }
 
        /*
-        *      !FALSE -> TRUE
+        *      End of string is allowed, unless we're still looking
+        *      for closing braces.
         */
-       if (c->type == COND_TYPE_FALSE) {
-               if (c->negate) {
-                       c->negate = false;
-                       c->type = COND_TYPE_TRUE;
+       if (!fr_sbuff_extend(&our_in)) {
+               if (brace) {
+                       fr_strerror_printf("Missing closing brace");
+                       goto error;
                }
+               goto done;
        }
 
        /*
-        *      true && FOO --> FOO
+        *      We've parsed all of the condition, stop.
         */
-       if ((c->type == COND_TYPE_TRUE) &&
-           (c->next_op == COND_AND)) {
-               fr_cond_t *next;
-
-               next = talloc_steal(ctx, c->next);
-               c->next = NULL;
+       if (brace == 0) {
+               if (fr_sbuff_is_space(&our_in)) goto done;
 
-               talloc_free(c);
-               c = next;
+               /*
+                *      Open a section, it's OK to be done.
+                */
+               if (fr_sbuff_is_char(&our_in, '{')) goto done;
        }
 
        /*
-        *      false && FOO --> false
+        *      Allow ((a == b) && (b == c))
         */
-       if ((c->type == COND_TYPE_FALSE) &&
-           (c->next_op == COND_AND)) {
-               talloc_free(c->next);
-               c->next = NULL;
-               c->next_op = COND_NONE;
+       fr_sbuff_out_by_longest_prefix(&slen, &cond_op, cond_logical_op_table,
+                                      &our_in, COND_NONE);
+       if (slen == 0) {
+               fr_strerror_printf("Unexpected text after condition");
+               goto error;
        }
 
        /*
-        *      false || FOO --> FOO
+        *      Recurse to parse the next condition.
         */
-       if ((c->type == COND_TYPE_FALSE) &&
-           (c->next_op == COND_OR)) {
-               fr_cond_t *next;
+       c->next_op = cond_op;
 
-               next = talloc_steal(ctx, c->next);
-               c->next = NULL;
+       /*
+        *      May still be looking for a closing brace.
+        */
+       slen = cond_tokenize(c, &c->next, cs, &our_in, brace, &our_t_rules);
+       if (slen <= 0) {
+               fr_sbuff_advance(&our_in, slen * -1);
+               goto error;
+       }
 
+done:
+       if (cond_normalise(ctx, lhs ? lhs->quote : T_INVALID, &c) < 0) {
                talloc_free(c);
-               c = next;
+               return 0;
        }
 
-       /*
-        *      true || FOO --> true
-        */
-       if ((c->type == COND_TYPE_TRUE) &&
-           (c->next_op == COND_OR)) {
-               talloc_free(c->next);
-               c->next = NULL;
-               c->next_op = COND_NONE;
-       }
+       *out = c;
 
-       *pcond = c;
-       return p - in;
+       return fr_sbuff_set(in, &our_in);
 }
 
 /** Tokenize a conditional check
  *
  * @param[in] cs       current CONF_SECTION and talloc ctx
  * @param[out] head    the parsed condition structure
- * @param[out] error   the parse error (if any)
  * @param[in] dict     dictionary to resolve attributes in.
- * @param[in] in       the start of the string to process.  Should be "(..."
- * @param[in] inlen    the length of the string to process.
+ * @param[in] in       the start of the string to process.
  * @return
  *     - Length of the string skipped.
  *     - < 0 (the offset to the offending error) on error.
  */
-ssize_t fr_cond_tokenize(CONF_SECTION *cs,
-                        fr_cond_t **head, char const **error,
-                        fr_dict_t const *dict, char const *in, size_t inlen)
+ssize_t fr_cond_tokenize(CONF_SECTION *cs, fr_cond_t **head, fr_dict_t const *dict, fr_sbuff_t *in)
 {
-       return cond_tokenize(cs, cs, head, error, in, inlen, 0, &(tmpl_rules_t){ .dict_def = dict });
+       char buffer[8192];
+       ssize_t diff, slen;
+
+       if (!cf_expand_variables(cf_filename(cs), cf_lineno(cs), cf_item_to_section(cf_parent(cs)),
+                                buffer, sizeof(buffer),
+                                fr_sbuff_current(in), fr_sbuff_remaining(in), NULL)) {
+               fr_strerror_printf("Failed expanding configuration variable");
+               return 0;
+       }
+
+       diff = fr_sbuff_remaining(in) - strlen(buffer); /* Hack so that we appear to consume more of the string */
+       slen = cond_tokenize(cs, head, cs, &FR_SBUFF_IN(buffer, strlen(buffer)), 0,
+                            &(tmpl_rules_t){
+                                       .dict_def = dict,
+                                       .allow_unresolved = true,
+                                       .allow_unknown = true,
+                                       .allow_foreign = (dict == NULL) /* Allow foreign attributes if we have no dict */
+                            });
+       if (slen < 0) return slen;
+
+       return slen + diff;
 }
 
 /*
@@ -1632,6 +1739,7 @@ bool fr_cond_walk(fr_cond_t *c, bool (*callback)(fr_cond_t *cond, void *uctx), v
                case COND_TYPE_INVALID:
                        return false;
 
+               case COND_TYPE_RCODE:
                case COND_TYPE_EXISTS:
                case COND_TYPE_MAP:
                case COND_TYPE_TRUE:
index 5a31227435b27d98340fc0be4311675b973c259b..3cd99bcc7afd0a00db04c58ffe46f75042ed866d 100644 (file)
@@ -91,7 +91,8 @@ static void fr_exec_pair_to_env(REQUEST *request, VALUE_PAIR *input_pairs, char
                }
 
                n = strlen(buffer);
-               fr_pair_value_snprint(buffer + n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0);
+               fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer + n, sizeof(buffer) - n), vp,
+                                          shell_escape ? T_DOUBLE_QUOTED_STRING : T_BARE_WORD);
 
                DEBUG3("export %s", buffer);
                envp[i++] = talloc_typed_strdup(envp, buffer);
index 5896a4cfda2fdad4baccce1d7d243b1a61feb888..f51c47bcc64fa3aaabdb9344dc8b773eec46adff 100644 (file)
@@ -555,7 +555,7 @@ void vlog_module_failure_msg(REQUEST *request, char const *fmt, va_list ap)
 
        MEM(pair_add_request(&vp, attr_module_failure_message) >= 0);
        if (request->module && (request->module[0] != '\0')) {
-               fr_pair_value_asprintf(vp, "%s: %s", request->module, p);
+               fr_pair_value_aprintf(vp, "%s: %s", request->module, p);
        } else {
                fr_pair_value_strdup(vp, p);
        }
index bf4e264b06b3e8db44f2e104ec9f3f3fa5d8dd4d..39b48c02402997c9779b99226558adf098bb6595 100644 (file)
@@ -65,10 +65,10 @@ static inline vp_map_t *map_alloc(TALLOC_CTX *ctx)
  *
  *
  * @param map to process.
- * @param rhs_type quotation type around rhs.
+ * @param rhs_quote quotation type around rhs.
  * @param rhs string to re-parse.
  */
-bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_type, char const *rhs)
+bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_quote, char const *rhs)
 {
        size_t                  len;
        uint8_t                 *ptr;
@@ -77,7 +77,7 @@ bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_type, char const *rhs)
 
        fr_dict_attr_t const    *da;
        VALUE_PAIR              *vp = NULL;
-       tmpl_t          *vpt;
+       tmpl_t                  *vpt;
        fr_value_box_t          cast;
 
        fr_assert(map != NULL);
@@ -105,7 +105,7 @@ bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_type, char const *rhs)
         *      If the RHS is something OTHER than an octet
         *      string, go parse it as that.
         */
-       if (rhs_type != T_BARE_WORD) return false;
+       if (rhs_quote != T_BARE_WORD) return false;
        if ((rhs[0] != '0') || (tolower((int)rhs[1]) != 'x')) return false;
        if (!rhs[2]) return false;
 
@@ -140,7 +140,7 @@ bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_type, char const *rhs)
         *      through a lot of work in order to set the name to
         *      (drum roll) exactly the same thing it was before.
         */
-       vpt = tmpl_alloc(map, TMPL_TYPE_ATTR, NULL, -1, T_BARE_WORD);
+       vpt = tmpl_alloc(map, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0);
        tmpl_attr_copy(vpt, map->lhs);
        tmpl_attr_set_leaf_da(vpt, da);
 
@@ -157,9 +157,9 @@ bool map_cast_from_hex(vp_map_t *map, fr_token_t rhs_type, char const *rhs)
        len = radius_list_name(&list, p, PAIR_LIST_UNKNOWN);
 
        if (list != PAIR_LIST_UNKNOWN) {
-               tmpl_set_name(vpt, T_BARE_WORD, "%.*s:%s", (int) len, map->lhs->name, tmpl_da(map->lhs)->name);
+               tmpl_set_name_printf(vpt, T_BARE_WORD, "%.*s:%s", (int) len, map->lhs->name, tmpl_da(map->lhs)->name);
        } else {
-               tmpl_set_name(vpt, T_BARE_WORD, "&%s", tmpl_da(map->lhs)->name);
+               tmpl_set_name_printf(vpt, T_BARE_WORD, "&%s", tmpl_da(map->lhs)->name);
        }
        talloc_free(map->lhs);
        map->lhs = vpt;
@@ -195,7 +195,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
                 tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules)
 {
        vp_map_t        *map;
-       char const      *attr, *value;
+       char const      *attr, *value, *marker_subject;
        ssize_t         slen;
        fr_token_t      type;
 
@@ -203,7 +203,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
 
        if (!cp) return -1;
 
-       map = talloc_zero(ctx, vp_map_t);
+       MEM(map = talloc_zero(ctx, vp_map_t));
        map->op = cf_pair_operator(cp);
        map->ci = cf_pair_to_item(cp);
 
@@ -222,12 +222,17 @@ int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
        switch (type) {
        case T_DOUBLE_QUOTED_STRING:
        case T_BACK_QUOTED_STRING:
-               slen = tmpl_afrom_str(ctx, &map->lhs, attr, talloc_array_length(attr) - 1, type, lhs_rules, true);
+               slen = tmpl_afrom_substr(ctx, &map->lhs,
+                                        &FR_SBUFF_IN(attr, talloc_array_length(attr) - 1),
+                                        type,
+                                        tmpl_parse_rules_unquoted[type],       /* We're not searching for quotes */
+                                        lhs_rules);
                if (slen <= 0) {
                        char *spaces, *text;
 
+                       marker_subject = attr;
                marker:
-                       fr_canonicalize_error(ctx, &spaces, &text, slen, attr);
+                       fr_canonicalize_error(ctx, &spaces, &text, slen, marker_subject);
                        cf_log_err(cp, "%s", text);
                        cf_log_perr(cp, "%s^", spaces);
 
@@ -241,11 +246,11 @@ int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
                slen = tmpl_afrom_attr_str(ctx, NULL, &map->lhs, attr, lhs_rules);
                if (slen <= 0) {
                        cf_log_err(cp, "Failed parsing attribute reference");
-
+                       marker_subject = attr;
                        goto marker;
                }
 
-               if (tmpl_unknown_attr_add(map->lhs) < 0) {
+               if (tmpl_is_attr(map->lhs) && tmpl_attr_unknown_add(map->lhs) < 0) {
                        cf_log_perr(cp, "Failed creating attribute %s", map->lhs->name);
                        goto error;
                }
@@ -257,23 +262,26 @@ int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
         */
        type = cf_pair_value_quote(cp);
 
-       if ((type == T_BARE_WORD) && (value[0] == '0') && (tolower((int)value[1]) == 'x') &&
-           tmpl_is_attr(map->lhs) &&
-           map_cast_from_hex(map, type, value)) {
-               /* do nothing */
-       } else {
-               slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, rhs_rules, true);
-               if (slen < 0) goto marker;
-               if (tmpl_unknown_attr_add(map->rhs) < 0) {
-                       cf_log_perr(cp, "Failed creating attribute %s", map->rhs->name);
-                       goto error;
-               }
+       slen = tmpl_afrom_substr(map, &map->rhs,
+                                &FR_SBUFF_IN(value, strlen(value)),
+                                type,
+                                tmpl_parse_rules_unquoted[type],       /* We're not searching for quotes */
+                                rhs_rules);
+       if (slen < 0) {
+               marker_subject = value;
+               goto marker;
        }
+
        if (!map->rhs) {
                cf_log_perr(cp, "Failed parsing RHS");
                goto error;
        }
 
+       if (tmpl_is_attr(map->rhs) && (tmpl_attr_unknown_add(map->rhs) < 0)) {
+               cf_log_perr(cp, "Failed creating attribute %s", map->rhs->name);
+               goto error;
+       }
+
        /*
         *      We cannot assign a count to an attribute.  That must
         *      be done in an xlat.
@@ -422,9 +430,6 @@ int map_afrom_cs(TALLOC_CTX *ctx, vp_map_t **out, CONF_SECTION *cs,
 
                        /*
                         *      Only TLV and GROUP can be grouped.
-                        *
-                        *      @todo - maybe "tagged" too, for stupid
-                        *      RADIUS nonsense?
                         */
                        if ((tmpl_da(map->lhs)->type != FR_TYPE_TLV) &&
                            (tmpl_da(map->lhs)->type != FR_TYPE_GROUP)) {
@@ -501,27 +506,30 @@ int map_afrom_cs(TALLOC_CTX *ctx, vp_map_t **out, CONF_SECTION *cs,
  * @param[in] ctx              for talloc.
  * @param[out] out             Where to store the head of the map.
  * @param[in] lhs              of the tuple.
- * @param[in] lhs_type         type of quoting around the LHS string
+ * @param[in] lhs_quote                type of quoting around the LHS string
  * @param[in] lhs_rules                rules that control parsing of the LHS string.
  * @param[in] op               the operation to perform
  * @param[in] rhs              of the tuple.
- * @param[in] rhs_type         type of quoting aounrd the RHS string.
+ * @param[in] rhs_quote                type of quoting aounrd the RHS string.
  * @param[in] rhs_rules                rules that control parsing of the RHS string.
  * @return
  *     - #vp_map_t if successful.
  *     - NULL on error.
  */
 int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out,
-                    char const *lhs, fr_token_t lhs_type, tmpl_rules_t const *lhs_rules,
+                    char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules,
                     fr_token_t op,
-                    char const *rhs, fr_token_t rhs_type, tmpl_rules_t const *rhs_rules)
+                    char const *rhs, fr_token_t rhs_quote, tmpl_rules_t const *rhs_rules)
 {
        ssize_t slen;
        vp_map_t *map;
 
        map = talloc_zero(ctx, vp_map_t);
-
-       slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, lhs_rules, true);
+       slen = tmpl_afrom_substr(map, &map->lhs,
+                                &FR_SBUFF_IN(lhs, strlen(lhs)),
+                                lhs_quote,
+                                NULL,
+                                lhs_rules);
        if (slen < 0) {
        error:
                talloc_free(map);
@@ -532,11 +540,15 @@ int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out,
 
        if (tmpl_is_attr(map->lhs) &&
            tmpl_da(map->lhs)->flags.is_raw &&
-           map_cast_from_hex(map, rhs_type, rhs)) {
+           map_cast_from_hex(map, rhs_quote, rhs)) {
                return 0;
        }
 
-       slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, rhs_rules, true);
+       slen = tmpl_afrom_substr(map, &map->rhs,
+                                &FR_SBUFF_IN(rhs, strlen(rhs)),
+                                rhs_quote,
+                                NULL,
+                                rhs_rules);
        if (slen < 0) goto error;
 
        MAP_VERIFY(map);
@@ -555,7 +567,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out,
  * @param[in] ctx              for talloc
  * @param[out] out             Where to store the head of the map.
  * @param[in] lhs              of the operation
- * @param[in] lhs_type         type of the LHS string
+ * @param[in] lhs_quote                type of the LHS string
  * @param[in] lhs_rules                rules that control parsing of the LHS string.
  * @param[in] op               the operation to perform
  * @param[in] rhs              of the operation
@@ -566,7 +578,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out,
  *     - NULL on error.
  */
 int map_afrom_value_box(TALLOC_CTX *ctx, vp_map_t **out,
-                       char const *lhs, fr_token_t lhs_type, tmpl_rules_t const *lhs_rules,
+                       char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules,
                        fr_token_t op,
                        fr_value_box_t *rhs, bool steal_rhs_buffs)
 {
@@ -575,7 +587,11 @@ int map_afrom_value_box(TALLOC_CTX *ctx, vp_map_t **out,
 
        map = talloc_zero(ctx, vp_map_t);
 
-       slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, lhs_rules, true);
+       slen = tmpl_afrom_substr(map, &map->lhs,
+                                &FR_SBUFF_IN(lhs, strlen(lhs)),
+                                lhs_quote,
+                                NULL,
+                                lhs_rules);
        if (slen < 0) {
        error:
                talloc_free(map);
@@ -681,29 +697,34 @@ int map_afrom_vp(TALLOC_CTX *ctx, vp_map_t **out, VALUE_PAIR *vp, tmpl_rules_t c
                return -1;
        }
 
-       map->lhs = tmpl_alloc(map, TMPL_TYPE_ATTR, NULL, -1, T_BARE_WORD);
+       /*
+        *      Allocate the LHS
+        */
+       map->lhs = tmpl_alloc(map, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0);
        if (!map->lhs) goto oom;
 
        tmpl_attr_set_da(map->lhs, vp->da);
        tmpl_attr_set_leaf_num(map->lhs, NUM_ANY);
-       tmpl_attr_set_leaf_tag(map->lhs, vp->tag);
 
        tmpl_attr_set_request(map->lhs, rules->request_def);
        tmpl_attr_set_list(map->lhs, rules->list_def);
 
-       tmpl_snprint(NULL, buffer, sizeof(buffer), map->lhs);
-       tmpl_set_name(map->lhs, T_BARE_WORD, "%s", buffer);
+       tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
+       tmpl_set_name(map->lhs, T_BARE_WORD, buffer, -1);
 
-       map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, NULL, -1, T_BARE_WORD);
+       /*
+        *      Allocate the RHS
+        */
+       map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, T_BARE_WORD, NULL, -1);
        if (!map->lhs) goto oom;
 
        switch (vp->vp_type) {
        case FR_TYPE_QUOTED:
-               tmpl_set_name(map->rhs, T_DOUBLE_QUOTED_STRING, "%pV", &vp->data);
+               tmpl_set_name_printf(map->rhs, T_DOUBLE_QUOTED_STRING, "%pV", &vp->data);
                break;
 
        default:
-               tmpl_set_name(map->rhs, T_BARE_WORD, "%pV", &vp->data);
+               tmpl_set_name_printf(map->rhs, T_BARE_WORD, "%pV", &vp->data);
                break;
        }
 
@@ -865,7 +886,6 @@ static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, v
 
                MEM(vp = fr_pair_afrom_da(ctx, tmpl_da(map->lhs)));
                vp->op = map->op;
-               vp->tag = tmpl_tag(map->lhs);
                if (fr_pair_value_from_str(vp, answer, -1, '"', false) < 0) {
                        RPEDEBUG("Failed parsing exec output");
                        fr_pair_list_free(&vp);
@@ -983,32 +1003,6 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
                        goto error;
                }
                n->op = map->op;
-               n->tag = tmpl_tag(map->lhs);
-               *out = n;
-               break;
-
-       case TMPL_TYPE_XLAT_UNRESOLVED:
-               fr_assert(tmpl_is_attr(map->lhs));
-               fr_assert(tmpl_da(map->lhs));   /* We need to know which attribute to create */
-
-               MEM(n = fr_pair_afrom_da(ctx, tmpl_da(map->lhs)));
-
-               str = NULL;
-               slen = xlat_aeval(request, &str, request, map->rhs->name, NULL, NULL);
-               if (slen < 0) {
-                       rcode = slen;
-                       talloc_free(n);
-                       goto error;
-               }
-
-               rcode = fr_pair_value_from_str(n, str, -1, '\0', false);
-               talloc_free(str);
-               if (rcode < 0) {
-                       fr_pair_list_free(&n);
-                       goto error;
-               }
-               n->op = map->op;
-               n->tag = tmpl_tag(map->lhs);
                *out = n;
                break;
 
@@ -1024,7 +1018,6 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
                        goto error;
                }
                n->op = map->op;
-               n->tag = tmpl_tag(map->lhs);
                *out = n;
                break;
 
@@ -1067,7 +1060,6 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
                                fr_assert((n->vp_type != FR_TYPE_STRING) || (n->vp_strvalue != NULL));
 
                                n->op = map->op;
-                               n->tag = tmpl_tag(map->lhs);
                                fr_cursor_append(&to, n);
                        }
 
@@ -1081,7 +1073,6 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
                for (; vp; vp = fr_cursor_next(&from)) {
                        vp->da = tmpl_da(map->lhs);
                        vp->op = map->op;
-                       vp->tag = tmpl_tag(map->lhs);
                }
                *out = found;
        }
@@ -1108,7 +1099,6 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
                        }
                }
                n->op = map->op;
-               n->tag = tmpl_tag(map->lhs);
                *out = n;
 
                MAP_VERIFY(map);
@@ -1138,14 +1128,16 @@ int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t cons
 #define DEBUG_OVERWRITE(_old, _new) \
 do {\
        if (RDEBUG_ENABLED3) {\
-               char *old = fr_pair_value_asprint(request, _old, '"');\
-               char *n = fr_pair_value_asprint(request, _new, '"');\
-               RINDENT();\
-               RDEBUG3("--> overwriting '%s' with '%s'", old, n);\
-               REXDENT();\
-               talloc_free(old);\
-               talloc_free(n);\
-       }\
+               char *our_old; \
+               char *our_new; \
+               fr_pair_aprint_value_quoted(request, &our_old, _old, T_DOUBLE_QUOTED_STRING); \
+               fr_pair_aprint_value_quoted(request, &our_new, _new, T_DOUBLE_QUOTED_STRING); \
+               RINDENT(); \
+               RDEBUG3("--> overwriting '%s' with '%s'", our_old, our_new); \
+               REXDENT(); \
+               talloc_free(our_old); \
+               talloc_free(our_new); \
+       } \
 } while (0)
 
 /** Convert #vp_map_t to #VALUE_PAIR (s) and add them to a #REQUEST.
@@ -1204,7 +1196,6 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
         *      This allows the syntax like:
         *      - "Attr-%{number}" := "value"
         */
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT:
        case TMPL_TYPE_EXEC:
        {
@@ -1362,11 +1353,11 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
        num = tmpl_num(map->lhs);
        (void) fr_pair_cursor_init(&dst_list, list);
        if ((num != NUM_ALL) && (num != NUM_ANY)) {
-               while ((dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs), tmpl_tag(map->lhs)))) {
+               while ((dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs)))) {
                        if (num-- == 0) break;
                }
        } else {
-               dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs), tmpl_tag(map->lhs));
+               dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs));
        }
        fr_assert(!dst || (tmpl_da(map->lhs) == dst->da));
 
@@ -1386,11 +1377,10 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
                if (!dst) goto finish;
 
                /*
-                *      Wildcard: delete all of the matching ones, based on tag.
+                *      Wildcard: delete all of the matching ones
                 */
                if (tmpl_num(map->lhs) == NUM_ANY) {
-                       fr_pair_delete_by_child_num(list, tmpl_da(map->lhs)->parent,
-                                                   tmpl_da(map->lhs)->attr, tmpl_tag(map->lhs));
+                       fr_pair_delete_by_child_num(list, tmpl_da(map->lhs)->parent, tmpl_da(map->lhs)->attr);
                        dst = NULL;
                /*
                 *      We've found the Nth one.  Delete it, and only it.
@@ -1449,7 +1439,7 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
                 */
                for (dst = fr_pair_cursor_current(&dst_list);
                     dst;
-                    dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs), tmpl_tag(map->lhs))) {
+                    dst = fr_pair_cursor_next_by_da(&dst_list, tmpl_da(map->lhs))) {
                        for (vp = fr_pair_cursor_head(&src_list);
                             vp;
                             vp = fr_pair_cursor_next(&src_list)) {
@@ -1468,17 +1458,6 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
                goto update;
        }
 
-       /*
-        *      Another fixup pass to set tags on attributes we're about to insert
-        */
-       if (tmpl_tag(map->lhs) != TAG_ANY) {
-               for (vp = fr_pair_cursor_init(&src_list, &head);
-                    vp;
-                    vp = fr_pair_cursor_next(&src_list)) {
-                       vp->tag = tmpl_tag(map->lhs);
-               }
-       }
-
        switch (map->op) {
        /*
         *      = - Set only if not already set
@@ -1535,8 +1514,8 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
        {
                VALUE_PAIR *a, *b;
 
-               fr_pair_list_sort(&head, fr_pair_cmp_by_da_tag);
-               fr_pair_list_sort(list, fr_pair_cmp_by_da_tag);
+               fr_pair_list_sort(&head, fr_pair_cmp_by_da);
+               fr_pair_list_sort(list, fr_pair_cmp_by_da);
 
                fr_pair_cursor_head(&dst_list);
 
@@ -1548,7 +1527,7 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
                             a = fr_pair_cursor_next(&dst_list)) {
                                int8_t cmp;
 
-                               cmp = fr_pair_cmp_by_da_tag(a, b);      /* attribute and tag match */
+                               cmp = fr_pair_cmp_by_da(a, b);  /* attribute and tag match */
                                if (cmp > 0) break;
                                else if (cmp < 0) continue;
 
@@ -1578,74 +1557,55 @@ finish:
        return rcode;
 }
 
-/**  Print a map to a string
+/** Print a map to a string
  *
- * @param[out] need    The buffer space we would have needed to
- *                     print more of the string.
  * @param[out] out     Buffer to write string to.
- * @param[in] outlen   Size of the output buffer.
  * @param[in] map      to print.
  * @return
  *     - The number of bytes written to the out buffer.
  *     - A number >= outlen if truncation has occurred.
  */
-size_t map_snprint(size_t *need, char *out, size_t outlen, vp_map_t const *map)
+ssize_t map_print(fr_sbuff_t *out, vp_map_t const *map)
 {
-       size_t          len;
-       char            *p = out;
-       char            *end = out + outlen;
-       size_t          our_need;
-
-       if (!need) need = &our_need;
-
-       RETURN_IF_NO_SPACE_INIT(need, 1, p, out, end);
+       fr_sbuff_t      our_out = FR_SBUFF_NO_ADVANCE(out);
 
        MAP_VERIFY(map);
 
-       len = tmpl_snprint(need, out, end - p, map->lhs);
-       if (*need) return len;
-       p += len;
-
-       RETURN_IF_NO_SPACE(need, 1, p, out, end);
-       *(p++) = ' ';
-
-       len = strlcpy(p, fr_token_name(map->op), end - p);
-       RETURN_IF_TRUNCATED(need, len, p, out, end);
+       /*
+        *      Print the lhs
+        */
+       FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, map->lhs, TMPL_ATTR_REF_PREFIX_YES);
 
-       RETURN_IF_NO_SPACE(need, 1, p, out, end);
-       *(p++) = ' ';
+       /*
+        *      Print separators and operator
+        */
+       FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ');
+       FR_SBUFF_IN_STRCPY_RETURN(&our_out, fr_token_name(map->op));
+       FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ');
 
        /*
         *      The RHS doesn't matter for many operators
         */
        if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
-               len = strlcpy(p, "ANY", (end - p));
-               RETURN_IF_TRUNCATED(need, len, p, out, end - 1);
-               return p - out;
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, "ANY");
+               return fr_sbuff_set(out, &our_out);
        }
 
-       if (!map->child && !fr_cond_assert(map->rhs != NULL)) return -1;
-
-       if (tmpl_is_attr(map->lhs) &&
-           (tmpl_da(map->lhs)->type == FR_TYPE_STRING) &&
-           tmpl_is_unresolved(map->rhs)) {
-               RETURN_IF_NO_SPACE(need, 1, p, out, end);
-               *(p++) = '\'';
-
-               len = tmpl_snprint(need, p, end - p, map->rhs);
-               if (*need) return len;
-               p += len;
-
-               RETURN_IF_NO_SPACE(need, 1, p, out, end);
-               *(p++) = '\'';
-       } else {
-               len = tmpl_snprint(need, p, end - p, map->rhs);
-               if (*need) return len;
-               p += len;
+       /*
+        *      If there's no child and no RHS then the
+        *      map was invalid.
+        */
+       if (!map->child && !fr_cond_assert(map->rhs != NULL)) {
+               fr_sbuff_terminate(out);
+               return 0;
        }
 
-       *p = '\0';
-       return p - out;
+       /*
+        *      Print the RHS.
+        */
+       FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, map->rhs, TMPL_ATTR_REF_PREFIX_YES);
+
+       return fr_sbuff_set(out, &our_out);
 }
 
 /*
@@ -1668,34 +1628,42 @@ void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
         */
        default:
        case TMPL_TYPE_UNRESOLVED:
-               rhs = fr_pair_value_asprint(request, vp, fr_token_quote[map->rhs->quote]);
+               fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
                break;
 
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT:
-               rhs = fr_pair_value_asprint(request, vp, fr_token_quote[map->rhs->quote]);
+               fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
                break;
 
        case TMPL_TYPE_DATA:
-               rhs = fr_pair_value_asprint(request, vp, fr_token_quote[map->rhs->quote]);
+               fr_pair_aprint_value_quoted(request, &rhs, vp, map->rhs->quote);
                break;
 
        /*
         *      For the lists, we can't use the original name, and have to
-        *      rebuild it using tmpl_snprint, for each attribute we're
+        *      rebuild it using tmpl_print, for each attribute we're
         *      copying.
         */
        case TMPL_TYPE_LIST:
        {
-               tmpl_t  *vpt;
-               char const      *quote = (vp->vp_type == FR_TYPE_STRING) ? "\"" : "";
+               tmpl_t          *vpt;
+               fr_token_t      quote;
 
-               vpt = tmpl_alloc(request, TMPL_TYPE_ATTR, map->rhs->name, strlen(map->rhs->name), *quote);
+               switch (vp->vp_type) {
+               case FR_TYPE_QUOTED:
+                       quote = T_DOUBLE_QUOTED_STRING;
+                       break;
+               default:
+                       quote = T_BARE_WORD;
+                       break;
+               }
+
+               vpt = tmpl_alloc(request, TMPL_TYPE_ATTR, quote, map->rhs->name, strlen(map->rhs->name));
 
                /*
                 *      Fudge a temporary tmpl that describes the attribute we're copying
                 *      this is a combination of the original list tmpl, and values from
-                *      the VALUE_PAIR. This way, we get tag info included.
+                *      the VALUE_PAIR.
                 */
                tmpl_attr_copy(vpt, map->rhs);
                tmpl_attr_set_leaf_da(vpt, vp->da);
@@ -1706,9 +1674,9 @@ void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
                 *      around the list ref. The attribute value has no quoting, so we choose
                 *      the quoting based on the data type.
                 */
-               value = fr_pair_value_asprint(request, vp, quote[0]);
-               tmpl_snprint(NULL, buffer, sizeof(buffer), vpt);
-               rhs = talloc_typed_asprintf(request, "%s -> %s%s%s", buffer, quote, value, quote);
+               fr_pair_aprint_value_quoted(request, &value, vp, quote);
+               tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vpt, TMPL_ATTR_REF_PREFIX_YES, NULL);
+               rhs = talloc_typed_asprintf(request, "%s -> %s", buffer, value);
 
                talloc_free(vpt);
        }
@@ -1716,18 +1684,25 @@ void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
 
        case TMPL_TYPE_ATTR:
        {
-               char const *quote;
+               fr_token_t      quote;
 
-               quote = (vp->vp_type == FR_TYPE_STRING) ? "\"" : "";
+               switch (vp->vp_type) {
+               case FR_TYPE_QUOTED:
+                       quote = T_DOUBLE_QUOTED_STRING;
+                       break;
+               default:
+                       quote = T_BARE_WORD;
+                       break;
+               }
 
                /*
                 *      Not appropriate to use map->rhs->quote here, as that's the quoting
                 *      around the attr ref. The attribute value has no quoting, so we choose
                 *      the quoting based on the data type.
                 */
-               value = fr_pair_value_asprint(request, vp, quote[0]);
-               tmpl_snprint(NULL, buffer, sizeof(buffer), map->rhs);
-               rhs = talloc_typed_asprintf(request, "%s -> %s%s%s", buffer, quote, value, quote);
+               fr_pair_aprint_value_quoted(request, &value, vp, quote);
+               tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->rhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
+               rhs = talloc_typed_asprintf(request, "%s -> %s", buffer, value);
        }
                break;
 
@@ -1744,14 +1719,14 @@ void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
                 *      map name.
                 */
                if (vp) {
-                       tmpl_snprint(NULL, buffer, sizeof(buffer), map->lhs);
+                       tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);    /* Fixme - bad escaping */
                        RDEBUG2("%s%s %s %s", buffer, vp->da->name, fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"), rhs);
                        break;
                }
                FALL_THROUGH;
 
        case TMPL_TYPE_ATTR:
-               tmpl_snprint(NULL, buffer, sizeof(buffer), map->lhs);
+               tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
                RDEBUG2("%s %s %s", buffer, fr_table_str_by_value(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), rhs);
                break;
 
index bae2350b01cc1ca0e2750041e25cf5056298bffc..981123074d7cc666a42e93e18f93b473416c6cbf 100644 (file)
@@ -44,6 +44,18 @@ typedef struct vp_list_mod_s vp_list_mod_t;
 extern "C" {
 #endif
 
+/** Single character tokens used as terminals for the LHS operand
+ *
+ */
+#define MAP_LHS_TERMINALS \
+       ['-'] = true, \
+       [':'] = true, \
+       ['+'] = true, \
+       ['<'] = true, \
+       ['='] = true, \
+       ['>'] = true, \
+       ['~'] = true
+
 /** Value pair map
  *
  * Value pair maps contain a pair of templates, that describe a src attribute
@@ -130,7 +142,7 @@ int         map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
 int            map_to_request(REQUEST *request, vp_map_t const *map,
                               radius_map_getvalue_t func, void *ctx);
 
-size_t         map_snprint(size_t *need, char *out, size_t outlen, vp_map_t const *map);
+ssize_t                map_print(fr_sbuff_t *out, vp_map_t const *map);
 
 void           map_debug_log(REQUEST *request, vp_map_t const *map,
                              VALUE_PAIR const *vp) CC_HINT(nonnull(1, 2));
index 415cc8ee57a131d30932963afff66b472a510048..f7db4b1b060ce66e03864774ef5547218d701dee 100644 (file)
@@ -80,7 +80,7 @@ static inline vp_list_mod_t *list_mod_generic_afrom_map(TALLOC_CTX *ctx,
        if (!n->mod) return NULL;
        n->mod->lhs = mutated->lhs;
        n->mod->op = mutated->op;
-       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_DATA, NULL, -1, T_BARE_WORD);
+       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0);
        if (!n->mod->rhs) {
                talloc_free(n);
                return NULL;
@@ -120,7 +120,7 @@ static inline vp_list_mod_t *list_mod_delete_afrom_map(TALLOC_CTX *ctx,
 
        n->mod->lhs = mutated->lhs;
        n->mod->op = T_OP_CMP_FALSE;    /* Means delete the LHS */
-       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_NULL, NULL, -1, T_BARE_WORD);
+       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_NULL, T_BARE_WORD, NULL, 0);
        if (!n->mod->rhs) {
                talloc_free(n);
                return NULL;
@@ -165,7 +165,7 @@ static inline vp_list_mod_t *list_mod_empty_string_afrom_map(TALLOC_CTX *ctx,
 
        n->mod->lhs = mutated->lhs;
        n->mod->op = mutated->op;
-       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_DATA, NULL, -1, T_DOUBLE_QUOTED_STRING);
+       n->mod->rhs = tmpl_alloc(n->mod, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, NULL, -1);
        if (!n->mod->rhs) {
                talloc_free(n);
                return NULL;
@@ -395,21 +395,20 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
                         *      the attribute, with the same destination list
                         *      as the current LHS map.
                         */
-                       n_mod->lhs = tmpl_alloc(n, TMPL_TYPE_ATTR, mutated->lhs->name, mutated->lhs->len, T_BARE_WORD);
+                       n_mod->lhs = tmpl_alloc(n, TMPL_TYPE_ATTR, T_BARE_WORD, mutated->lhs->name, mutated->lhs->len);
                        if (!n_mod->lhs) goto error;
 
                        if (tmpl_attr_copy(n_mod->lhs, mutated->lhs) < 0) goto error;
 
                        tmpl_attr_set_leaf_da(n_mod->lhs, vp->da);
-                       tmpl_attr_set_leaf_tag(n_mod->lhs, vp->tag);
 
                        /*
                         *      For the RHS we copy the value of the attribute
                         *      we just found, creating data (literal) tmpl.
                         */
-                       n_mod->rhs = tmpl_alloc(n_mod, TMPL_TYPE_DATA, NULL, -1,
-                                               vp->data.type == FR_TYPE_STRING ?
-                                               T_DOUBLE_QUOTED_STRING : T_BARE_WORD);
+                       n_mod->rhs = tmpl_alloc(n_mod, TMPL_TYPE_DATA,
+                                               vp->data.type == FR_TYPE_STRING ? T_DOUBLE_QUOTED_STRING : T_BARE_WORD,
+                                               NULL, 0);
                        if (!n_mod->rhs) goto error;
 
                        /*
@@ -459,11 +458,8 @@ int map_to_list_mod(TALLOC_CTX *ctx, vp_list_mod_t **out,
 
        switch (mutated->rhs->type) {
        case TMPL_TYPE_XLAT:
-               fr_assert(tmpl_xlat(mutated->rhs) != NULL);
-               FALL_THROUGH;
-
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        {
+               fr_assert(tmpl_xlat(mutated->rhs) != NULL);
                fr_cursor_t     from;
                fr_value_box_t  *vb, *n_vb;
 
@@ -784,8 +780,6 @@ static inline VALUE_PAIR *map_list_mod_to_vp(TALLOC_CTX *ctx, tmpl_t const *attr
        VALUE_PAIR *vp;
 
        MEM(vp = fr_pair_afrom_da(ctx, tmpl_da(attr)));
-       vp->tag = tmpl_tag(attr);
-
        if (fr_value_box_copy(vp, &vp->data, value) < 0) {
                talloc_free(vp);
                return NULL;
@@ -873,7 +867,6 @@ static inline void map_list_mod_debug(REQUEST *request,
         *      Just print the value being assigned
         */
        default:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT:
        case TMPL_TYPE_UNRESOLVED:
        case TMPL_TYPE_DATA:
@@ -882,14 +875,14 @@ static inline void map_list_mod_debug(REQUEST *request,
 
        /*
         *      For the lists, we can't use the original name, and have to
-        *      rebuild it using tmpl_snprint, for each attribute we're
+        *      rebuild it using tmpl_print, for each attribute we're
         *      copying.
         */
        case TMPL_TYPE_LIST:
        {
                char buffer[256];
 
-               tmpl_snprint(NULL, buffer, sizeof(buffer), map->rhs);
+               tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->rhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
                rhs = fr_asprintf(request, "%s -> %s%pV%s", buffer, quote, vb, quote);
        }
                break;
@@ -1015,7 +1008,7 @@ int map_list_mod_apply(REQUEST *request, vp_list_mod_t const *vlm)
                                for (vp_to = fr_cursor_head(&to);
                                     vp_to;
                                     vp_to = fr_cursor_next(&to)) {
-                                       if (fr_pair_cmp_by_da_tag(vp_to, vp) == 0) exists = true;
+                                       if (fr_pair_cmp_by_da(vp_to, vp) == 0) exists = true;
                                }
 
                                if (exists) {
@@ -1078,7 +1071,7 @@ int map_list_mod_apply(REQUEST *request, vp_list_mod_t const *vlm)
                if (tmpl_num(map->lhs) != NUM_ALL) {
                        fr_cursor_free_item(&list);
                /*
-                *      Wildcard: delete all of the matching ones, based on tag.
+                *      Wildcard: delete all of the matching ones
                 */
                } else {
                        fr_cursor_free_list(&list);             /* Remember, we're using a custom iterator */
index a85fd7cf6aeebb80b0278f3fcd411f02080579c5..d3db00af0cd46de5a468e0afcf8135176cacc85a 100644 (file)
@@ -129,8 +129,8 @@ static int prefix_suffix_cmp(UNUSED void *instance,
 
        if (!request) return -1;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name, TAG_ANY);
-       if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name);
+       if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) return -1;
 
        VP_VERIFY(check);
@@ -158,13 +158,13 @@ static int prefix_suffix_cmp(UNUSED void *instance,
        /*
         *      If Strip-User-Name == No, then don't do any more.
         */
-       vp = fr_pair_find_by_da(check_list, attr_strip_user_name, TAG_ANY);
+       vp = fr_pair_find_by_da(check_list, attr_strip_user_name);
        if (vp && !vp->vp_uint32) return ret;
 
        /*
         *      See where to put the stripped user name.
         */
-       vp = fr_pair_find_by_da(check_list, attr_stripped_user_name, TAG_ANY);
+       vp = fr_pair_find_by_da(check_list, attr_stripped_user_name);
        if (!vp) {
                /*
                 *      If "request" is NULL, then the memory will be
@@ -327,13 +327,15 @@ int paircmp_pairs(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
                if (check->vp_type == FR_TYPE_STRING) {
                        expr_p = check->vp_strvalue;
                } else {
-                       expr_p = expr = fr_pair_value_asprint(check, check, '\0');
+                       fr_value_box_aprint(check, &expr, &check->data, NULL);
+                       expr_p = expr;
                }
 
                if (vp->vp_type == FR_TYPE_STRING) {
                        value_p = vp->vp_strvalue;
                } else {
-                       value_p = value = fr_pair_value_asprint(vp, vp, '\0');
+                       fr_value_box_aprint(vp, &value, &vp->data, NULL);
+                       value_p = value;
                }
 
                if (!expr_p || !value_p) {
@@ -400,15 +402,6 @@ int paircmp_pairs(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
         */
        if (vp->vp_type != check->vp_type) return -1;
 
-       /*
-        *      Tagged attributes are equal if and only if both the
-        *      tag AND value match.
-        */
-       if (check->da->flags.has_tag && !TAG_EQ(check->tag, vp->tag)) {
-               ret = ((int) vp->tag) - ((int) check->tag);
-               if (ret != 0) goto finish;
-       }
-
        /*
         *      Not a regular expression, compare the types.
         */
@@ -614,7 +607,7 @@ int paircmp(REQUEST *request,
                                WARN("Are you sure you don't mean Cleartext-Password?");
                                WARN("See \"man rlm_pap\" for more information");
                        }
-                       if (fr_pair_find_by_num(request_list, 0, FR_USER_PASSWORD, TAG_ANY) == NULL) continue;
+                       if (fr_pair_find_by_num(request_list, 0, FR_USER_PASSWORD) == NULL) continue;
                }
 
                /*
index 3bf71a53a95255d86c1ec23adbe6f359b51ee6ed..c236665d3e695787846894695db798c39b3b0eed 100644 (file)
@@ -33,13 +33,13 @@ RCSID("$Id$")
 #include <freeradius-devel/server/rcode.h>
 
 fr_table_num_sorted_t const rcode_table[] = {
-       { L("disallow"),                RLM_MODULE_DISALLOW     },
+       { L("disallow"),        RLM_MODULE_DISALLOW     },
        { L("fail"),            RLM_MODULE_FAIL         },
        { L("handled"),         RLM_MODULE_HANDLED      },
        { L("invalid"),         RLM_MODULE_INVALID      },
        { L("noop"),            RLM_MODULE_NOOP         },
-       { L("notfound"),                RLM_MODULE_NOTFOUND     },
-       { L("ok"),                      RLM_MODULE_OK           },
+       { L("notfound"),        RLM_MODULE_NOTFOUND     },
+       { L("ok"),              RLM_MODULE_OK           },
        { L("reject"),          RLM_MODULE_REJECT       },
        { L("updated"),         RLM_MODULE_UPDATED      },
 };
index a53f96c08789862003cf2e815166274aeeb702a6..3321bf6691c965cd1208e7a4cb1ca5da4625c420 100644 (file)
@@ -412,7 +412,7 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, REQUEST *req
         *      int the reply, we use that in preference to the
         *      old state.
         */
-       vp = fr_pair_find_by_da(packet->vps, state->da, TAG_ANY);
+       vp = fr_pair_find_by_da(packet->vps, state->da);
        if (vp) {
                if (DEBUG_ENABLED && (vp->vp_length > sizeof(entry->state))) {
                        WARN("State too long, will be truncated.  Expected <= %zd bytes, got %zu bytes",
@@ -558,7 +558,7 @@ void fr_state_discard(fr_state_tree_t *state, REQUEST *request)
        fr_state_entry_t        *entry;
        VALUE_PAIR              *vp;
 
-       vp = fr_pair_find_by_da(request->packet->vps, state->da, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, state->da);
        if (!vp) return;
 
        PTHREAD_MUTEX_LOCK(&state->mutex);
@@ -614,7 +614,7 @@ void fr_state_to_request(fr_state_tree_t *state, REQUEST *request)
        /*
         *      No State, don't do anything.
         */
-       vp = fr_pair_find_by_da(request->packet->vps, state->da, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, state->da);
        if (!vp) {
                RDEBUG3("No &request:State attribute, can't restore &session-state");
                if (request->seq_start == 0) request->seq_start = request->number;      /* Need check for fake requests */
@@ -685,7 +685,7 @@ int fr_request_to_state(fr_state_tree_t *state, REQUEST *request)
                log_request_pair_list(L_DBG_LVL_2, request, request->state, "&session-state:");
        }
 
-       vp = fr_pair_find_by_da(request->packet->vps, state->da, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, state->da);
 
        PTHREAD_MUTEX_LOCK(&state->mutex);
        if (vp) old = state_entry_find(state, request, &vp->data);
index c65cf4b5ade0382350eb0b7874fbc60bcbf07c28..9442687b562151f725b00c428fa668ed4e932d12 100644 (file)
@@ -35,31 +35,45 @@ RCSID("$Id$")
 #include <freeradius-devel/util/hex.h>
 #include <freeradius-devel/util/misc.h>
 
+#include <freeradius-devel/util/sbuff.h>
+
 #include <ctype.h>
 
 /** Map #tmpl_type_t values to descriptive strings
  */
-fr_table_num_sorted_t const tmpl_type_table[] = {
-       { L("uninitialised"),   TMPL_TYPE_UNINITIALISED         },
+fr_table_num_ordered_t const tmpl_type_table[] = {
+       { L("uninitialised"),           TMPL_TYPE_UNINITIALISED         },
 
-       { L("null"),            TMPL_TYPE_NULL                  },
-       { L("data"),            TMPL_TYPE_DATA                  },
+       { L("null"),                    TMPL_TYPE_NULL                  },
+       { L("data"),                    TMPL_TYPE_DATA                  },
 
-       { L("attr"),            TMPL_TYPE_ATTR                  },
-       { L("list"),            TMPL_TYPE_LIST                  },
+       { L("attr"),                    TMPL_TYPE_ATTR                  },
+       { L("list"),                    TMPL_TYPE_LIST                  },
 
-       { L("exec"),            TMPL_TYPE_EXEC                  },
-       { L("xlat"),            TMPL_TYPE_XLAT                  },
+       { L("exec"),                    TMPL_TYPE_EXEC                  },
+       { L("xlat"),                    TMPL_TYPE_XLAT                  },
 
-       { L("regex"),           TMPL_TYPE_REGEX                 },
+       { L("regex"),                   TMPL_TYPE_REGEX                 },
+       { L("regex-uncompiled"),        TMPL_TYPE_REGEX_UNCOMPILED      },
+       { L("regex-xlat"),              TMPL_TYPE_REGEX_XLAT            },
 
-       { L("literal"),         TMPL_TYPE_UNRESOLVED            },
-       { L("attr-unparsed"),   TMPL_TYPE_ATTR_UNRESOLVED               },
-       { L("xlat-unparsed"),   TMPL_TYPE_XLAT_UNRESOLVED               },
-       { L("regex-unparsed"),  TMPL_TYPE_REGEX_UNRESOLVED      }
+       { L("unresolved"),              TMPL_TYPE_UNRESOLVED            },
+       { L("attr-unresolved"),         TMPL_TYPE_ATTR_UNRESOLVED       },
+       { L("exec-unresolved"),         TMPL_TYPE_EXEC_UNRESOLVED       },
+       { L("xlat-unresolved"),         TMPL_TYPE_XLAT_UNRESOLVED       },
+       { L("regex-unresolved"),        TMPL_TYPE_REGEX_XLAT_UNRESOLVED }
 };
 size_t tmpl_type_table_len = NUM_ELEMENTS(tmpl_type_table);
 
+/** Attr ref types
+ */
+fr_table_num_ordered_t const attr_ref_table[] = {
+       { L("normal"),          TMPL_ATTR_TYPE_NORMAL           },
+       { L("unknown"),         TMPL_ATTR_TYPE_UNKNOWN          },
+       { L("unresolved"),      TMPL_ATTR_TYPE_UNRESOLVED       }
+};
+size_t attr_ref_table_len = NUM_ELEMENTS(attr_ref_table);
+
 /** Map keywords to #pair_list_t values
  */
 fr_table_num_ordered_t const pair_list_table[] = {
@@ -84,13 +98,181 @@ size_t request_ref_table_len = NUM_ELEMENTS(request_ref_table);
 /** Special attribute reference indexes
  */
 static fr_table_num_sorted_t const attr_num_table[] = {
-       { L("*"),                       NUM_ALL                         },
-       { L("#"),                       NUM_COUNT                       },
+       { L("*"),               NUM_ALL                         },
+       { L("#"),               NUM_COUNT                       },
        { L("any"),             NUM_ANY                         },
-       { L("n"),                       NUM_LAST                        }
+       { L("n"),               NUM_LAST                        }
 };
 static size_t attr_num_table_len = NUM_ELEMENTS(attr_num_table);
 
+static void tmpl_attr_ref_to_raw(tmpl_t *vpt, tmpl_attr_t *ref);
+
+void tmpl_attr_debug(tmpl_t const *vpt)
+{
+       tmpl_attr_t             *ar = NULL;
+       tmpl_request_t          *rr = NULL;
+       unsigned int            i = 0;
+       char                    buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
+
+       switch (vpt->type) {
+       case TMPL_TYPE_ATTR:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+       case TMPL_TYPE_LIST:
+               break;
+
+       default:
+               INFO("%s can't print tmpls of type %s", __FUNCTION__,
+                    fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
+               return;
+       }
+
+       INFO("tmpl_t %s (%.8x) \"%pV\" (%p)",
+            fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"),
+            vpt->type,
+            fr_box_strvalue_len(vpt->name, vpt->len), vpt);
+
+       INFO("\tquote  : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
+
+       INFO("request references:");
+
+       /*
+        *      Print all the request references
+        */
+       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+               INFO("\t[%u] %s (%u)", i,
+                    fr_table_str_by_value(request_ref_table, rr->request, "<INVALID>"), rr->request);
+               i++;
+       }
+       i = 0;
+
+       INFO("list: %s", fr_table_str_by_value(pair_list_table, vpt->data.attribute.list, "<INVALID>"));
+
+       INFO("attribute references:");
+       /*
+        *      Print all the attribute references
+        */
+       while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+               snprintf(buffer, sizeof(buffer), "%i", ar->num);
+
+               switch (ar->type) {
+               case TMPL_ATTR_TYPE_NORMAL:
+               case TMPL_ATTR_TYPE_UNKNOWN:
+                       if (!ar->da) {
+                               INFO("\t[%u] %s null%s%s%s",
+                                    i,
+                                    fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+                                    ar->num != NUM_ANY ? "[" : "",
+                                    ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+                                    ar->num != NUM_ANY ? "]" : "");
+                               goto next;
+                       }
+
+                       INFO("\t[%u] %s %s %s%s%s%s (%u)",
+                            i,
+                            fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+                            fr_table_str_by_value(fr_value_box_type_table, ar->da->type, "<INVALID>"),
+                            ar->da->name,
+                            ar->num != NUM_ANY ? "[" : "",
+                            ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+                            ar->num != NUM_ANY ? "]" : "",
+                            ar->da->attr
+                       );
+                       if (ar->da->parent) INFO("\t    parent     : %s", ar->da->parent->name);
+                       INFO("\t    is_raw     : %pV", fr_box_bool(ar->da->flags.is_raw));
+                       INFO("\t    is_unknown : %pV", fr_box_bool(ar->da->flags.is_unknown));
+                       break;
+
+
+               case TMPL_ATTR_TYPE_UNRESOLVED:
+                       INFO("\t[%u] %s %s%s%s%s - unresolved",
+                            i,
+                            fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+                            ar->ar_unresolved,
+                            ar->num != NUM_ANY ? "[" : "",
+                            ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+                            ar->num != NUM_ANY ? "]" : "");
+                       break;
+
+               default:
+                       INFO("\t[%u] Bad type %s(%u)",
+                            i, fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"), ar->type);
+                       break;
+               }
+
+       next:
+               i++;
+       }
+}
+
+void tmpl_debug(tmpl_t const *vpt)
+{
+       switch (vpt->type) {
+       case TMPL_TYPE_ATTR:
+       case TMPL_TYPE_LIST:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+               tmpl_attr_debug(vpt);
+               return;
+
+       default:
+               break;
+       }
+
+       INFO("tmpl_t %s (%.8x) \"%pR\" (%p)",
+            fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"),
+            vpt->type,
+            fr_box_strvalue_len(vpt->name, vpt->len), vpt);
+
+       INFO("\tquote      : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
+       switch (vpt->type) {
+       case TMPL_TYPE_NULL:
+               return;
+
+       case TMPL_TYPE_DATA:
+               INFO("\ttype       : %s", fr_table_str_by_value(fr_value_box_type_table,
+                                                           tmpl_value_type(vpt), "<INVALID>"));
+               INFO("\tlen        : %zu", tmpl_value_length(vpt));
+               INFO("\tvalue      : %pV", tmpl_value(vpt));
+
+               if (tmpl_value_enumv(vpt)) INFO("\tenumv      : %s (%p)",
+                                               tmpl_value_enumv(vpt)->name, tmpl_value_enumv(vpt));
+               return;
+
+       case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_EXEC:
+       case TMPL_TYPE_REGEX_XLAT:
+       {
+               char *str;
+
+               xlat_aprint(NULL, &str, tmpl_xlat(vpt), NULL);
+
+               INFO("\texpansion  : %pR", fr_box_strvalue_buffer(str));
+
+               talloc_free(str);
+       }
+               break;
+
+       case TMPL_TYPE_REGEX:
+       {
+               INFO("\tpattern    : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
+       }
+               break;
+
+       default:
+               if (tmpl_needs_resolving(vpt)) {
+                       if (tmpl_is_unresolved(vpt)) {
+                               INFO("\tunescaped  : %pR", fr_box_strvalue_buffer(vpt->data.unescaped));
+                               INFO("\tlen        : %zu", talloc_array_length(vpt->data.unescaped) - 1);
+                       } else {
+                               INFO("\tunresolved : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
+                               INFO("\tlen        : %zu", vpt->len);
+                       }
+               } else {
+                       INFO("debug nyi");
+               }
+               break;
+       }
+}
+
 /** @name Parse list and request qualifiers to #pair_list_t and #request_ref_t values
  *
  * These functions also resolve #pair_list_t and #request_ref_t values to #REQUEST
@@ -157,10 +339,7 @@ size_t radius_list_name(pair_list_t *out, char const *name, pair_list_t def)
                return 0;
 
        /*
-        *      It may be a list qualifier delimiter. Because of tags
-        *      We need to check that it doesn't look like a tag suffix.
-        *      We do this by looking at the chars between ':' and the
-        *      next token delimiter, and seeing if they're all digits.
+        *      It may be a list qualifier delimiter
         */
        case ':':
        {
@@ -169,11 +348,6 @@ size_t radius_list_name(pair_list_t *out, char const *name, pair_list_t def)
                if (isdigit((int) *d)) {
                        while (isdigit((int) *d)) d++;
 
-                       /*
-                        *      Char after the number string
-                        *      was a token delimiter, so this is a
-                        *      tag, not a list qualifier.
-                        */
                        if (!fr_dict_attr_allowed_chars[(uint8_t) *d]) {
                                *out = def;
                                return 0;
@@ -404,37 +578,24 @@ int radius_request(REQUEST **context, request_ref_t name)
  * @{
  */
 
-/** Initialise stack allocated #tmpl_t
- *
- * @note Name is not talloc_strdup'd or memcpy'd so must be available, and must not change
- *     for the lifetime of the #tmpl_t.
+/** Initialise fields inside a tmpl depending on its type
  *
- * @param[out] vpt to initialise.
- * @param[in] type to set in the #tmpl_t.
- * @param[in] name of the #tmpl_t.
- * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
- *     If < 0 strlen will be used to determine the length.
- * @param[in] quote The type of quoting around the template name.
- * @return a pointer to the initialised #tmpl_t. The same value as vpt.
  */
-tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len, fr_token_t quote)
+static inline CC_HINT(always_inline) void tmpl_type_init(tmpl_t *vpt, tmpl_type_t type)
 {
-       fr_assert(vpt);
-       fr_assert(type != TMPL_TYPE_UNINITIALISED);
 
-       memset(vpt, 0, sizeof(tmpl_t));
-       vpt->type = type;
-
-       if (name) {
-               vpt->name = name;
-               vpt->len = len < 0 ? strlen(name) :
-                                    (size_t) len;
-               vpt->quote = quote;
-       }
+       switch (type) {
+#ifndef HAVE_REGEX
+       case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
+               fr_assert(0);
+               return;
+#endif
 
-       switch (type) {
        case TMPL_TYPE_ATTR:
-       case TMPL_ATTR_TYPE_UNRESOLVED:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
        case TMPL_TYPE_LIST:
                fr_dlist_talloc_init(&vpt->data.attribute.ar, tmpl_attr_t, entry);
                fr_dlist_talloc_init(&vpt->data.attribute.rr, tmpl_request_t, entry);
@@ -443,157 +604,235 @@ tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len,
        default:
                break;
        }
+       vpt->type = type;
+ }
 
-       return vpt;
-}
-
-/** Create a new heap allocated #tmpl_t
+/** Set the name on a pre-initialised tmpl
  *
- * @param[in,out] ctx to allocate in.
- * @param[in] type to set in the #tmpl_t.
- * @param[in] name of the #tmpl_t (will be copied to a new talloc buffer parented
- *     by the #tmpl_t).
- * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
- *     If < 0 strlen will be used to determine the length.
- * @param[in] quote The type of quoting around the template name.
- * @return the newly allocated #tmpl_t.
+ * @param[in] vpt      to set the name for.
+ * @param[in] quote    Original quoting around the name.
+ * @param[in] fmt      string.
+ * @param[in] ...      format arguments.
  */
-tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name, ssize_t len, fr_token_t quote)
+void tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...)
 {
-       tmpl_t *vpt;
+       va_list         ap;
+       char const      *old = NULL;
 
-#ifndef HAVE_REGEX
-       if ((type == TMPL_TYPE_REGEX_UNRESOLVED) || (type == TMPL_TYPE_REGEX)) return NULL;
-#endif
+       if (vpt->type != TMPL_TYPE_UNINITIALISED) old = vpt->name;
 
-       /*
-        *      Allocate enough memory to hold at least
-        *      one attribute reference and one request
-        *      reference.
-        */
-       MEM(vpt = talloc_zero_pooled_object(ctx, tmpl_t, 2, sizeof(tmpl_request_t) + sizeof(tmpl_attr_t)));
-       vpt->type = type;
+       va_start(ap, fmt);
+       vpt->name = fr_vasprintf(vpt, fmt, ap);
+       vpt->quote = quote;
+       vpt->len = talloc_array_length(vpt->name) - 1;
+       va_end(ap);
 
-       /*
-        *      Can't use compound literal for init
-        *      because we can't set the type field
-        *      in the embedded value box.
-        */
-       if (name) {
-               vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
-               vpt->len = talloc_array_length(vpt->name) - 1;
-               vpt->quote = quote;
-       }
+       talloc_const_free(old); /* Free name last so it can be used in the format string */
+}
 
-       /*
-        *      Don't add default here.  We want to warn
-        *      if there may be special initialisation
-        *      needed.
-        */
-       switch (type) {
-       case TMPL_TYPE_ATTR:
-       case TMPL_TYPE_ATTR_UNRESOLVED:
-       case TMPL_TYPE_LIST:
-               fr_dlist_talloc_init(&vpt->data.attribute.ar, tmpl_attr_t, entry);
-               fr_dlist_talloc_init(&vpt->data.attribute.rr, tmpl_request_t, entry);
-               break;
+/** Set the name on a pre-initialised tmpl
+ *
+ * @param[in] vpt      to set the name for.
+ * @param[in] quote    Original quoting around the name.
+ * @param[in] name     of the #tmpl_t.
+ * @param[in] len      The length of the buffer (or a substring of the buffer) pointed to by name.
+ *                     If < 0 strlen will be used to determine the length.
+ */
+void tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
+{
+       fr_assert(vpt->type != TMPL_TYPE_UNINITIALISED);
 
-       case TMPL_TYPE_NULL:
-       case TMPL_TYPE_DATA:
-       case TMPL_TYPE_EXEC:
-       case TMPL_TYPE_XLAT:
-       case TMPL_TYPE_REGEX:
-       case TMPL_TYPE_UNRESOLVED:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
-       case TMPL_TYPE_REGEX_UNRESOLVED:
-               break;
+       vpt->name = name;
+       vpt->len = len < 0 ? strlen(name) : (size_t)len;
+       vpt->quote = quote;
+}
 
-       case TMPL_TYPE_UNINITIALISED:
-       case TMPL_TYPE_MAX:
-               fr_assert(0);
-       }
+/** Set the name on a pre-initialised tmpl
+ *
+ * @param[in] vpt      to set the name for.
+ * @param[in] quote    Original quoting around the name.
+ * @param[in] name     of the #tmpl_t.
+ * @param[in] len      The length of the buffer (or a substring of the buffer) pointed to by name.
+ *                     If < 0 strlen will be used to determine the length.
+ */
+void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len)
+{
+       fr_assert(vpt->type != TMPL_TYPE_UNINITIALISED);
 
-       return vpt;
+       talloc_const_free(vpt->name);
+
+       vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
+       vpt->len = talloc_array_length(vpt->name) - 1;
+       vpt->quote = quote;
 }
 
-/** Set a new name for a tmpl_t
+/** Initialise a tmpl using a format string to create the name
  *
- * @param[in] vpt      to set name for.
+ * @param[in] vpt      to initialise.
  * @param[in] quote    Original quoting around the name.
  * @param[in] fmt      string.
  * @param[in] ...      format arguments.
+ * @return A pointer to the newly initialised tmpl.
  */
-void tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...)
+tmpl_t *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...)
 {
        va_list         ap;
-       char const      *old;
-
-       old = vpt->name;
-       if (fmt) {
-               va_start(ap, fmt);
-               MEM(vpt->name = fr_vasprintf(vpt, fmt, ap));
-               va_end(ap);
-               vpt->len = talloc_array_length(vpt->name) - 1;
-       } else {
-               vpt->name = NULL;
-               vpt->len = 0;
-       }
-       talloc_const_free(old); /* Do this last, so the caller can pass in the current name */
+
+       fr_assert(vpt->type == TMPL_TYPE_UNINITIALISED);
+
+       memset(vpt, 0, sizeof(*vpt));
+       tmpl_type_init(vpt, type);
+
+       va_start(ap, fmt);
+       vpt->name = fr_vasprintf(vpt, fmt, ap),
+       vpt->len = talloc_array_length(vpt->name) - 1;
        vpt->quote = quote;
+       va_end(ap);
+
+       return vpt;
 }
-/** @} */
 
-/** @name Create new #tmpl_t from a string
+/** Initialise a tmpl without copying the input name string
  *
- * @{
- */
-
- /** Allocate a new request reference and add it to the end of the attribute reference list
+ * @note Name is not talloc_strdup'd or memcpy'd so must be available, and must not change
+ *     for the lifetime of the #tmpl_t.
  *
+ * @param[out] vpt     to initialise.
+ * @param[in] type     to set in the #tmpl_t.
+ * @param[in] quote    The type of quoting around the template name.
+ * @param[in] name     of the #tmpl_t.
+ * @param[in] len      The length of the buffer (or a substring of the buffer) pointed to by name.
+ *                     If < 0 strlen will be used to determine the length.
+ * @return a pointer to the initialised #tmpl_t. The same value as vpt.
  */
-static tmpl_request_t *tmpl_rr_add(tmpl_t *vpt, request_ref_t request)
+tmpl_t *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
 {
-       tmpl_request_t  *rr;
-       TALLOC_CTX              *ctx;
-
-       if (fr_dlist_num_elements(&vpt->data.attribute.rr) == 0) {
-               ctx = vpt;
-       } else {
-               ctx = fr_dlist_tail(&vpt->data.attribute.rr);
-       }
+       fr_assert(vpt->type == TMPL_TYPE_UNINITIALISED);
 
-       MEM(rr = talloc_zero(ctx, tmpl_request_t));
-       rr->request = request;
-
-       fr_dlist_insert_tail(&vpt->data.attribute.rr, rr);
+       memset(vpt, 0, sizeof(*vpt));
+       tmpl_type_init(vpt, type);
+       tmpl_set_name_shallow(vpt, quote, name, len);
 
-       return rr;
+       return vpt;
 }
 
-/** Allocate a new attribute reference and add it to the end of the attribute reference list
+/** Initialise a tmpl using a literal string to create the name
  *
+ * @param[in] vpt      to initialise.
+ * @param[in] quote    Original quoting around the name.
+ * @param[in] name     to set for the tmpl.
+ * @param[in] len      Name length.  If < 0 strlen will be used
+ *                     to determine the name.
+ * @return A pointer to the newly initialised tmpl.
  */
-static tmpl_attr_t *tmpl_ar_add(tmpl_t *vpt, tmpl_attr_type_t type)
+tmpl_t *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
 {
-       tmpl_attr_t     *ar;
-       TALLOC_CTX      *ctx;
-
-       if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
-               ctx = vpt;
-       } else {
-               ctx = fr_dlist_tail(&vpt->data.attribute.ar);
-       }
+       fr_assert(vpt->type == TMPL_TYPE_UNINITIALISED);
 
-       MEM(ar = talloc_zero(ctx, tmpl_attr_t));
-       ar->type = type;
-       ar->num = NUM_ANY;
-
-       fr_dlist_insert_tail(&vpt->data.attribute.ar, ar);
+       memset(vpt, 0, sizeof(*vpt));
+       tmpl_type_init(vpt, type);
+       tmpl_set_name(vpt, quote, name, len);
 
-       return ar;
+       return vpt;
 }
 
-/** Create a #tmpl_t from a #fr_value_box_t
+/** Create a new heap allocated #tmpl_t
+ *
+ * Must be later initialised with a tmpl_init_* function.
+ *
+ * This function is provided to allow tmpls to be pre-allocated for talloc purposes before
+ * their name is known.
+ */
+static inline CC_HINT(always_inline) tmpl_t *tmpl_alloc_null(TALLOC_CTX *ctx)
+{
+       tmpl_t *vpt;
+
+       /*
+        *      Allocate enough memory to hold at least
+        *      one attribute reference and one request
+        *      reference.
+        */
+       MEM(vpt = talloc_pooled_object(ctx, tmpl_t, 2, sizeof(tmpl_request_t) + sizeof(tmpl_attr_t)));
+       vpt->type = TMPL_TYPE_UNINITIALISED;
+
+       return vpt;
+}
+
+/** Create a new heap allocated #tmpl_t
+ *
+ * @param[in,out] ctx to allocate in.
+ * @param[in] type to set in the #tmpl_t.
+ * @param[in] name of the #tmpl_t (will be copied to a new talloc buffer parented
+ *     by the #tmpl_t).
+ * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
+ *     If < 0 strlen will be used to determine the length.
+ * @param[in] quote The type of quoting around the template name.
+ * @return the newly allocated #tmpl_t.
+ */
+tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len)
+{
+       tmpl_t *vpt;
+
+       vpt = tmpl_alloc_null(ctx);
+       memset(vpt, 0, sizeof(*vpt));
+
+       tmpl_type_init(vpt, type);
+       if (name) tmpl_set_name(vpt, quote, name, len);
+
+       return vpt;
+}
+
+/** @name Create new #tmpl_t from a string
+ *
+ * @{
+ */
+
+ /** Allocate a new request reference and add it to the end of the attribute reference list
+ *
+ */
+static tmpl_request_t *tmpl_req_ref_add(tmpl_t *vpt, request_ref_t request)
+{
+       tmpl_request_t  *rr;
+       TALLOC_CTX      *ctx;
+
+       if (fr_dlist_num_elements(&vpt->data.attribute.rr) == 0) {
+               ctx = vpt;
+       } else {
+               ctx = fr_dlist_tail(&vpt->data.attribute.rr);
+       }
+
+       MEM(rr = talloc_zero(ctx, tmpl_request_t));
+       rr->request = request;
+
+       fr_dlist_insert_tail(&vpt->data.attribute.rr, rr);
+
+       return rr;
+}
+
+/** Allocate a new attribute reference and add it to the end of the attribute reference list
+ *
+ */
+static tmpl_attr_t *tmpl_attr_ref_add(tmpl_t *vpt, tmpl_attr_type_t type)
+{
+       tmpl_attr_t     *ar;
+       TALLOC_CTX      *ctx;
+
+       if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
+               ctx = vpt;
+       } else {
+               ctx = fr_dlist_tail(&vpt->data.attribute.ar);
+       }
+
+       MEM(ar = talloc_zero(ctx, tmpl_attr_t));
+       ar->type = type;
+       ar->num = NUM_ANY;
+
+       fr_dlist_insert_tail(&vpt->data.attribute.ar, ar);
+
+       return ar;
+}
+
+/** Create a #tmpl_t from a #fr_value_box_t
  *
  * @param[in,out] ctx  to allocate #tmpl_t in.
  * @param[out] out     Where to write pointer to new #tmpl_t.
@@ -606,13 +845,14 @@ static tmpl_attr_t *tmpl_ar_add(tmpl_t *vpt, tmpl_attr_type_t type)
  */
 int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
 {
-       char const *name;
-       tmpl_t *vpt;
+       char            *name;
+       size_t          len;
+       tmpl_t          *vpt;
+       fr_token_t      quote = (data->type == FR_TYPE_STRING) ? T_SINGLE_QUOTED_STRING : T_BARE_WORD;
 
-       vpt = talloc(ctx, tmpl_t);
-       name = fr_value_box_asprint(vpt, data, '\0');
-       tmpl_init(vpt, TMPL_TYPE_DATA, name, talloc_array_length(name),
-                 (data->type == FR_TYPE_STRING) ? T_SINGLE_QUOTED_STRING : T_BARE_WORD);
+       MEM(vpt = talloc(ctx, tmpl_t));
+       len = fr_value_box_aprint(vpt, &name, data, fr_value_escape_by_quote[quote]);
+       tmpl_init_shallow(vpt, TMPL_TYPE_DATA, quote, name, len);
 
        if (steal) {
                if (fr_value_box_steal(vpt, tmpl_value(vpt), data) < 0) {
@@ -630,89 +870,6 @@ int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bo
        return 0;
 }
 
-void tmpl_attr_debug(tmpl_t const *vpt)
-{
-       tmpl_attr_t             *ar = NULL;
-       tmpl_request_t  *rr = NULL;
-       unsigned int            i = 0;
-       char                    buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
-
-       switch (vpt->type) {
-       case TMPL_TYPE_ATTR:
-       case TMPL_TYPE_ATTR_UNRESOLVED:
-       case TMPL_TYPE_LIST:
-               break;
-
-       default:
-               INFO("%s can't print tmpls of type %s", __FUNCTION__,
-                    fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
-               return;
-       }
-
-       INFO("tmpl_t %s %s (%p)", fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"), vpt->name, vpt);
-       INFO("request references:");
-
-       /*
-        *      Print all the request references
-        */
-       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
-               INFO("\t[%u] %s (%u)", i,
-                    fr_table_str_by_value(request_ref_table, rr->request, "<INVALID>"), rr->request);
-               i++;
-       }
-       i = 0;
-
-       INFO("attribute references:");
-       /*
-        *      Print all the attribute references
-        */
-       while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
-               snprintf(buffer, sizeof(buffer), "%i", ar->num);
-
-               switch (ar->type) {
-               case TMPL_ATTR_TYPE_NORMAL:
-               case TMPL_ATTR_TYPE_UNKNOWN:
-                       if (!ar->da) {
-                               INFO("\t[%u] null%s%s%s",
-                                    i,
-                                    ar->num != NUM_ANY ? "[" : "",
-                                    ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
-                                    ar->num != NUM_ANY ? "]" : "");
-                               goto next;
-                       }
-
-
-                       INFO("\t[%u] %s %s(%u)%s%s%s%s",
-                            i,
-                            fr_table_str_by_value(fr_value_box_type_table, ar->da->type, "<INVALID>"),
-                            ar->da->name,
-                            ar->da->attr,
-                            ar->num != NUM_ANY ? "[" : "",
-                            ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
-                            ar->num != NUM_ANY ? "]" : "",
-                            ar->da->flags.is_unknown ? " - is_unknown" : ""
-                       );
-                       break;
-
-
-               case TMPL_ATTR_TYPE_UNRESOLVED:
-                       INFO("\t[%u] %s%s%s%s - unparsed",
-                            i, ar->unknown.name,
-                            ar->num != NUM_ANY ? "[" : "",
-                            ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
-                            ar->num != NUM_ANY ? "]" : "");
-                       break;
-
-               default:
-                       INFO("\t[%u] Bad type %u", i, ar->type);
-                       break;
-               }
-
-       next:
-               i++;
-       }
-}
-
 /** Copy a list of attribute and request references from one tmpl to another
  *
  */
@@ -727,7 +884,7 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
        if (fr_dlist_num_elements(&dst->data.attribute.ar) > 0) fr_dlist_talloc_reverse_free(&dst->data.attribute.ar);
 
        while ((src_ar = fr_dlist_next(&src->data.attribute.ar, src_ar))) {
-               dst_ar = tmpl_ar_add(dst, src_ar->type);
+               dst_ar = tmpl_attr_ref_add(dst, src_ar->type);
 
                switch (src_ar->type) {
                case TMPL_ATTR_TYPE_NORMAL:
@@ -745,8 +902,6 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
                default:
                        if (!fr_cond_assert(0)) return -1;
                }
-
-               dst_ar->ar_tag = src_ar->ar_tag;
                dst_ar->ar_num = src_ar->ar_num;
        }
 
@@ -756,7 +911,7 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
        if (fr_dlist_num_elements(&dst->data.attribute.rr) > 0) fr_dlist_talloc_reverse_free(&dst->data.attribute.rr);
 
        while ((src_rr = fr_dlist_next(&src->data.attribute.rr, src_rr))) {
-               MEM(dst_rr = tmpl_rr_add(dst, src_rr->request));
+               MEM(dst_rr = tmpl_req_ref_add(dst, src_rr->request));
        }
 
        /*
@@ -769,85 +924,6 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src)
        return 0;
 }
 
-/** Convert an abstract da into a concrete one
- *
- * Usually used to fixup combo ip addresses
- */
-int tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type)
-{
-       fr_dict_attr_t const    *abstract;
-       fr_dict_attr_t const    *concrete;
-       tmpl_attr_t     *ref;
-
-       tmpl_assert_type(tmpl_is_attr(vpt));
-
-       abstract = tmpl_da(vpt);
-       if (abstract->type != FR_TYPE_COMBO_IP_ADDR) {
-               fr_strerror_printf("Abstract attribute \"%s\" is of incorrect type '%s'", abstract->name,
-                                  fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"));
-               return -1;
-       }
-
-       concrete = fr_dict_attr_by_type(abstract, type);
-       if (!concrete) {
-               fr_strerror_printf("Can't convert abstract type '%s' to concrete type '%s'",
-                                  fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"),
-                                  fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"));
-               return -1;
-       }
-
-       ref = fr_dlist_tail(&vpt->data.attribute.ar);
-       ref->da = concrete;
-
-       TMPL_ATTR_VERIFY(vpt);
-
-       return 0;
-}
-
-/** Covert the leaf attribute of a tmpl to a unknown/raw type
- *
- */
-void tmpl_attr_to_raw(tmpl_t *vpt)
-{
-       tmpl_attr_t *ref;
-
-       ref = fr_dlist_tail(&vpt->data.attribute.ar);
-       switch (ref->type) {
-       case TMPL_ATTR_TYPE_NORMAL:
-       {
-               char            buffer[256] = "Attr-";
-               char            *p = buffer + strlen(buffer);
-               char            *end = buffer + sizeof(buffer);
-               size_t          len;
-               fr_dict_attr_t  *da;
-
-               len = fr_dict_print_attr_oid(NULL, p, end - p, NULL, ref->da);
-               p += len;
-
-               ref->da = ref->ar_unknown = da = fr_dict_unknown_acopy(vpt, ref->da);
-               ref->ar_unknown->type = FR_TYPE_OCTETS;
-               ref->ar_unknown->flags.is_raw = 1;
-
-               talloc_const_free(da->name);
-               MEM(da->name = talloc_bstrndup(da, buffer, p - buffer));
-
-               ref->type = TMPL_ATTR_TYPE_UNKNOWN;
-       }
-               break;
-
-       case TMPL_ATTR_TYPE_UNKNOWN:
-               ref->ar_unknown->type = FR_TYPE_OCTETS;
-               ref->ar_unknown->flags.is_raw = 1;
-               break;
-
-       case TMPL_ATTR_TYPE_UNRESOLVED:
-               fr_assert(0);
-               break;
-       }
-
-       TMPL_ATTR_VERIFY(vpt);
-}
-
 /** Replace the current attribute reference
  *
  */
@@ -868,10 +944,10 @@ int tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da)
         *      Unknown attributes get copied
         */
        if (da->flags.is_unknown) {
-               ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
+               ref = tmpl_attr_ref_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
                ref->da = ref->ar_unknown = fr_dict_unknown_acopy(vpt, da);
        } else {
-               ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_NORMAL);
+               ref = tmpl_attr_ref_add(vpt, TMPL_ATTR_TYPE_NORMAL);
                ref->da = da;
        }
 
@@ -907,20 +983,20 @@ int tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da)
                }
 
                /*
-                *      Free old unknown and undefined attributes...
+                *      Free old unknown and unresolved attributes...
                 */
                talloc_free_children(ref);
        } else {
-               ref = tmpl_ar_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
+               ref = tmpl_attr_ref_add(vpt, da->flags.is_unknown ? TMPL_ATTR_TYPE_UNKNOWN : TMPL_ATTR_TYPE_NORMAL);
        }
 
 
        /*
         *      Unknown attributes get copied
         */
-       if (da->flags.is_unknown || (parent && parent->ar_da->flags.is_unknown)) {
+       if (da->flags.is_unknown) {
                ref->type = TMPL_ATTR_TYPE_UNKNOWN;
-               ref->da= ref->ar_unknown = fr_dict_unknown_acopy(vpt, da);
+               ref->da = ref->ar_unknown = fr_dict_unknown_acopy(vpt, da);
        } else {
                ref->type = TMPL_ATTR_TYPE_NORMAL;
                ref->da = da;
@@ -938,7 +1014,7 @@ void tmpl_attr_set_leaf_num(tmpl_t *vpt, int16_t num)
        tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_list(vpt) || tmpl_is_attr_unresolved(vpt));
 
        if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
-               ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
+               ref = tmpl_attr_ref_add(vpt, TMPL_ATTR_TYPE_UNKNOWN);
        } else {
                ref = fr_dlist_tail(&vpt->data.attribute.ar);
        }
@@ -979,22 +1055,6 @@ void tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to)
        TMPL_ATTR_VERIFY(vpt);
 }
 
-void tmpl_attr_set_leaf_tag(tmpl_t *vpt, int8_t tag)
-{
-       tmpl_attr_t *ref;
-
-       tmpl_assert_type(tmpl_is_attr(vpt) || tmpl_is_list(vpt) || tmpl_is_attr_unresolved(vpt));
-
-       if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
-               ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_NORMAL);
-       } else {
-               ref = fr_dlist_tail(&vpt->data.attribute.ar);
-       }
-       ref->tag = tag;
-
-       TMPL_ATTR_VERIFY(vpt);
-}
-
 void tmpl_attr_set_unresolved(tmpl_t *vpt, char const *name, size_t len)
 {
        tmpl_attr_t *ref;
@@ -1008,83 +1068,13 @@ void tmpl_attr_set_unresolved(tmpl_t *vpt, char const *name, size_t len)
                fr_dlist_talloc_reverse_free(&vpt->data.attribute.ar);
        }
 
-       ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_UNRESOLVED);
+       ref = tmpl_attr_ref_add(vpt, TMPL_ATTR_TYPE_UNRESOLVED);
        ref->ar_unresolved = talloc_strndup(vpt, name, len);
 
        TMPL_ATTR_VERIFY(vpt);
 }
 
-/** Resolve an undefined attribute using the specified rules
- *
- */
-int tmpl_attr_resolve_unresolved(tmpl_t *vpt, tmpl_rules_t const *rules)
-{
-       tmpl_attr_t             *ar = NULL;
-       fr_dict_attr_t const    *parent = NULL;
-       fr_dict_attr_t const    *da;
-
-       fr_assert_msg(tmpl_is_attr_unresolved(vpt), "Expected tmpl type 'undefined-attr', got '%s'",
-                     fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
-
-       /*
-        *      First ref is special as it can resolve in the
-        *      internal dictionary or the protocol specific
-        *      dictionary.
-        */
-       ar = fr_dlist_head(&vpt->data.attribute.ar);
-       if (fr_dict_attr_by_qualified_name(&da, rules->dict_def,
-                                          ar->unknown.name, true) != FR_DICT_ATTR_OK) {
-               parent = fr_dict_root(rules->dict_def);
-               goto unknown;
-       } else {
-               ar->da = da;
-               ar->type = TMPL_ATTR_TYPE_NORMAL;
-               parent = ar->da;
-       }
-
-       /*
-        *      Loop, resolving each unparsed attribute in turn
-        */
-       while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
-               ssize_t         slen;
-
-               slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent,
-                                                        &FR_SBUFF_IN(ar->unknown.name, strlen(ar->unknown.name)),
-                                                        false);
-               if (slen <= 0) {
-                       fr_dict_attr_t  *unknown_da;
-
-               unknown:
-                       /*
-                        *      Can't find it under it's regular name.  Try an unknown attribute.
-                        */
-                       slen = fr_dict_unknown_afrom_oid_str(vpt, &unknown_da, parent, ar->unknown.name);
-                       if ((slen <= 0) || (ar->unknown.name[slen] != '\0')) {
-                               fr_strerror_printf_push("Failed resolving undefined attribute");
-                               return -1;
-                       }
-
-                       ar->type = TMPL_ATTR_TYPE_UNKNOWN;
-                       ar->da = ar->unknown.da = unknown_da;
-                       parent = ar->da;
-                       continue;
-               }
-
-               /*
-                *      Known attribute, just rewrite.
-                */
-               ar->da = da;
-               ar->type = TMPL_ATTR_TYPE_NORMAL;
-               parent = ar->da;
-       }
-
-       vpt->type = TMPL_TYPE_ATTR;
-       TMPL_ATTR_VERIFY(vpt);
-
-       return 0;
-}
-
-/** Set the request for an attribute ref
+/** Set the request for an attribute ref
  *
  */
 void tmpl_attr_set_request(tmpl_t *vpt, request_ref_t request)
@@ -1094,7 +1084,7 @@ void tmpl_attr_set_request(tmpl_t *vpt, request_ref_t request)
 
        if (fr_dlist_num_elements(&vpt->data.attribute.rr) > 0) fr_dlist_talloc_reverse_free(&vpt->data.attribute.rr);
 
-       tmpl_rr_add(vpt, request);
+       tmpl_req_ref_add(vpt, request);
 
        TMPL_ATTR_VERIFY(vpt);
 }
@@ -1109,15 +1099,14 @@ void tmpl_attr_set_list(tmpl_t *vpt, pair_list_t list)
 /** Create a new tmpl from a list tmpl and a da
  *
  */
-int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
-                        fr_dict_attr_t const *da, int8_t tag)
+int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list, fr_dict_attr_t const *da)
 {
        tmpl_t *vpt;
 
        char attr[256];
-       size_t need, len;
+       ssize_t slen;
 
-       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, NULL, 0, T_BARE_WORD));
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
 
        /*
         *      Copies request refs and the list ref
@@ -1126,21 +1115,21 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
        tmpl_attr_set_list(vpt, tmpl_list(list));       /* Remove when lists are attributes */
        tmpl_attr_set_leaf_da(vpt, da);                 /* This should add a new da when lists are attributes */
        tmpl_attr_set_leaf_num(vpt, tmpl_num(list));
-       tmpl_attr_set_leaf_tag(vpt, tag);
 
        /*
         *      We need to rebuild the attribute name, to be the
         *      one we copied from the source list.
         */
-       len = tmpl_snprint(&need, attr, sizeof(attr), vpt);
-       if (need) {
+       slen = tmpl_print(&FR_SBUFF_OUT(attr, sizeof(attr)), vpt, TMPL_ATTR_REF_PREFIX_YES,
+                         fr_value_escape_by_quote[list->quote]);
+       if (slen < 0) {
                fr_strerror_printf("Serialized attribute too long.  Must be < "
-                                  STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", len);
+                                  STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", (size_t)-slen);
                talloc_free(vpt);
                return -1;
        }
 
-       vpt->len = len;
+       vpt->len = (size_t)slen;
        vpt->name = talloc_typed_strdup(vpt, attr);
        vpt->quote = T_BARE_WORD;
 
@@ -1151,6 +1140,11 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
        return 0;
 }
 
+/** @name Produce a #tmpl_t from a string or substring
+ *
+ * @{
+ */
+
 /** Default parser rules
  *
  * Because this is getting to be a ridiculous number of parsing rules
@@ -1158,454 +1152,927 @@ int tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
  *
  * Defaults are used if a NULL rules pointer is passed to the parsing function.
  */
-static tmpl_rules_t const default_rules = {
+static tmpl_rules_t const default_attr_ref_rules = {
        .request_def = REQUEST_CURRENT,
        .list_def = PAIR_LIST_REQUEST
 };
 
-/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+/** Default formatting rules
  *
- * @param[in,out] ctx          to allocate #tmpl_t in.
- * @param[out] err             May be NULL.  Provides the exact error that the parser hit
- *                             when processing the attribute ref.
- * @param[out] out             Where to write pointer to new #tmpl_t.
- * @param[in] name             of attribute including #request_ref_t and #pair_list_t qualifiers.
- *                             If only #request_ref_t #pair_list_t qualifiers are found,
- *                             a #TMPL_TYPE_LIST #tmpl_t will be produced.
- * @param[in] name_len         Length of name, or -1 to do strlen()
- * @param[in] rules            Rules which control parsing:
- *                             - dict_def              The default dictionary to use if attributes
- *                                                     are unqualified.
- *                             - request_def           The default #REQUEST to set if no
- *                                                     #request_ref_t qualifiers are found in name.
- *                             - list_def              The default list to set if no #pair_list_t
- *                                                     qualifiers are found in the name.
- *                             - allow_unknown         If true attributes in the format accepted by
- *                                                     #fr_dict_unknown_afrom_oid_substr will be allowed,
- *                                                     even if they're not in the main dictionaries.
- *                                                     If an unknown attribute is found a #TMPL_TYPE_ATTR
- *                                                     #tmpl_t will be produced.
- *                                                     If #tmpl_afrom_attr_substr is being called on
- *                                                     startup, the #tmpl_t may be passed to
- *                                                     #tmpl_unknown_attr_add to
- *                                                     add the unknown attribute to the main dictionary.
- *                                                     If the unknown attribute is not added to
- *                                                     the main dictionary the #tmpl_t cannot be used
- *                                                     to search for a #VALUE_PAIR in a #REQUEST.
- *                             - allow_unresolved      If true, we don't generate a parse error on
- *                                                     unknown attributes. If an unknown attribute is
- *                                                     found a #TMPL_TYPE_ATTR_UNRESOLVED
- *                                                     #tmpl_t will be produced.
- *                             - allow_foreign         If true, allow attribute names to be qualified
- *                                                     with a protocol outside of the passed dict_def.
- *                             - disallow_internal     If true, don't allow fallback to internal
- *                                                     attributes.
+ * Control token termination, escaping and how the tmpl is printed.
+ */
+fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_unquoted = {
+
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_double_unquoted = {
+       .escapes = &fr_value_unescape_double
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_single_unquoted = {
+       .escapes = &fr_value_unescape_single
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_unquoted = {
+       .escapes = &fr_value_unescape_solidus
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_unquoted = {
+       .escapes = &fr_value_unescape_backtick
+};
+
+/** Parse rules for non-quoted strings
  *
- * @see REMARKER to produce pretty error markers from the return value.
+ * These parse rules should be used for processing escape sequences in
+ * data from external data sources like SQL databases and REST APIs.
  *
- * @return
- *     - <= 0 on error (offset as negative integer)
- *     - > 0 on success (number of bytes parsed).
+ * They do not include terminals to stop parsing as it assumes the values
+ * are discreet, and not wrapped in quotes.
  */
-ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
-                              tmpl_t **out, char const *name, ssize_t name_len, tmpl_rules_t const *rules)
-{
-       char const              *p, *q;
-       long                    num;
-       ssize_t                 slen;
-       tmpl_t          *vpt;
-       request_ref_t           request_ref;
-       pair_list_t             list;
-       fr_dict_attr_t const    *da;
-       bool                    is_raw = false;
+fr_sbuff_parse_rules_t const *tmpl_parse_rules_unquoted[T_TOKEN_LAST] = {
+       [T_BARE_WORD]                   = &tmpl_parse_rules_bareword_unquoted,
+       [T_DOUBLE_QUOTED_STRING]        = &tmpl_parse_rules_double_unquoted,
+       [T_SINGLE_QUOTED_STRING]        = &tmpl_parse_rules_single_unquoted,
+       [T_SOLIDUS_QUOTED_STRING]       = &tmpl_parse_rules_solidus_unquoted,
+       [T_BACK_QUOTED_STRING]          = &tmpl_parse_rules_backtick_unquoted
+};
 
-       if (!rules) rules = &default_rules;     /* Use the defaults */
+fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_quoted = {
+       .escapes = &(fr_sbuff_unescape_rules_t){
+               .chr = '\\',
+               /*
+                *      Allow barewords to contain whitespace
+                *      if they're escaped.
+                */
+               .subs = {
+                       ['\t'] = '\t',
+                       ['\n'] = '\n',
+                       [' '] = ' '
+               },
+               .do_hex = false,
+               .do_oct = false
+       },
+       .terminals = &FR_SBUFF_TERMS(
+               L("\t"),
+               L("\n"),
+               L(" ")
+       )
+};
 
-       if (err) *err = ATTR_REF_ERROR_NONE;
+fr_sbuff_parse_rules_t const tmpl_parse_rules_double_quoted = {
+       .escapes = &fr_value_unescape_double,
+       .terminals = &FR_SBUFF_TERM("\"")
+};
 
-       if (name_len < 0) name_len = strlen(name);
+fr_sbuff_parse_rules_t const tmpl_parse_rules_single_quoted = {
+       .escapes = &fr_value_unescape_single,
+       .terminals = &FR_SBUFF_TERM("'")
+};
 
-       p = name;
+fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_quoted = {
+       .escapes = &fr_value_unescape_solidus,
+       .terminals = &FR_SBUFF_TERM("/")
+};
 
-       if (!*p) {
-               fr_strerror_printf("Empty attribute reference");
-               if (err) *err = ATTR_REF_ERROR_EMPTY;
-               return 0;
-       }
+fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_quoted = {
+       .escapes = &fr_value_unescape_backtick,
+       .terminals = &FR_SBUFF_TERM("`")
+};
+
+/** Parse rules for quoted strings
+ *
+ * These parse rules should be used for internal parsing functions that
+ * are working with configuration files.
+ *
+ * They include appropriate quote terminals to force functions parsing
+ * quoted strings to return when they reach a quote character.
+ */
+fr_sbuff_parse_rules_t const *tmpl_parse_rules_quoted[T_TOKEN_LAST] = {
+       [T_BARE_WORD]                   = &tmpl_parse_rules_bareword_quoted,
+       [T_DOUBLE_QUOTED_STRING]        = &tmpl_parse_rules_double_quoted,
+       [T_SINGLE_QUOTED_STRING]        = &tmpl_parse_rules_single_quoted,
+       [T_SOLIDUS_QUOTED_STRING]       = &tmpl_parse_rules_solidus_quoted,
+       [T_BACK_QUOTED_STRING]          = &tmpl_parse_rules_backtick_quoted
+};
+
+/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
+ *
+ * @param[in] in       the sbuff to check.
+ * @param[in] p_rules  to use terminals from.
+ * @return
+ *     - true if substr is terminated correctly.
+ *     - false if subst is not terminated correctly.
+ */
+static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
+                                                                    fr_sbuff_parse_rules_t const *p_rules)
+{
+       fr_sbuff_marker_t       m;
+       bool                    ret;
+
+       if (!fr_sbuff_extend(in)) return true;          /* we're at the end of the string */
+       if (!p_rules || !p_rules->terminals) return false;      /* more stuff to parse but don't have a terminal set */
+
+       fr_sbuff_marker(&m, in);
+       ret = fr_sbuff_is_terminal(in, p_rules->terminals);
+       fr_sbuff_set(in, &m);
+       fr_sbuff_marker_release(&m);
+       return ret;
+}
 
+/** Parse array subscript and in future other filters
+ *
+ * @param[out] err     Parse error code.
+ * @param[in] ar       to populate filter for.
+ * @param[in] name     containing more attribute ref data.
+ * @return
+ *     - 1 on success with filter parsed.
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int tmpl_attr_ref_parse_filter(attr_ref_error_t *err, tmpl_attr_t *ar, fr_sbuff_t *name)
+{
        /*
-        *      Check to see if we expect a reference prefix
+        *      Parse array subscript (and eventually complex filters)
         */
-       switch (rules->prefix) {
-       case TMPL_ATTR_REF_PREFIX_YES:
-               if (*p != '&') {
-                       fr_strerror_printf("Invalid attribute reference, missing '&' prefix");
-                       if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
-                       return 0;
-               }
-               p++;
+       if (!fr_sbuff_next_if_char(name, '[')) return 0;
+
+       switch (*fr_sbuff_current(name)) {
+       case '#':
+               ar->num = NUM_COUNT;
+               fr_sbuff_next(name);
                break;
 
-       case TMPL_ATTR_REF_PREFIX_NO:
-               if (*p == '&') {
-                       fr_strerror_printf("Attribute references used here must not have a '&' prefix");
-                       if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
-                       return 0;
-               }
+       case '*':
+               ar->num = NUM_ALL;
+               fr_sbuff_next(name);
                break;
 
-       case TMPL_ATTR_REF_PREFIX_AUTO:
-               if (*p == '&') p++;
+       case 'n':
+               ar->num = NUM_LAST;
+               fr_sbuff_next(name);
                break;
-       }
 
-       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, NULL, 0, T_BARE_WORD));
+       default:
+       {
+               fr_sbuff_parse_error_t  sberr = FR_SBUFF_PARSE_OK;
+               fr_sbuff_t tmp = FR_SBUFF_NO_ADVANCE(name);
+
+               if (fr_sbuff_out(&sberr, &ar->num, &tmp) == 0) {
+                       if (sberr == FR_SBUFF_PARSE_ERROR_NOT_FOUND) {
+                               fr_strerror_printf("Array index is not an integer");
+                               if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+                       error:
+                               return -1;
+                       }
+
+                       fr_strerror_printf("Invalid array index");
+                       if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+                       goto error;
+               }
+
+               if ((ar->num > 1000) || (ar->num < 0)) {
+                       fr_strerror_printf("Invalid array index '%hi' (should be between 0-1000)", ar->num);
+                       ar->num = 0;
+                       if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+                       goto error;
+               }
+               fr_sbuff_set(name, &tmp);       /* Advance name _AFTER_ doing checks */
+       }
+               break;
+       }
 
        /*
-        *      Search for a recognised list
-        *
-        *      There may be multiple '.' separated
-        *      components, so don't error out if
-        *      the first one doesn't match a known list.
-        *
-        *      The next check for a list qualifier
+        *      Always advance here, so the error
+        *      marker points to the bad char.
         */
-       q = p;
-       p += radius_request_name(&request_ref, p, rules->request_def);
+       if (!fr_sbuff_next_if_char(name, ']')) {
+               fr_strerror_printf("No closing ']' for array index");
+               if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+               goto error;
+       }
+
+       return 1;
+}
+
+/** Parse an attribute reference, either an OID or attribute name
+ *
+ * This function calls itself recursively to process additional OID
+ * components once we've failed to resolve one component.
+ *
+ * @note Do not call directly.
+ *
+ * @param[in] ctx              to allocate new attribute reference in.
+ * @param[out] err             Parse error.
+ * @param[in,out] vpt          to append this reference to.
+ * @param[in] name             to parse.
+ * @param[in] rules            see tmpl_attr_ref_afrom_attr_substr.
+ * @param[in] depth            How deep we are.  Used to check for maximum nesting level.
+ * @return
+ *     - <0 on error.
+ *     - 0 on success.
+ */
+static inline int tmpl_attr_ref_afrom_attr_unresolved_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+                                                            tmpl_t *vpt,
+                                                            fr_sbuff_t *name, tmpl_rules_t const *rules,
+                                                            unsigned int depth)
+{
+       tmpl_attr_t             *ar = NULL;
+       fr_dlist_head_t         *list = &vpt->data.attribute.ar;
+       int                     ret;
+       char                    *unresolved;
+       size_t                  len;
 
-       if (rules->disallow_qualifiers && (p != q)) {
-               fr_strerror_printf("It is not permitted to specify a request list here.");
-               goto invalid_list;
+       if (depth > FR_DICT_MAX_TLV_STACK) {
+               fr_strerror_printf("Attribute nesting too deep");
+               if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+               return -1;
+       }
+
+       /*
+        *      Input too short
+        */
+       if (!fr_sbuff_extend(name)) {
+               fr_strerror_printf("Missing attribute reference");
+               if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+               return -1;
        }
-       tmpl_attr_set_request(vpt, request_ref);
 
-       if (tmpl_request(vpt) == REQUEST_UNKNOWN) tmpl_attr_set_request(vpt, rules->request_def);
+       /*
+        *      Mark the tmpl up as an unresolved attribute reference
+        *      the attribute reference will be resolved later.
+        */
+       vpt->type = TMPL_TYPE_ATTR_UNRESOLVED;
 
+       MEM(ar = talloc(ctx, tmpl_attr_t));
        /*
-        *      Finding a list qualifier is optional
+        *      Copy out a string of allowed dictionary chars to form
+        *      the unresolved attribute name.
         *
-        *      Because the list name parser can
-        *      determine if something was a tag
-        *      or a list, we trust that when it
-        *      returns PAIR_LIST_UNKOWN that
-        *      the input string is invalid.
-        */
-       q = p;
-       p += radius_list_name(&list, p, rules->list_def);
-
-       if (rules->disallow_qualifiers && (p != q)) {
-               fr_strerror_printf("It is not permitted to specify a pair list here.");
-       invalid_list:
-               if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
-               slen = -(q - name);
+        *      This will be resolved later (outside of this function).
+        */
+       len = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
+                                            name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
+                                            fr_dict_attr_allowed_chars);
+       if (len == 0) {
+               fr_strerror_printf("Invalid attribute name");
+               if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+       error:
+               talloc_free(ar);
+               return -1;
+       }
+       if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
+               fr_strerror_printf("Attribute name is too long");
+               if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
                goto error;
        }
 
-       if (list == PAIR_LIST_UNKNOWN) {
-               fr_strerror_printf("Invalid list qualifier");
-               if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
-               slen = -(p - name);
-       error:
-               talloc_free(vpt);
-               return slen;
-       }
+       *ar = (tmpl_attr_t){
+               .ar_num = NUM_ANY,
+               .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
+               .ar_unresolved = unresolved
+       };
 
-       tmpl_attr_set_list(vpt, list);
-       tmpl_attr_set_leaf_tag(vpt, TAG_NONE);
-       tmpl_attr_set_leaf_num(vpt, NUM_ANY);
+       if (tmpl_attr_ref_parse_filter(err, ar, name) < 0) goto error;
+
+       fr_dlist_insert_tail(list, ar);
 
        /*
-        *      No more input after parsing the list ref, we're done.
+        *      Once one OID component is created as unresolved all
+        *      future OID components are also unresolved.
         */
-       if (p == (name + name_len)) {
-               vpt->type = TMPL_TYPE_LIST;
-               goto finish;
+       if (fr_sbuff_next_if_char(name, '.')) {
+               ret = tmpl_attr_ref_afrom_attr_unresolved_substr(ctx, err, vpt, name, rules, depth + 1);
+               if (ret < 0) {
+                       fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+                       return -1;
+               }
        }
 
-       /*
-        *      This may be just a bare list, but it can still
-        *      have instance selectors and tag selectors.
-        */
-       switch (*p) {
-       case '\0':
-               vpt->type = TMPL_TYPE_LIST;
-               goto finish;
+       return 0;
+}
 
-       case '[':
-               vpt->type = TMPL_TYPE_LIST;
-               goto do_num;
+/** Parse an attribute reference, either an OID or attribute name
+ *
+ * @note Do not call directly.
+ *
+ * @param[in] ctx              to allocate new attribute reference in.
+ * @param[out] err             Parse error.
+ * @param[in,out] vpt          to append this reference to.
+ * @param[in] parent           Result of parsing the previous attribute reference.
+ * @param[in] name             to parse.
+ * @param[in] rules            which places constraints on attribute reference parsing.
+ *                             Rules interpreted by this function is:
+ *                             - allow_unknown - If false unknown OID components
+ *                               result in a parse error.
+ *                             - allow_unresolved - If false unknown attribute names
+ *                               result in a parse error.
+ *                             - disallow_internal - If an attribute resolves in the
+ *                               internal dictionary then that results in a parse
+ *                               error.
+ *                             - allow_foreign - If an attribute resolves in a dictionary
+ *                               that does not match the parent
+ *                               (exception being FR_TYPE_GROUP) then that results
+ *                               in a parse error.
+ * @param[in] depth            How deep we are.  Used to check for maximum nesting level.
+ * @return
+ *     - <0 on error.
+ *     - 0 on success.
+ */
+static inline int tmpl_attr_ref_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+                                                 tmpl_t *vpt,
+                                                 fr_dict_attr_t const *parent,
+                                                 fr_sbuff_t *name, tmpl_rules_t const *rules, unsigned int depth)
+{
+       uint32_t                oid = 0;
+       ssize_t                 slen;
+       fr_dlist_head_t         *list = &vpt->data.attribute.ar;
+       tmpl_attr_t             *ar = NULL;
+       fr_dict_attr_t const    *da;
+       fr_sbuff_marker_t       m_s;
 
-       default:
-               break;
+       fr_sbuff_marker(&m_s, name);
+
+       if (depth > FR_DICT_MAX_TLV_STACK) {
+               fr_strerror_printf("Attribute nesting too deep");
+               if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+       error:
+               fr_sbuff_marker_release(&m_s);
+               return -1;
        }
 
        /*
-        *      Hack to markup attributes as raw
+        *      Input too short
         */
-       if (strncmp(p, "raw.", 4) == 0) {
-               is_raw = true;
-               p += 4;
+       if (!fr_sbuff_extend(name)) {
+               fr_strerror_printf("Missing attribute reference");
+               if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+               goto error;
        }
 
        /*
-        *      Look up by name, *including* any Attr-1.2.3.4 which was created when
-        *      parsing the configuration files.
+        *      No parent means we need to go hunting through all the dctionaries
+        */
+       if (!parent) {
+               slen = fr_dict_attr_by_qualified_name_substr(NULL, &da,
+                                                            rules->dict_def, name, !rules->disallow_internal);
+       /*
+        *      Otherwise we're resolving in the context of the last component,
+        *      or its reference in the case of group attributes.
         */
-       slen = fr_dict_attr_by_qualified_name_substr(NULL, &da,
-                                                    rules->dict_def, &FR_SBUFF_IN(p, strlen(p)),
-                                                    !rules->disallow_internal);
-       if (slen <= 0) {
-               fr_dict_attr_t *unknown_da;
+       } else {
+               slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent, name, false);
+       }
 
-               fr_strerror();  /* Clear out any existing errors */
+       /*
+        *      The named component was a known attribute
+        *      so record it as a normal attribute
+        *      reference.
+        */
+       if (slen > 0) {
+               MEM(ar = talloc(ctx, tmpl_attr_t));
+               *ar = (tmpl_attr_t){
+                       .ar_num = NUM_ANY,
+                       .ar_type = TMPL_ATTR_TYPE_NORMAL,
+                       .ar_da = da
+               };
+
+               goto check_attr;
+       }
 
-               /*
-                *      At this point, the OID *must* be unknown, and
-                *      not previously used.
+       /*
+        *      See if the ref begins with an unsigned integer
+        *      if it does it's probably an OID component
+        *
+        *      .<oid>
+        */
+       slen = fr_sbuff_out(NULL, &oid, name);
+       if (slen > 0) {
+               fr_dict_attr_t *da_unknown;
+
+               fr_strerror();  /* Clear out any existing errors */
+
+               /*
+                *      Locating OID attributes is different than
+                *      locating named attributes because we have
+                *      significantly more numberspace overlap
+                *      between the protocols.
                 */
-               slen = fr_dict_unknown_afrom_oid_substr(vpt, &unknown_da,
-                                                       fr_dict_root(rules->dict_def), p);
+               if (!parent && rules->dict_def) parent = fr_dict_root(rules->dict_def);
+               if (!parent && !rules->disallow_internal) parent = fr_dict_root(fr_dict_internal());
+               if (!parent) {
+                       fr_strerror_printf("OID references must be qualified with a protocol when used here");
+                       if (err) *err = ATTR_REF_ERROR_UNQUALIFIED_ATTRIBUTE_NOT_ALLOWED;
+                       fr_sbuff_set(name, &m_s);
+                       goto error;
+               }
                /*
-                *      Attr-1.2.3.4 is OK.
+                *      If it's numeric and not a known attribute
+                *      then we create an unknown attribute with
+                *      the specified attribute number.
                 */
-               if (slen > 0) {
+               da = fr_dict_attr_child_by_num(parent, oid);
+               if (da) {
+                       /*
+                        *      The OID component was a known attribute
+                        *      so record it as a normal attribute
+                        *      reference.
+                        */
+                       MEM(ar = talloc(ctx, tmpl_attr_t));
+                       *ar = (tmpl_attr_t){
+                               .ar_num = NUM_ANY,
+                               .ar_type = TMPL_ATTR_TYPE_NORMAL,
+                               .ar_da = da,
+                       };
                        vpt->data.attribute.was_oid = true;
 
-                       if (!is_raw) {
-                               da = fr_dict_attr_known(fr_dict_by_da(unknown_da), unknown_da);
-                               if (da) {
-                                       talloc_free(unknown_da);
-                                       tmpl_attr_set_leaf_da(vpt, da);
+                       goto check_attr;
+               }
 
-                                       p += slen;
+               if (!rules->allow_unknown) {
+                       fr_strerror_printf("Unknown attributes not allowed here");
+                       if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;
+                       fr_sbuff_set(name, &m_s);
+                       goto error;
+               }
 
-                                       goto do_num;
-                               }
+               MEM(ar = talloc(ctx, tmpl_attr_t));
+
+               switch (parent->type) {
+               case FR_TYPE_VSA:
+                       if (fr_dict_unknown_vendor_afrom_num(ar, &da_unknown, parent, oid) < 0) {
+                               if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;   /* strerror set by dict function */
+                               goto error;
                        }
+                       break;
 
-                       if (!rules->allow_unknown) {
-                               fr_strerror_printf("Unknown attribute");
-                               if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;
-                               slen = -(p - name);
+               default:
+                       if (fr_dict_unknown_attr_afrom_num(ar, &da_unknown, parent, oid) < 0) {
+                               if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;   /* strerror set by dict function */
                                goto error;
                        }
+                       break;
+               }
+               da_unknown->flags.internal = 1;
+
+               *ar = (tmpl_attr_t){
+                       .ar_num = NUM_ANY,
+                       .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
+                       .ar_unknown = da_unknown,
+                       .ar_da = da_unknown,
+               };
+               da = da_unknown;
+               vpt->data.attribute.was_oid = true;
+               goto do_suffix;
+       }
 
-                       /*
-                        *      Unknown attributes can be encoded, BUT
-                        *      they must be of type "octets".
-                        */
-                       fr_assert(unknown_da->type == FR_TYPE_OCTETS);
-                       tmpl_attr_set_leaf_da(vpt, unknown_da);
-                       talloc_free(unknown_da);
+       /*
+        *      Can't parse it as an attribute, might be a literal string
+        *      let the caller decide.
+        *
+        *      Don't alter the fr_strerror buffer, may contain useful
+        *      errors from the dictionary code.
+        */
+       if (!rules->allow_unresolved) {
+               fr_strerror_printf_push("Unparsed attributes not allowed here");
+               if (err) *err = ATTR_REF_ERROR_UNRESOLVED_ATTRIBUTE_NOT_ALLOWED;
+               fr_sbuff_set(name, &m_s);
+               goto error;
+       }
 
-                       p += slen;
+       fr_sbuff_marker_release(&m_s);
 
-                       goto do_num; /* unknown attributes can't have tags */
-               }
+       /*
+        *      Once we hit one unresolved attribute we have to treat
+        *      the rest of the components are unresolved as well.
+        */
+       return tmpl_attr_ref_afrom_attr_unresolved_substr(ctx, err, vpt, name, rules, depth);
+
+check_attr:
+       /*
+        *      Attribute location (dictionary) checks
+        */
+       if (!rules->allow_foreign || rules->disallow_internal) {
+               fr_dict_t const *found_in = fr_dict_by_da(da);
 
                /*
-                *      Can't parse it as an attribute, might be a literal string
-                *      let the caller decide.
-                *
-                *      Don't alter the fr_strerror buffer, should contain the parse
-                *      error from fr_dict_unknown_from_suboid.
+                *      Parent is the dict root if this is the first ref in the
+                *      chain.
                 */
-               if (!rules->allow_unresolved) {
-                       fr_strerror_printf_push("Undefined attributes not allowed here");
-                       if (err) *err = ATTR_REF_ERROR_UNDEFINED_ATTRIBUTE_NOT_ALLOWED;
-                       slen = -(p - name);
-                       goto error;
-               }
+               if (!parent) parent = fr_dict_root(rules->dict_def);
 
                /*
-                *      Copy the name to a field for later resolution
+                *      Even if allow_foreign is false, if disallow_internal is not
+                *      true, we still allow the resolution.
                 */
-               vpt->type = TMPL_TYPE_ATTR_UNRESOLVED;
-               for (q = p; (q < (name + name_len)) && ((*q == '.') || fr_dict_attr_allowed_chars[(uint8_t) *q]); q++);
-               if (q == p) {
-                       fr_strerror_printf("Invalid attribute name");
-                       if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
-                       slen = -(p - name);
+               if (rules->disallow_internal && (found_in == fr_dict_internal())) {
+                       fr_strerror_printf("Internal attributes not allowed here");
+                       if (err) *err = ATTR_REF_ERROR_INTERNAL_ATTRIBUTE_NOT_ALLOWED;
+                       fr_sbuff_set(name, &m_s);
                        goto error;
                }
-
-               if ((q - p) > FR_DICT_ATTR_MAX_NAME_LEN) {
-                       fr_strerror_printf("Attribute name is too long");
-                       if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
-                       slen = -(p - name);
+               /*
+                *      Check that the attribute we resolved was from an allowed
+                *      dictionary.
+                *
+                *      We already checked if internal attributes were disallowed
+                *      above, so we skip this check if the attribute is internal.
+                *
+                *      The reason this checks works with foreign attributes is
+                *      because when an attr ref resolves to a group parent is not
+                *      set to that attribute, but the foreign dictionary attribute
+                *      that it references.
+                *
+                *      My-Dhcp-In-RADIUS-Attribute.My-DHCP-Attribute
+                *      |                         ||_ DHCP attribute
+                *      |                         |_ Lookup inside linking attribute triggers dictionary change
+                *      |_ RADIUS attribute
+                */
+               if (found_in != fr_dict_internal() &&
+                   !rules->allow_foreign && (found_in != fr_dict_by_da(parent))) {
+                       fr_strerror_printf("Only attributes from the %s protocol are allowed here",
+                                          fr_dict_root(rules->dict_def)->name);
+                       if (err) *err = ATTR_REF_ERROR_FOREIGN_ATTRIBUTES_NOT_ALLOWED;
+                       fr_sbuff_set(name, &m_s);
                        goto error;
                }
-               tmpl_attr_set_unresolved(vpt, p, q - p);
-               p = q;
-
-               goto do_num;
-       } else {
-               tmpl_attr_set_leaf_da(vpt, da);
        }
 
+do_suffix:
        /*
-        *      Attribute location checks
+        *      Parse the attribute reference filter
         */
-       {
-               fr_dict_t const *found_in = fr_dict_by_da(tmpl_da(vpt));
-
+       switch (tmpl_attr_ref_parse_filter(err, ar, name)) {
+       case 0:                                         /* No filter */
                /*
-                *      Even if allow_foreign is false, if disallow_internal is not
-                *      true, we still allow foreign
+                *      Omit nesting types where the relationship is already
+                *      described by the dictionaries and there's no filter.
+                *
+                *      These attribute references would just use additional
+                *      memory for no real purpose.
+                *
+                *      Because we pre-allocate an attribute reference in
+                *      each tmpl talloc pool, unless the attribute
+                *      reference list contains a group, there's no performance
+                *      penalty in repeatedly allocating and freeing this ar.
                 */
-               if (found_in == fr_dict_internal()) {
-                       if (rules->disallow_internal) {
-                               fr_strerror_printf("Internal attributes not allowed here");
-                               if (err) *err = ATTR_REF_ERROR_INTERNAL_ATTRIBUTE_NOT_ALLOWED;
-                               slen = -(p - name);
-                               goto error;
-                       }
+               switch (da->type) {
+               case FR_TYPE_STRUCT:
+               case FR_TYPE_TLV:
+               case FR_TYPE_VENDOR:
+               case FR_TYPE_VSA:
+                       TALLOC_FREE(ar);
+                       break;
+
                /*
-                *      Check that the attribute we resolved was from an allowed dictionary
+                *      Groups are fine, as are leaf values.
                 */
+               case FR_TYPE_GROUP:
+               case FR_TYPE_VALUE:
+                       break;
+
+               default:
+                       goto error;
                }
-#if 0
-               else if ((rules->dict_def && (found_in != rules->dict_def))) {
+               FALL_THROUGH;
+
+       case 1:                                         /* Found a filter */
+               fr_dlist_insert_tail(list, ar);
+               break;
+
+       default:                                        /* Parse error */
+               goto error;
+       }
+
+       /*
+        *      At the end of the attribute reference. If there's a
+        *      trailing '.' then there's another attribute reference
+        *      we need to parse, otherwise we're done.
+        */
+       if (fr_sbuff_next_if_char(name, '.')) {
+               switch (da->type) {
                /*
-                *      @fixme - We can't enforce this until we support nested attributes
-                *      where the change of attribute context gives us a new dictionary.
-                *
-                *      i.e.
-                *
-                *      My-Dhcp-In-RADIUS-Attribute.My-DHCP-Attribute
-                *      |                          ||_ DHCP attribute
-                *      |                          |_ Lookup inside linking attribute triggers dictionary change
-                *      |_ RADIUS attribute
+                *      If this is a group then the parent is the
+                *      group ref.
                 */
+               case FR_TYPE_GROUP:
+                       parent = da->ref;
+                       break;
 
-                       if (!rules->allow_foreign) {
-                               fr_strerror_printf("Only attributes from the %s protocol are allowed here",
-                                                  fr_dict_root(rules->dict_def)->name);
-                               if (err) *err = ATTR_REF_ERROR_FOREIGN_ATTRIBUTES_NOT_ALLOWED;
-                               slen = -(p - name);
-                               goto error;
-                       }
+               case FR_TYPE_STRUCT:
+               case FR_TYPE_TLV:
+               case FR_TYPE_VENDOR:
+               case FR_TYPE_VSA:
+                       parent = da;
+                       break;
+
+               default:
+                       fr_strerror_printf("Parent type of nested attribute must be of "
+                                          "\"struct\", \"tlv\", \"vendor\", \"vsa\" or \"group\", got \"%s\"",
+                                          fr_table_str_by_value(fr_value_box_type_table,
+                                                                da->type, "<INVALID>"));
+                       fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+                       fr_sbuff_set(name, &m_s);
+                       goto error;
+               }
+
+               if (tmpl_attr_ref_afrom_attr_substr(ctx, err, vpt, parent, name, rules, depth + 1) < 0) {
+                       fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+                       goto error;
                }
-#endif
        }
 
+       fr_sbuff_marker_release(&m_s);
+
+       return 0;
+}
+
+static inline int tmpl_request_ref_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+                                                    tmpl_t *vpt,
+                                                    fr_sbuff_t *name,
+                                                    fr_sbuff_parse_rules_t const *p_rules,
+                                                    tmpl_rules_t const *ar_rules,
+                                                    unsigned int depth)
+{
+       request_ref_t           ref;
+       size_t                  ref_len;
+       tmpl_request_t          *rr;
+       fr_dlist_head_t         *list = &vpt->data.attribute.rr;
+       fr_sbuff_marker_t       s_m;
+
+       fr_sbuff_marker(&s_m, name);
+       fr_sbuff_out_by_longest_prefix(&ref_len, &ref, request_ref_table, name, ar_rules->request_def);
+
        /*
-        *      Parsing was successful, so advance the pointer
+        *      No match
         */
-       p += slen;
+       if (ref_len == 0) {
+               /*
+                *      If depth == 0, then just use
+                *      the default request reference.
+                */
+       default_ref:
+               if (depth == 0) {
+                       MEM(rr = talloc(ctx, tmpl_request_t));
+                       *rr = (tmpl_request_t){
+                               .request = ref
+                       };
+                       fr_dlist_insert_tail(list, rr);
+               }
+
+               return 0;
+       }
 
        /*
-        *      If it's an attribute, look for a tag.
-        *
-        *      Note that we check for tags even if the attribute
-        *      isn't tagged.  This lets us print more useful error
-        *      messages.
+        *      We don't want to misidentify the list
+        *      as being part of an attribute.
         */
-       if (*p == ':') {
-               char *end;
+       if (!fr_sbuff_is_char(name, '.') &&
+           ((!p_rules || !p_rules->terminals) ||
+           !tmpl_substr_terminal_check(name, p_rules))) {
+               fr_sbuff_set(name, &s_m);
+               goto default_ref;
+       }
 
-               if (!tmpl_da(vpt)->flags.has_tag) { /* Lists don't have a da */
-                       fr_strerror_printf("Attribute '%s' cannot have a tag", tmpl_da(vpt)->name);
-                       if (err) *err = ATTR_REF_ERROR_TAGGED_ATTRIBUTE_NOT_ALLOWED;
-                       slen = -(p - name);
-                       goto error;
-               }
+       fr_sbuff_marker_release(&s_m);
 
-               /*
-                *      Allow '*' as an explicit wildcard.
-                */
-               if (p[1] == '*') {
-                       tmpl_attr_set_leaf_tag(vpt, TAG_ANY);
-                       p += 2;
+       /*
+        *      Nesting level too deep
+        */
+       if (depth > TMPL_MAX_REQUEST_REF_NESTING) {
+               fr_strerror_printf("Request ref nesting too deep");
+               if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+               return -1;
+       }
 
-               } else {
-                       num = strtol(p + 1, &end, 10);
-                       if (!TAG_VALID_ZERO(num)) {
-                               fr_strerror_printf("Invalid tag value '%li' (should be between 0-31)", num);
-                               if (err) *err = ATTR_REF_ERROR_INVALID_TAG;
-                               slen = -((p + 1) - name);
-                               goto error;
-                       }
+       if (ar_rules->disallow_qualifiers) {
+               fr_strerror_printf("It is not permitted to specify a request reference here");
+               if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
+               return -1;
+       }
 
-                       tmpl_attr_set_leaf_tag(vpt, num);
-                       p = end;
-               }
+       /*
+        *      Add a new entry to the dlist
+        */
+       MEM(rr = talloc(ctx, tmpl_request_t));
+       *rr = (tmpl_request_t){
+               .request = ref
+       };
+       fr_dlist_insert_tail(list, rr);
 
        /*
-        *      The attribute is tagged, but the admin didn't
-        *      specify one.  This means it's likely a
-        *      "search" thingy.. i.e. "find me ANY attribute,
-        *      no matter what the tag".
+        *      Advance past the separator (if there is one)
         */
-       } else if (tmpl_da(vpt)->flags.has_tag) {
-               tmpl_attr_set_leaf_tag(vpt, TAG_ANY);
+       if (fr_sbuff_next_if_char(name, '.')) {
+               if (tmpl_request_ref_afrom_attr_substr(ctx, err, vpt, name, p_rules, ar_rules, depth + 1) < 0) {
+                       fr_dlist_talloc_free_tail(list); /* Remove and free rr */
+                       return -1;
+               }
        }
 
-do_num:
-       if (*p == '\0') goto finish;
+       return 0;
+}
 
-       if (*p == '[') {
-               p++;
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+ *
+ * @param[in,out] ctx          to allocate #tmpl_t in.
+ * @param[out] err             May be NULL.  Provides the exact error that the parser hit
+ *                             when processing the attribute ref.
+ * @param[out] out             Where to write pointer to new #tmpl_t.
+ * @param[in] name             of attribute including #request_ref_t and #pair_list_t qualifiers.
+ *                             If only #request_ref_t #pair_list_t qualifiers are found,
+ *                             a #TMPL_TYPE_LIST #tmpl_t will be produced.
+ * @param[in] p_rules          Formatting rules used to check for trailing garbage.
+ * @param[in] ar_rules         Rules which control parsing:
+ *                             - dict_def              The default dictionary to use if attributes
+ *                                                     are unqualified.
+ *                             - request_def           The default #REQUEST to set if no
+ *                                                     #request_ref_t qualifiers are found in name.
+ *                             - list_def              The default list to set if no #pair_list_t
+ *                                                     qualifiers are found in the name.
+ *                             - allow_unknown         If true attributes in the format accepted by
+ *                                                     #fr_dict_unknown_afrom_oid_substr will be allowed,
+ *                                                     even if they're not in the main dictionaries.
+ *                                                     If an unknown attribute is found a #TMPL_TYPE_ATTR
+ *                                                     #tmpl_t will be produced.
+ *                                                     If #tmpl_afrom_attr_substr is being called on
+ *                                                     startup, the #tmpl_t may be passed to
+ *                                                     #tmpl_attr_unknown_add to
+ *                                                     add the unknown attribute to the main dictionary.
+ *                                                     If the unknown attribute is not added to
+ *                                                     the main dictionary the #tmpl_t cannot be used
+ *                                                     to search for a #VALUE_PAIR in a #REQUEST.
+ *                             - allow_unresolved      If true, we don't generate a parse error on
+ *                                                     unknown attributes. If an unknown attribute is
+ *                                                     found a #TMPL_TYPE_ATTR_UNRESOLVED
+ *                                                     #tmpl_t will be produced.
+ *                             - allow_foreign         If true, allow attribute names to be qualified
+ *                                                     with a protocol outside of the passed dict_def.
+ *                             - disallow_internal     If true, don't allow fallback to internal
+ *                                                     attributes.
+ *
+ * @see REMARKER to produce pretty error markers from the return value.
+ *
+ * @return
+ *     - <= 0 on error (offset as negative integer)
+ *     - > 0 on success (number of bytes parsed).
+ */
+ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+                              tmpl_t **out, fr_sbuff_t *name,
+                              fr_sbuff_parse_rules_t const *p_rules,
+                              tmpl_rules_t const *ar_rules)
+{
+       int             ret;
+       size_t          list_len;
+       tmpl_t          *vpt;
+       fr_sbuff_t      our_name = FR_SBUFF_NO_ADVANCE(name);   /* Take a local copy in case we need to back track */
+       bool            ref_prefix = false;
+       bool            is_raw = false;
 
-               switch (*p) {
-               case '#':
-                       tmpl_attr_set_leaf_num(vpt, NUM_COUNT);
-                       p++;
-                       break;
+       if (!ar_rules) ar_rules = &default_attr_ref_rules;      /* Use the defaults */
 
-               case '*':
-                       tmpl_attr_set_leaf_num(vpt, NUM_ALL);
-                       p++;
-                       break;
+       if (err) *err = ATTR_REF_ERROR_NONE;
 
-               case 'n':
-                       tmpl_attr_set_leaf_num(vpt, NUM_LAST);
-                       p++;
-                       break;
+       if (!fr_sbuff_extend(&our_name)) {
+               fr_strerror_printf("Empty attribute reference");
+               if (err) *err = ATTR_REF_ERROR_EMPTY;
+               FR_SBUFF_ERROR_RETURN(&our_name);
+       }
 
-               default:
-               {
-                       char *end;
+       /*
+        *      Check to see if we expect a reference prefix
+        */
+       switch (ar_rules->prefix) {
+       case TMPL_ATTR_REF_PREFIX_YES:
+               if (!fr_sbuff_next_if_char(&our_name, '&')) {
+                       fr_strerror_printf("Invalid attribute reference, missing '&' prefix");
+                       if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
+                       FR_SBUFF_ERROR_RETURN(&our_name);
+               }
 
-                       num = strtol(p, &end, 10);
-                       if (p == end) {
-                               fr_strerror_printf("Array index is not an integer");
-                               if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
-                               slen = -(p - name);
-                               goto error;
-                       }
+               break;
 
-                       if ((num > 1000) || (num < 0)) {
-                               fr_strerror_printf("Invalid array reference '%li' (should be between 0-1000)", num);
-                               if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
-                               slen = -(p - name);
-                               goto error;
-                       }
-                       tmpl_attr_set_leaf_num(vpt, num);
-                       p = end;
+       case TMPL_ATTR_REF_PREFIX_NO:
+               if (fr_sbuff_is_char(&our_name, '&')) {
+                       fr_strerror_printf("Attribute references used here must not have a '&' prefix");
+                       if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
+                       FR_SBUFF_ERROR_RETURN(&our_name);
                }
+               break;
+
+       case TMPL_ATTR_REF_PREFIX_AUTO:
+               fr_sbuff_next_if_char(&our_name, '&');
+               break;
+       }
+
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
+       vpt->data.attribute.ref_prefix = ref_prefix;
+
+       /*
+        *      For backwards compatibility strip off "Attr-"
+        */
+       (void)fr_sbuff_adv_past_strcase_literal(&our_name, "Attr-");
+
+       /*
+        *      The "raw." prefix marks up the leaf attribute
+        *      as unknown if it wasn't already which allows
+        *      users to stick whatever they want in there as
+        *      a value.
+        */
+       if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
+
+       /*
+        *      Parse one or more request references
+        */
+       ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, ar_rules, 0);
+       if (ret < 0) {
+       error:
+               talloc_free(vpt);
+               FR_SBUFF_ERROR_RETURN(&our_name);
+       }
+
+       /*
+        *      Parse the list reference
+        *
+        *      This code should be removed when lists
+        *      are integrated into attribute references.
+        */
+       fr_sbuff_out_by_longest_prefix(&list_len, &vpt->data.attribute.list, pair_list_table,
+                                      &our_name, ar_rules->list_def);
+
+       if (ar_rules->disallow_qualifiers && (list_len > 0)) {
+               fr_strerror_printf("It is not permitted to specify a pair list here");
+               if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
+               talloc_free(vpt);
+               FR_SBUFF_ERROR_RETURN(&our_name);
+       }
+
+       /*
+        *      Parse the attribute reference
+        *
+        *      This will either be after:
+        *      - A zero length list, i.e. just after the prefix '&', in which case we require an attribue
+        *      - A ':' or '.' and then an allowed char, so we're sure it's not just a bare list ref.
+        */
+       if ((list_len == 0) ||
+           (((fr_sbuff_next_if_char(&our_name, ':') && (vpt->data.attribute.old_list_sep = true)) ||
+            fr_sbuff_next_if_char(&our_name, '.')) && fr_sbuff_is_in_charset(&our_name, fr_dict_attr_allowed_chars))) {
+               ret = tmpl_attr_ref_afrom_attr_substr(vpt, err,
+                                                     vpt, NULL, &our_name, ar_rules, 0);
+               if (ret < 0) goto error;
+
+               /*
+                *      Check to see if the user wants the leaf
+                *      attribute to be raw.
+                *
+                *      We can only do the conversion now _if_
+                *      the complete hierarchy has been resolved
+                *      otherwise we'll need to do the conversion
+                *      later.
+                */
+               if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
+       }
+
+       /*
+        *      If there's no attribute references
+        *      treat this as a list reference.
+        *
+        *      Eventually we'll remove TMPL_TYPE_LIST
+        */
+       if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
+               tmpl_attr_t *ar;
+
+               MEM(ar = talloc_zero(vpt, tmpl_attr_t));
+               switch (tmpl_attr_ref_parse_filter(err, ar, &our_name)) {
+               case 0:                                         /* No filter */
+                       talloc_free(ar);
                        break;
-               }
 
-               if (*p != ']') {
-                       fr_strerror_printf("No closing ']' for array index");
-                       if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
-                       slen = -(p - name);
+               case 1:                                         /* Found a filter */
+                       fr_dlist_insert_tail(&vpt->data.attribute.ar, ar);
+                       break;
+
+               default:                                        /* Parse error */
                        goto error;
                }
-               p++;
+
+               vpt->type = TMPL_TYPE_LIST;
        }
 
-finish:
-       vpt->name = talloc_strndup(vpt, name, p - name);
-       vpt->len = p - name;
-       vpt->quote = T_BARE_WORD;
+       tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
+       vpt->rules = *ar_rules; /* Record the rules */
+
+       if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
+               fr_strerror_printf("Unexpected text after attribute reference");
+               if (err) *err = ATTR_REF_ERROR_MISSING_TERMINATOR;
+               talloc_free(vpt);
+               *out = NULL;
+               return -fr_sbuff_used(&our_name);
+       }
 
        TMPL_VERIFY(vpt);       /* Because we want to ensure we produced something sane */
 
        *out = vpt;
-
-       return vpt->len;
+       return fr_sbuff_set(name, &our_name);
 }
 
 /** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
@@ -1627,17 +2094,18 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, attr_ref_error_t *err,
 {
        ssize_t slen, name_len;
 
-       if (!rules) rules = &default_rules;     /* Use the defaults */
+       if (!rules) rules = &default_attr_ref_rules;    /* Use the defaults */
 
        name_len = strlen(name);
-       slen = tmpl_afrom_attr_substr(ctx, err, out, name, name_len, rules);
+       slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, rules);
        if (slen <= 0) return slen;
 
        if (!fr_cond_assert(*out)) return -1;
 
        if (slen != name_len) {
-               /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
-               fr_strerror_printf("Unexpected text after %s", fr_table_str_by_value(tmpl_type_table, (*out)->type, "<INVALID>"));
+               /* This looks wrong, but it produces meaningful errors for unknown attrs */
+               fr_strerror_printf("Unexpected text after %s",
+                                  fr_table_str_by_value(tmpl_type_table, (*out)->type, "<INVALID>"));
                return -slen;
        }
 
@@ -1646,10 +2114,373 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, attr_ref_error_t *err,
        return slen;
 }
 
-/** Convert an arbitrary string into a #tmpl_t
+/** Parse a truth value
  *
- * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
- *     may just mean a 0 length string was parsed.
+ * @param[in] ctx      to allocate tmpl to.
+ * @param[out] out     where to write tmpl.
+ * @param[in] in       sbuff to parse.
+ * @param[in] p_rules  formatting rules.
+ * @return
+ *     - 0 sbuff does not contain a boolean value.
+ *     - > 0 how many bytes were parsed.
+ */
+static ssize_t tmpl_afrom_bool_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                     fr_sbuff_parse_rules_t const *p_rules)
+{
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       bool            a_bool;
+       tmpl_t  *vpt;
+
+       if (!fr_sbuff_out(NULL, &a_bool, &our_in)) {
+               fr_strerror_printf("Not a boolean value");
+               return 0;
+       }
+
+       if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+               fr_strerror_printf("Unexpected text after bool");
+               return -fr_sbuff_used(in);
+       }
+
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+
+       fr_value_box_init(&vpt->data.literal, FR_TYPE_BOOL, NULL, false);
+       vpt->data.literal.vb_bool = a_bool;
+
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Parse bareword as an octet string
+ *
+ * @param[in] ctx      to allocate tmpl to.
+ * @param[out] out     where to write tmpl.
+ * @param[in] in       sbuff to parse.
+ * @param[in] p_rules  formatting rules.
+ * @return
+ *     - < 0 negative offset where parse error occurred.
+ *     - 0 sbuff does not contain a hex string.
+ *     - > 0 how many bytes were parsed.
+ */
+static ssize_t tmpl_afrom_octets_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                       fr_sbuff_parse_rules_t const *p_rules)
+{
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       tmpl_t  *vpt;
+       char            *hex;
+       size_t          binlen, len;
+       uint8_t         *bin;
+
+       if (!fr_sbuff_adv_past_strcase_literal(&our_in, "0x")) return 0;
+
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, NULL, 0));
+
+       /*
+        *      This allows stream parsing to work correctly
+        *      we could be less lazy and copy hex data in
+        *      chunks, but never mind...
+        */
+       len = fr_sbuff_out_abstrncpy_allowed(vpt, &hex, &our_in, SIZE_MAX, sbuff_char_class_hex);
+       if (len & 0x01) {
+               fr_strerror_printf("Hex string not even length");
+       error:
+               talloc_free(vpt);
+               return -fr_sbuff_used(&our_in);
+       }
+       if (len == 0) {
+               fr_strerror_printf("Zero length hex string is invalid");
+               goto error;
+       }
+
+       if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+               fr_strerror_printf("Unexpected text after hex string");
+               goto error;
+       }
+
+       bin = (uint8_t *)hex;
+       binlen = len / 2;
+
+       tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in));
+
+       (void)fr_hex2bin(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(hex, len), false);
+       MEM(bin = talloc_realloc_size(vpt, bin, binlen));       /* Realloc to the correct length */
+       (void)fr_value_box_memdup_shallow(&vpt->data.literal, NULL, bin, binlen, false);
+
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Parse bareword as an IPv4 address or prefix
+ *
+ * @param[in] ctx      to allocate tmpl to.
+ * @param[out] out     where to write tmpl.
+ * @param[in] in       sbuff to parse.
+ * @param[in] p_rules  formatting rules.
+ * @return
+ *     - 0 sbuff does not contain an IPv4 address or prefix.
+ *     - > 0 how many bytes were parsed.
+ */
+static ssize_t tmpl_afrom_ipv4_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                     fr_sbuff_parse_rules_t const *p_rules)
+{
+       tmpl_t  *vpt;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       uint8_t         octet;
+       fr_type_t       type;
+
+       /*
+        *      Check for char sequence
+        *
+        *      xxx.xxxx.xxx.xxx
+        */
+       if (!(fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
+             fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
+             fr_sbuff_out(NULL, &octet, &our_in) && fr_sbuff_next_if_char(&our_in, '.') &&
+             fr_sbuff_out(NULL, &octet, &our_in))) {
+       error:
+               return -fr_sbuff_used(&our_in);
+       }
+
+       /*
+        *      If it has a trailing '/' then it's probably
+        *      an IP prefix.
+        */
+       if (fr_sbuff_next_if_char(&our_in, '/')) {
+               if (!fr_sbuff_out(NULL, &octet, &our_in)) {
+                       fr_strerror_printf("IPv4 CIDR mask malformed");
+                       goto error;
+               }
+
+               if (octet > 32) {
+                       fr_strerror_printf("IPv4 CIDR mask too high");
+                       goto error;
+               }
+
+               type = FR_TYPE_IPV4_PREFIX;
+       } else {
+               type = FR_TYPE_IPV4_ADDR;
+       }
+
+       if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+               fr_strerror_printf("Unexpected text after IPv4 string or prefix");
+               goto error;
+       }
+
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+       if (fr_value_box_from_str(vpt, &vpt->data.literal, &type, NULL,
+                                 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), '\0', false) < 0) {
+               talloc_free(vpt);
+               goto error;
+       }
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Parse bareword as an IPv6 address or prefix
+ *
+ * @param[in] ctx              to allocate tmpl to.
+ * @param[out] out             where to write tmpl.
+ * @param[in] in               sbuff to parse.
+ * @param[in] p_rules          formatting rules.
+ * @return
+ *     - 0 sbuff does not contain an IPv4 address or prefix.
+ *     - > 0 how many bytes were parsed.
+ */
+static ssize_t tmpl_afrom_ipv6_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                     fr_sbuff_parse_rules_t const *p_rules)
+{
+       tmpl_t          *vpt;
+       fr_sbuff_t              our_in = FR_SBUFF_NO_ADVANCE(in);
+       fr_sbuff_marker_t       m;
+       fr_type_t               type;
+       size_t                  len;
+       char                    *sep_a, *sep_b;
+
+       static bool ipv6_chars[UINT8_MAX + 1] = {
+               ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
+               ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
+               ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
+               ['f'] = true,
+               ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
+               ['F'] = true,
+               [':'] = true, ['.'] = true
+       };
+
+       /*
+        *      Drop a marker to pin the start of the
+        *      address in the buffer.
+        */
+       fr_sbuff_marker(&m, &our_in);
+
+       /*
+        *      Check for something looking like an IPv6 address
+        */
+       len = fr_sbuff_adv_past_allowed(&our_in, FR_IPADDR_STRLEN + 1, ipv6_chars);
+       if ((len < 3) || (len > FR_IPADDR_STRLEN)) {
+       error:
+               return -fr_sbuff_used(&our_in);
+       }
+
+       /*
+        *      Got ':' after '.', this isn't allowed.
+        *
+        *      We need this check else IPv4 gets parsed
+        *      as blank IPv6 address.
+        */
+       sep_a = memchr(fr_sbuff_current(&m), '.', len);
+       if (sep_a && (!(sep_b = memchr(fr_sbuff_current(&m), ':', len)) || (sep_b > sep_a))) {
+               fr_strerror_printf("First IPv6 component separator was a '.'");
+               goto error;
+       }
+
+       /*
+        *      The v6 parse function will happily turn
+        *      integers into v6 addresses *sigh*.
+        */
+       sep_a = memchr(fr_sbuff_current(&m), ':', len);
+       if (!sep_a) {
+               fr_strerror_printf("No IPv6 component separator");
+               goto error;
+       }
+
+       /*
+        *      Handle scope
+        */
+       if (fr_sbuff_next_if_char(&our_in, '%')) {
+               len = fr_sbuff_adv_until(&our_in, IFNAMSIZ + 1, p_rules->terminals, '\0');
+               if ((len < 1) || (len > IFNAMSIZ)) {
+                       fr_strerror_printf("IPv6 scope too long");
+                       goto error;
+               }
+       }
+
+       /*
+        *      ...and finally the prefix.
+        */
+       if (fr_sbuff_next_if_char(&our_in, '/')) {
+               uint8_t         mask;
+
+               if (!fr_sbuff_out(NULL, &mask, &our_in)) {
+                       fr_strerror_printf("IPv6 CIDR mask malformed");
+                       goto error;
+               }
+               if (mask > 128) {
+                       fr_strerror_printf("IPv6 CIDR mask too high");
+                       goto error;
+               }
+
+               type = FR_TYPE_IPV6_PREFIX;
+       } else {
+               type = FR_TYPE_IPV6_ADDR;
+       }
+
+       if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+               fr_strerror_printf("Unexpected text after IPv6 string or prefix");
+               goto error;
+       }
+
+       MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+       if (fr_value_box_from_str(vpt, &vpt->data.literal, &type, NULL,
+                                 fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), '\0', false) < 0) {
+               talloc_free(vpt);
+               goto error;
+       }
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Try and parse signed or unsigned integers
+ *
+ * @param[in] ctx      to allocate tmpl to.
+ * @param[out] out     where to write tmpl.
+ * @param[in] in       sbuff to parse.
+ * @return
+ *     - 0 sbuff does not contain an integer.
+ *     - > 0 how many bytes were parsed.
+ */
+static ssize_t tmpl_afrom_integer_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                        fr_sbuff_parse_rules_t const *p_rules)
+{
+       tmpl_t  *vpt;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       ssize_t         slen;
+       fr_value_box_t  *vb;
+
+       /*
+        *      Pick the narrowest signed type
+        */
+       if (fr_sbuff_is_char(&our_in, '-')) {
+               int64_t         a_int;
+
+               slen = fr_sbuff_out(NULL, &a_int, &our_in);
+               if (slen <= 0) return 0;
+
+               if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+                       fr_strerror_printf("Unexpected text after signed integer");
+               error:
+                       return -fr_sbuff_used(&our_in);
+               }
+
+               MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
+                                    T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+               vb = tmpl_value(vpt);
+               if (a_int >= INT8_MIN) {
+                       fr_value_box_init(vb, FR_TYPE_INT8, NULL, false);
+                       vb->vb_int8 = (int8_t)a_int;
+               } else if (a_int >= INT16_MIN) {
+                       fr_value_box_init(vb, FR_TYPE_INT16, NULL, false);
+                       vb->vb_int16 = (int16_t)a_int;
+               } else if (a_int >= INT32_MIN) {
+                       fr_value_box_init(vb, FR_TYPE_INT32, NULL, false);
+                       vb->vb_int32 = (int32_t)a_int;
+               } else {
+                       fr_value_box_init(vb, FR_TYPE_INT64, NULL, false);
+                       vb->vb_int64 = (int64_t)a_int;
+               }
+       /*
+        *      Pick the widest signed type
+        */
+       } else {
+               uint64_t        a_uint;
+
+               slen = fr_sbuff_out(NULL, &a_uint, &our_in);
+               if (slen <= 0) return slen;
+
+               if (!tmpl_substr_terminal_check(&our_in, p_rules)) {
+                       fr_strerror_printf("Unexpected text after unsigned integer");
+                       goto error;
+               }
+
+               MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA,
+                                    T_BARE_WORD, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+               vb = tmpl_value(vpt);
+               if (a_uint <= UINT8_MAX) {
+                       fr_value_box_init(vb, FR_TYPE_UINT8, NULL, false);
+                       vb->vb_uint8 = (uint8_t)a_uint;
+               } else if (a_uint <= UINT16_MAX) {
+                       fr_value_box_init(vb, FR_TYPE_UINT16, NULL, false);
+                       vb->vb_uint16 = (uint16_t)a_uint;
+               } else if (a_uint <= UINT32_MAX) {
+                       fr_value_box_init(vb, FR_TYPE_UINT32, NULL, false);
+                       vb->vb_uint32 = (uint32_t)a_uint;
+               } else {
+                       fr_value_box_init(vb, FR_TYPE_UINT64, NULL, false);
+                       vb->vb_uint64 = (uint64_t)a_uint;
+               }
+       }
+
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Convert an arbitrary string into a #tmpl_t
+ *
+ * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
+ *     may just mean a 0 length string was parsed.
  *
  * @note xlats and regexes are left uncompiled.  This is to support the two pass parsing
  *     done by the modcall code.  Compilation on pass1 of that code could fail, as
@@ -1659,22 +2490,8 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, attr_ref_error_t *err,
  *
  * @param[in,out] ctx          To allocate #tmpl_t in.
  * @param[out] out             Where to write the pointer to the new #tmpl_t.
- * @param[in] in               String to convert to a #tmpl_t.
- * @param[in] inlen            length of string to convert.
- * @param[in] type             of quoting around value. May be one of:
- *                             - #T_BARE_WORD - If string begins with ``&``
- *                               produces #TMPL_TYPE_ATTR,
- *                               #TMPL_TYPE_ATTR_UNRESOLVED, #TMPL_TYPE_LIST or error.
- *                               If string does not begin with ``&`` produces
- *                               #TMPL_TYPE_UNRESOLVED, #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
- *                             - #T_SINGLE_QUOTED_STRING - Produces #TMPL_TYPE_UNRESOLVED
- *                             - #T_DOUBLE_QUOTED_STRING - Produces #TMPL_TYPE_XLAT_UNRESOLVED or
- *                               #TMPL_TYPE_UNRESOLVED (if string doesn't contain ``%``).
- *                             - #T_BACK_QUOTED_STRING - Produces #TMPL_TYPE_EXEC
- *                             - #T_OP_REG_EQ - Produces #TMPL_TYPE_REGEX_UNRESOLVED
- * @param[in] rules            Parsing rules for attribute references.
- * @param[in] do_unescape      whether or not we should do unescaping.
- *                             Should be false if the caller already did it.
+ * @param[in] p_rules          Formatting rules for the tmpl.
+ * @param[in] ar_rules         Validation rules for attribute references.
  * @return
  *     - <= 0 on error (offset as negative integer)
  *     - > 0 on success (number of bytes parsed).
@@ -1683,214 +2500,362 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, attr_ref_error_t *err,
  *
  * @see tmpl_afrom_attr_substr
  */
-ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, tmpl_t **out,
-                      char const *in, size_t inlen, fr_token_t type, tmpl_rules_t const *rules, bool do_unescape)
+ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
+                         fr_sbuff_t *in, fr_token_t quote,
+                         fr_sbuff_parse_rules_t const *p_rules,
+                         tmpl_rules_t const *ar_rules)
 {
-       bool            do_xlat;
-       char            quote;
-       char const      *p;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+
        ssize_t         slen;
-       fr_type_t       data_type = FR_TYPE_STRING;
-       tmpl_t  *vpt = NULL;
-       fr_value_box_t  data;
+       char            *str;
 
-       if (!rules) rules = &default_rules;     /* Use the defaults */
+       tmpl_t          *vpt = NULL;
 
-       switch (type) {
-       case T_BARE_WORD:
-       {
-               tmpl_rules_t    mrules;
+       if (!ar_rules) ar_rules = &default_attr_ref_rules;      /* Use the defaults */
 
-               memcpy(&mrules, rules, sizeof(mrules));
+       *out = NULL;
 
+       switch (quote) {
+       case T_BARE_WORD:
                /*
-                *  No attribute names start with 0x, and if they did, the user
-                *  can just use the explicit & prefix.
+                *      Skip other bareword types if
+                *      we find a '&' prefix.
                 */
-               if ((in[0] == '0') && (tolower(in[1]) == 'x')) {
-                       size_t binlen, len;
-                       uint8_t *bin;
+               if (fr_sbuff_is_char(&our_in, '&')) return tmpl_afrom_attr_substr(ctx, NULL, out, in,
+                                                                                 p_rules, ar_rules);
 
-                       /*
-                        *  Hex strings must contain even number of characters
-                        */
-                       if (inlen & 0x01) {
-                               fr_strerror_printf("Hex string not even length");
-                               return -inlen;
-                       }
+               /*
+                *      Allow bareword xlats if we
+                *      find a '%' prefix.
+                */
+               if (fr_sbuff_is_char(&our_in, '%')) {
+                       tmpl_type_t     type = TMPL_TYPE_XLAT;
+                       xlat_exp_t      *head = NULL;
+                       xlat_flags_t    flags = {};
 
-                       if (inlen <= 2) {
-                               fr_strerror_printf("Zero length hex string is invalid");
-                               return -inlen;
-                       }
+                       vpt = tmpl_alloc_null(ctx);
+                       slen = xlat_tokenize(vpt, &head, &flags,
+                                            &our_in, p_rules, ar_rules);
+                       if (!head) return slen;
 
-                       binlen = (inlen - 2) / 2;
+                       if (flags.needs_resolving) type |= TMPL_FLAG_UNRESOLVED;
 
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_DATA, in, inlen, type);
-                       (void)fr_value_box_mem_alloc(vpt, &bin, &vpt->data.literal, NULL, binlen, false);
-                       len = fr_hex2bin(NULL, &FR_DBUFF_TMP(bin, binlen), &FR_SBUFF_IN(in + 2, inlen - 2), false);
-                       if (len != binlen) {
-                               fr_strerror_printf("Hex string contains non-hex char");
-                               talloc_free(vpt);
-                               return -(len + 2);
-                       }
-                       slen = len;
-                       break;
+                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+                       vpt->data.xlat.ex = head;
+                       vpt->data.xlat.flags = flags;
+
+                       *out = vpt;
+
+                       TMPL_VERIFY(vpt);
+
+                       return fr_sbuff_set(in, &our_in);
                }
 
                /*
-                *      If we can parse it as an attribute, it's an attribute.
-                *      Otherwise, treat it as a literal.
+                *      See if it's a boolean value
                 */
-               quote = '\0';
+               slen = tmpl_afrom_bool_substr(ctx, out, &our_in, p_rules);
+               if (slen > 0) {
+               done_bareword:
+                       TMPL_VERIFY(*out);
 
-               mrules.allow_unresolved = (in[0] == '&');
+                       return fr_sbuff_set(in, &our_in);
+               }
 
                /*
-                *      This doesn't take a length, but it does return
-                *      how many "good" bytes it parsed.  If we didn't
-                *      parse the whole string, then it's an error.
-                *      We can't define a template with garbage after
-                *      the attribute name.
+                *      See if it's an octets string
                 */
-               slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt, in, inlen, &mrules);
-               if (mrules.allow_unresolved && (slen <= 0)) return slen;
-               if (slen > 0) {
-                       if ((size_t) slen < inlen) {
-                               fr_strerror_printf("Unexpected text after attribute name");
-                               talloc_free(vpt);
-                               return -slen;
-                       }
-                       break;
-               }
-       }
-               goto parse;
+               slen = tmpl_afrom_octets_substr(ctx, out, &our_in, p_rules);
+               if (slen > 0) goto done_bareword;
 
-       case T_SINGLE_QUOTED_STRING:
-               quote = '\'';
+               /*
+                *      See if it's an IPv4 address or prefix
+                */
+               slen = tmpl_afrom_ipv4_substr(ctx, out, &our_in, p_rules);
+               if (slen > 0) goto done_bareword;
 
-       parse:
-               if (do_unescape) {
-                       if (fr_value_box_from_str(ctx, &data, &data_type, NULL,
-                                                 in, inlen, quote, false) < 0) return 0;
+               /*
+                *      See if it's an IPv6 address or prefix
+                */
+               slen = tmpl_afrom_ipv6_substr(ctx, out, &our_in, p_rules);
+               if (slen > 0) goto done_bareword;
 
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_UNRESOLVED, data.vb_strvalue,
-                                        talloc_array_length(data.vb_strvalue) - 1, type);
-                       talloc_free(data.datum.ptr);
-               } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_UNRESOLVED, in, inlen, type);
+               /*
+                *      See if it's a float
+                */
+//             slen = tmpl_afrom_float_substr(ctx, out, &our_in);
+//             if (slen > 0) return fr_sbuff_set(in, &our_in);
+
+               /*
+                *      See if it's a mac address
+                */
+//             slen = tmpl_afrom_mac_address_substr(ctx, out, &our_in);
+//             if (slen > 0) return fr_sbuff_set(in, &our_in);
+
+               /*
+                *      See if it's a integer
+                */
+               slen = tmpl_afrom_integer_substr(ctx, out, &our_in, p_rules);
+               if (slen > 0) goto done_bareword;
+
+               /*
+                *      See if it's an attribute reference
+                *      without the prefix.
+                */
+               slen = tmpl_afrom_attr_substr(ctx, NULL, out, &our_in, p_rules, ar_rules);
+               if (slen > 0) goto done_bareword;
+
+               vpt = tmpl_alloc_null(ctx);
+
+               /*
+                *      If it doesn't match any other type
+                *      of bareword, assume it's an enum
+                *      value.
+                */
+               slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
+                                                   p_rules ? p_rules->terminals : NULL,
+                                                   p_rules ? p_rules->escapes : NULL);
+               if (slen == 0) {
+                       fr_strerror_printf("Empty bareword is invalid");
+                       talloc_free(vpt);
+                       return 0;
                }
-               slen = vpt->len;
+
+               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, T_BARE_WORD, fr_sbuff_start(&our_in), slen);
+               vpt->data.unescaped = str;
+               *out = vpt;
+
+               return fr_sbuff_set(in, &our_in);
+
+       case T_SINGLE_QUOTED_STRING:
+               vpt = tmpl_alloc_null(ctx);
+               slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
+                                                   p_rules ? p_rules->terminals : NULL,
+                                                   p_rules ? p_rules->escapes : NULL);
+               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
+               vpt->data.unescaped = str;
                break;
 
        case T_DOUBLE_QUOTED_STRING:
-               do_xlat = false;
-
-               p = in;
-               while (*p) {
-                       if (do_unescape) { /* otherwise \ is just another character */
-                               if (*p == '\\') {
-                                       if (!p[1]) break;
-                                       p += 2;
-                                       continue;
-                               }
-                       }
+       {
+               xlat_exp_t      *head = NULL;
+               xlat_flags_t    flags = {};
 
-                       if (*p == '%') {
-                               do_xlat = true;
-                               break;
-                       }
+               vpt = tmpl_alloc_null(ctx);
+               slen = xlat_tokenize(vpt,
+                                    &head, &flags,
+                                    &our_in, p_rules, ar_rules);
+               if (!head) return slen;
 
-                       p++;
-               }
+               /*
+                *      If the string doesn't contain an xlat, just
+                *      convert the xlat expansion into an unescaped
+                *      literal for parsing later.
+                */
+               if (xlat_to_literal(vpt, &str, &head)) {
+                       tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
+                       vpt->data.unescaped = str;      /* Store the unescaped string for parsing later */
 
                /*
-                *      If the double quoted string needs to be
-                *      expanded at run time, make it an xlat
-                *      expansion.  Otherwise, convert it to be a
-                *      literal.
+                *      If the string actually contains an xlat
+                *      store the compiled xlat.
                 */
-               if (do_unescape) {
-                       if (fr_value_box_from_str(ctx, &data, &data_type, NULL, in,
-                                                 inlen, fr_token_quote[type], false) < 0) return -1;
-                       if (do_xlat) {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT_UNRESOLVED, data.vb_strvalue,
-                                                talloc_array_length(data.vb_strvalue) - 1, type);
-                       } else {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_UNRESOLVED, data.vb_strvalue,
-                                                talloc_array_length(data.vb_strvalue) - 1, type);
-                               vpt->quote = T_DOUBLE_QUOTED_STRING;
-                       }
-                       talloc_free(data.datum.ptr);
                } else {
-                       if (do_xlat) {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT_UNRESOLVED, in, inlen, type);
-                       } else {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_UNRESOLVED, in, inlen, type);
-                               vpt->quote = T_DOUBLE_QUOTED_STRING;
-                       }
+                       tmpl_type_t     type = TMPL_TYPE_XLAT;
+
+                       if (flags.needs_resolving) type |= TMPL_FLAG_UNRESOLVED;
+
+                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+                       vpt->data.xlat.ex = head;
+                       vpt->data.xlat.flags = flags;
                }
-               slen = vpt->len;
+       }
                break;
 
        case T_BACK_QUOTED_STRING:
-               if (do_unescape) {
-                       if (fr_value_box_from_str(ctx, &data, &data_type, NULL, in,
-                                                 inlen, fr_token_quote[type], false) < 0) return -1;
+       {
+               tmpl_type_t             type = TMPL_TYPE_EXEC;
+               xlat_exp_t              *head = NULL;
+               xlat_flags_t            flags = {};
 
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, data.vb_strvalue,
-                                        talloc_array_length(data.vb_strvalue) - 1, type);
-                       talloc_free(data.datum.ptr);
-               } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, in, inlen, type);
-               }
+               vpt = tmpl_alloc_null(ctx);
 
                /*
                 *      Ensure that we pre-parse the exec string.
                 *      This allows us to catch parse errors as early
                 *      as possible.
                 */
-               slen = xlat_tokenize_argv(vpt, &tmpl_xlat(vpt), vpt->name, talloc_array_length(vpt->name) - 1, rules);
-               if (slen <= 0) {
+               slen = xlat_tokenize_argv(vpt, &head, &flags, &our_in, p_rules, ar_rules);
+               if (slen < 0) {
+                       fr_sbuff_advance(&our_in, slen * -1);
                        talloc_free(vpt);
                        return slen;
                }
 
-               slen = vpt->len;
-               break;
+               if (flags.needs_resolving) type |= TMPL_FLAG_UNRESOLVED;
 
-       case T_OP_REG_EQ: /* hack */
-               vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX_UNRESOLVED, in, inlen, T_BARE_WORD);
-               slen = vpt->len;
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               vpt->data.xlat.ex = head;
+               vpt->data.xlat.flags = flags;
+       }
                break;
 
-       default:
-               fr_assert(0);
-               return 0;       /* 0 is an error here too */
-       }
+       case T_SOLIDUS_QUOTED_STRING:
+       {
 
-       if (!vpt) return 0;
+               xlat_exp_t              *head = NULL;
+               xlat_flags_t            flags = {};
 
-       vpt->quote = type;
+               vpt = tmpl_alloc_null(ctx);
 
-       fr_assert(slen >= 0);
+               slen = xlat_tokenize(vpt, &head, &flags, &our_in, p_rules, ar_rules);
+               if (!head) return slen;
 
-       TMPL_VERIFY(vpt);
-       *out = vpt;
+               /*
+                *      Check if the string actually contains an xlat
+                *      if it doesn't, we unfortunately still
+                *      can't compile it here, as we don't know if it
+                *      should be ephemeral or what flags should be used
+                *      during the compilation.
+                *
+                *      The caller will need to do the compilation
+                *      after we return.
+                */
 
-       return slen;
-}
-/** @} */
+               if (xlat_to_literal(vpt, &str, &head)) {
+                       tmpl_init(vpt, TMPL_TYPE_REGEX_UNCOMPILED, quote, fr_sbuff_start(&our_in), slen);
+                       vpt->data.unescaped = str;      /* Store the unescaped string for compilation later */
 
-/** @name Cast or convert #tmpl_t
- *
- * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_UNRESOLVED to a #TMPL_TYPE_DATA of a
- *  specified #fr_type_t.
- *
- * #tmpl_cast_to_vp does the same as #tmpl_cast_in_place, but outputs a #VALUE_PAIR.
- *
- * #tmpl_unknown_attr_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
- * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
+               /*
+                *      Mark the regex up as a regex-xlat which
+                *      will need expanding before evaluation, and can never
+                *      be pre-compiled.
+                */
+               } else {
+                       tmpl_type_t type = TMPL_TYPE_REGEX_XLAT;
+
+                       if (flags.needs_resolving) type |= TMPL_FLAG_UNRESOLVED;
+
+                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+                       vpt->data.xlat.ex = head;
+                       vpt->data.xlat.flags = flags;
+               }
+       }
+               break;
+
+       default:
+               fr_assert(0);
+               return 0;       /* 0 is an error here too */
+       }
+
+       TMPL_VERIFY(vpt);
+       *out = vpt;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Parse a cast specifier
+ *
+ * @param[out] out     Where to write the cast type.
+ *                     Will default to FR_TYPE_INVALID.
+ * @param[in] in       String containing the cast marker.
+ * @return
+ *     - 0 no cast specifier found.
+ *     - >0 the number of bytes parsed.
+ *     - <0 offset of parse error.
+ */
+ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in)
+{
+       fr_sbuff_t              our_in = FR_SBUFF_NO_ADVANCE(in);
+       fr_sbuff_marker_t       m;
+       fr_type_t               cast = FR_TYPE_INVALID;
+       size_t                  slen;
+
+       if (fr_sbuff_next_if_char(&our_in, '<')) {
+               fr_sbuff_marker(&m, &our_in);
+               fr_sbuff_out_by_longest_prefix(&slen, &cast, fr_value_box_type_table, &our_in, FR_TYPE_INVALID);
+               if (cast == FR_TYPE_INVALID) {
+                       fr_strerror_printf("Unknown data type");
+                       FR_SBUFF_ERROR_RETURN(&our_in);
+               }
+               if (fr_dict_non_data_types[cast]) {
+                       fr_strerror_printf("Forbidden data type in cast");
+                       FR_SBUFF_MARKER_ERROR_RETURN(&m);
+               }
+               if (!fr_sbuff_next_if_char(&our_in, '>')) {
+                       fr_strerror_printf("Unterminated cast");
+                       FR_SBUFF_ERROR_RETURN(&our_in);
+               }
+               fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+       }
+       if (out) *out = cast;
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Set a cast for a tmpl
+ *
+ * @param[in,out] vpt  to set cast for.
+ * @param[in] type     to set.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int tmpl_cast_set(tmpl_t *vpt, fr_type_t type)
+{
+       if (fr_dict_non_data_types[type]) {
+               fr_strerror_printf("Forbidden data type in cast");
+               return -1;
+       }
+
+       vpt->cast = type;
+
+       return 0;
+}
+
+#ifdef HAVE_REGEX
+/** Parse a set of regular expression flags
+ *
+ * @param[out] vpt     Write the flags to the regex flags field in this #tmpl_t.
+ * @param[in] in       Where to parse the flag string from.
+ * @param[in] terminals        That mark the end of the regex flag string.
+ * @return
+ *     - 0 no flags found.
+ *     - >0 the number of bytes of flags parsed.
+ *     - <0 offset of parse error.
+ */
+ssize_t tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in, fr_sbuff_term_t const *terminals)
+{
+       ssize_t slen;
+       int     err = 0;
+
+       fr_assert(tmpl_is_regex_uncompiled(vpt) || tmpl_is_regex_xlat(vpt) || tmpl_is_regex_xlat_unresolved(vpt));
+
+       slen = regex_flags_parse(&err, &vpt->data.reg.flags, in, terminals, true);
+       switch (err) {
+       case 0:
+               break;
+
+       case -1:        /* Non-flag and non-terminal */
+       case -2:        /* Duplicate flag */
+               return slen;
+       }
+
+       return slen;
+}
+#endif
+/** @} */
+
+/** @name Change a #tmpl_t type, usually by casting or resolving a reference
+ *
+ * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_UNRESOLVED to a #TMPL_TYPE_DATA of a
+ * specified #fr_type_t.
+ *
+ * #tmpl_cast_from_substr_to_vp does the same as #tmpl_cast_in_place, but outputs a #VALUE_PAIR.
+ *
+ * #tmpl_attr_unknown_add converts a #TMPL_TYPE_ATTR with an unknown #fr_dict_attr_t to a
+ * #TMPL_TYPE_ATTR with a known #fr_dict_attr_t, by adding the unknown #fr_dict_attr_t to the main
  * dictionary, and updating the ``tmpl_da`` pointer.
  * @{
  */
@@ -1913,18 +2878,23 @@ int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
 {
        TMPL_VERIFY(vpt);
 
-       fr_assert(vpt != NULL);
        fr_assert(tmpl_is_unresolved(vpt) || tmpl_is_data(vpt));
 
        switch (vpt->type) {
        case TMPL_TYPE_UNRESOLVED:
+       {
+               char *unescaped = vpt->data.unescaped;
+
                /*
                 *      Why do we pass a pointer to a temporary type
                 *      variable? Goddamn WiMAX.
                 */
                if (fr_value_box_from_str(vpt, &vpt->data.literal, &type,
-                                         enumv, vpt->name, vpt->len, '\0', false) < 0) return -1;
+                                         enumv, unescaped, talloc_array_length(unescaped) - 1,
+                                         '\0', false) < 0) return -1;
+               talloc_free(unescaped);
                vpt->type = TMPL_TYPE_DATA;
+       }
                break;
 
        case TMPL_TYPE_DATA:
@@ -1940,97 +2910,777 @@ int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv)
        }
                break;
 
+       case TMPL_TYPE_ATTR:    /* FIXME - We should check cast compatibility with resolved attrs */
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+               vpt->cast = type;
+               break;
+
        default:
                fr_assert(0);
        }
 
+       /*
+        *      Fixup quoting
+        */
+       switch (type) {
+       case FR_TYPE_STRING:
+               switch (vpt->quote) {
+               case T_SINGLE_QUOTED_STRING:
+               case T_DOUBLE_QUOTED_STRING:
+                       break;
+
+               default:
+                       vpt->quote = T_SINGLE_QUOTED_STRING;
+                       break;
+               }
+               break;
+
+       default:
+               vpt->quote = T_BARE_WORD;
+               break;
+       }
+
        TMPL_VERIFY(vpt);
 
        return 0;
 }
 
-/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+/** Resolve an unresolved attribute
  *
- * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
- *     #fr_dict_attr_t inserted into the dictionary.
+ * Multi-pass parsing fixups for attribute references.
+ */
+static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt)
+{
+       tmpl_attr_t             *ar = NULL;
+       fr_dict_attr_t const    *parent = NULL;
+       fr_dict_attr_t const    *da;
+
+       fr_assert(tmpl_is_attr_unresolved(vpt));
+
+       /*
+        *      First ref is special as it can resolve in the
+        *      internal dictionary or the protocol specific
+        *      dictionary.
+        */
+       ar = fr_dlist_head(&vpt->data.attribute.ar);
+       if (fr_dict_attr_by_qualified_name(&da, vpt->rules.dict_def,
+                                          ar->ar_unresolved, true) != FR_DICT_ATTR_OK) {
+               parent = fr_dict_root(vpt->rules.dict_def);
+               goto unknown;
+       } else {
+               ar->da = da;
+               ar->type = TMPL_ATTR_TYPE_NORMAL;
+               parent = ar->da;
+       }
+
+       /*
+        *      Loop, resolving each unresolved attribute in turn
+        */
+       while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+               ssize_t         slen;
+               bool            is_raw;
+
+               if ((ar->type == TMPL_ATTR_TYPE_NORMAL) || (ar->type == TMPL_ATTR_TYPE_UNKNOWN)) break;
+
+               is_raw = ar->ar_unresolved_raw;
+
+               slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent,
+                                                        &FR_SBUFF_IN(ar->ar_unresolved, strlen(ar->ar_unresolved)),
+                                                        false);
+               if (slen <= 0) {
+                       fr_dict_attr_t  *unknown_da;
+
+               unknown:
+                       /*
+                        *      Can't find it under its regular name.  Try an unknown attribute.
+                        */
+                       slen = fr_dict_unknown_afrom_oid_str(vpt, &unknown_da, parent, ar->ar_unresolved);
+                       if ((slen <= 0) || (ar->ar_unresolved[slen] != '\0')) {
+                               fr_strerror_printf_push("Failed resolving unresolved attribute");
+                               return -1;
+                       }
+
+                       ar->type = TMPL_ATTR_TYPE_UNKNOWN;
+                       ar->da = ar->unknown.da = unknown_da;
+                       parent = ar->da;
+                       continue;
+               }
+
+               /*
+                *      Known attribute, just rewrite.
+                */
+               ar->da = da;
+               parent = ar->da;
+
+               /*
+                *      If the user wanted the leaf
+                *      to be raw, and it's not, correct
+                *      that now.
+                */
+               if (is_raw) tmpl_attr_ref_to_raw(vpt, ar);
+       }
+
+       vpt->type ^= TMPL_FLAG_UNRESOLVED;
+
+       TMPL_VERIFY(vpt);
+
+       return 0;
+}
+
+/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
+ *
+ * Multi-pass parsing fixups for attribute references.
+ *
+ * Works for base types:
+ * - TMPL_TYPE_XLAT
+ * - TMPL_TYPE_EXEC
+ * - TMPL_TYPE_REGEX_XLAT
+ *
+ * @param[in] vpt      Containing the xlat expansion to resolve.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static inline CC_HINT(always_inline) int tmpl_xlat_resolve(tmpl_t *vpt)
+{
+       if (xlat_resolve(&vpt->data.xlat.ex, &vpt->data.xlat.flags, false) < 0) return -1;
+
+       vpt->type ^= TMPL_FLAG_UNRESOLVED;
+
+       return 0;
+}
+
+/** Attempt to resolve functions and attributes in xlats and attribute references
+ *
+ * @param[in,out] vpt  to resolve.  Should be of type TMPL_TYPE_XLAT_UNRESOLVED
+ *                     or TMPL_TYPE_ATTR_UNRESOLVED.  All other types will be
+ *                     noops.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int tmpl_resolve(tmpl_t *vpt)
+{
+       int ret = 0;
+
+       if (!tmpl_needs_resolving(vpt)) return 0;       /* Nothing to do */
+
+       /*
+        *      The xlat component of the #tmpl_t needs resolving.
+        */
+       if (tmpl_contains_xlat(vpt)) {
+               ret = tmpl_xlat_resolve(vpt);
+       /*
+        *      The attribute reference needs resolving.
+        */
+       } else if (tmpl_contains_attr(vpt)) {
+               ret = tmpl_attr_resolve(vpt);
+
+       /*
+        *      Convert unresolved tmpls into literal string values.
+        */
+       } else if (tmpl_is_unresolved(vpt)) {
+               char *unescaped = vpt->data.unescaped;  /* Copy the pointer before zeroing the union */
+
+               fr_value_box_init_null(&vpt->data.literal);
+               fr_value_box_bstrdup_buffer_shallow(NULL, &vpt->data.literal, NULL, unescaped, false);
+               vpt->type = TMPL_TYPE_DATA;
+       }
+
+       TMPL_VERIFY(vpt);
+
+       return ret;
+}
+
+/** Reset the tmpl, leaving only the name in place
+ *
+ * After calling this function, the tmpl type will revert to TMPL_TYPE_UNRESOLVED
+ * and only the name and quoting will be preserved.
+ *
+ * @param[in] vpt      to reset.
+ */
+void tmpl_unresolve(tmpl_t *vpt)
+{
+       tmpl_t  tmp = {
+                       .type = TMPL_TYPE_UNRESOLVED,
+                       .name = vpt->name,
+                       .len = vpt->len,
+                       .quote = vpt->quote
+               };
+
+       switch (vpt->type) {
+       case TMPL_TYPE_UNINITIALISED:
+       case TMPL_TYPE_MAX:
+               fr_assert(0);
+               break;
+
+       case TMPL_TYPE_NULL:
+       case TMPL_TYPE_UNRESOLVED:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+               break;
+
+       case TMPL_TYPE_DATA:
+               fr_value_box_clear(&vpt->data.literal);
+               break;
+
+       /*
+        *      These types contain dynamically allocated
+        *      attribute and request references.
+        */
+       case TMPL_TYPE_LIST:
+       case TMPL_TYPE_ATTR:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+               fr_dlist_talloc_free(&vpt->data.attribute.ar);
+               fr_dlist_talloc_free(&vpt->data.attribute.rr);
+               break;
+
+       /*
+        *      These all store an xlat expansion
+        */
+       case TMPL_TYPE_EXEC:
+       case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_EXEC_UNRESOLVED:
+       case TMPL_TYPE_XLAT_UNRESOLVED:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
+               xlat_exp_free(&vpt->data.xlat.ex);
+               break;
+
+       case TMPL_TYPE_REGEX:
+               talloc_free(vpt->data.reg.ex);
+               break;
+
+       }
+
+       memcpy(vpt, &tmp, sizeof(*vpt));
+
+       TMPL_VERIFY(vpt);
+}
+
+/** Convert an attribute reference to an xlat expansion
+ *
+ * This is where a user attempts to use an attribute reference which is actually
+ * a virtual attribute.
+ *
+ * @param[in] ctx              to convert new tmpl in.
+ * @param[in,out] vpt_p                pointer to #tmpl_t of TMPL_TYPE_ATTR | TMPL_TYPE_ATTR_UNPARSED.
+ */
+int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
+{
+
+       tmpl_t  *vpt;
+       tmpl_t  *attr = *vpt_p;
+
+       /*
+        *      First alloc a new tmpl to hold the xlat expansion
+        */
+       vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, attr->quote, attr->name, attr->len);
+
+       /*
+        *      ...then wrap the old tmpl_t in an xlat expansion
+        *      doing conversion to a virtual attribute if necessary.
+        */
+       if (xlat_from_tmpl_attr(vpt, &vpt->data.xlat.ex, &vpt->data.xlat.flags, vpt_p) < 0) {
+               talloc_free(vpt);
+               return -1;
+       }
+
+       *vpt_p = vpt;
+
+       return 0;
+}
+
+static void tmpl_attr_ref_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
+{
+       if (!ref) return;
+
+       switch (ref->type) {
+       case TMPL_ATTR_TYPE_NORMAL:
+       {
+               char            buffer[256] = "raw.";
+               char            *p = buffer + strlen(buffer);
+               char            *end = buffer + sizeof(buffer);
+               size_t          len;
+               fr_dict_attr_t  *da;
+
+               len = fr_dict_print_attr_oid(NULL, p, end - p, NULL, ref->da);
+               p += len;
+
+               ref->da = ref->ar_unknown = da = fr_dict_unknown_acopy(vpt, ref->da);
+               ref->ar_unknown->type = FR_TYPE_OCTETS;
+               ref->ar_unknown->flags.is_raw = 1;
+               ref->ar_unknown->flags.is_unknown = 1;
+
+               talloc_const_free(da->name);
+               MEM(da->name = talloc_bstrndup(da, buffer, p - buffer));
+
+               ref->type = TMPL_ATTR_TYPE_UNKNOWN;
+       }
+               break;
+
+       case TMPL_ATTR_TYPE_UNKNOWN:
+               ref->ar_unknown->type = FR_TYPE_OCTETS;
+               ref->ar_unknown->flags.is_raw = 1;
+               break;
+
+       case TMPL_ATTR_TYPE_UNRESOLVED:
+               ref->ar_unresolved_raw = true;
+               break;
+       }
+
+       TMPL_ATTR_VERIFY(vpt);
+}
+
+/** Covert the leaf attribute of a tmpl to a unknown/raw type
+ *
+ */
+void tmpl_attr_to_raw(tmpl_t *vpt)
+{
+       tmpl_attr_ref_to_raw(vpt, fr_dlist_tail(&vpt->data.attribute.ar));
+}
+
+/** Convert an abstract da into a concrete one
+ *
+ * Usually used to fixup combo ip addresses
+ */
+int tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type)
+{
+       fr_dict_attr_t const    *abstract;
+       fr_dict_attr_t const    *concrete;
+       tmpl_attr_t     *ref;
+
+       tmpl_assert_type(tmpl_is_attr(vpt));
+
+       abstract = tmpl_da(vpt);
+       if (abstract->type != FR_TYPE_COMBO_IP_ADDR) {
+               fr_strerror_printf("Abstract attribute \"%s\" is of incorrect type '%s'", abstract->name,
+                                  fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"));
+               return -1;
+       }
+
+       concrete = fr_dict_attr_by_type(abstract, type);
+       if (!concrete) {
+               fr_strerror_printf("Can't convert abstract type '%s' to concrete type '%s'",
+                                  fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"),
+                                  fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"));
+               return -1;
+       }
+
+       ref = fr_dlist_tail(&vpt->data.attribute.ar);
+       ref->da = concrete;
+
+       TMPL_ATTR_VERIFY(vpt);
+
+       return 0;
+}
+
+/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+ *
+ * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
+ *     #fr_dict_attr_t inserted into the dictionary.
+ * @return
+ *     - 1 noop (did nothing) - Not possible to convert tmpl.
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int tmpl_attr_unknown_add(tmpl_t *vpt)
+{
+       fr_dict_attr_t const *da;
+
+       if (!vpt) return 1;
+
+       tmpl_assert_type(tmpl_is_attr(vpt));
+
+       TMPL_VERIFY(vpt);
+
+       if (!tmpl_da(vpt)->flags.is_unknown) return 1;
+
+       da = tmpl_da(vpt);
+       da = fr_dict_unknown_add(fr_dict_unconst(fr_dict_by_da(da)), da);
+       if (!da) return -1;
+       tmpl_attr_set_leaf_da(vpt, da);
+
+       return 0;
+}
+
+/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+ *
+ * @note fr_dict_attr_add will not return an error if the attribute already exists
+ *     meaning that multiple #tmpl_t specifying the same attribute can be
+ *     passed to this function to be fixed up, so long as the type and flags
+ *     are identical.
+ *
+ * @param[in] dict_def Default dictionary to use if none is
+ *                     specified by the tmpl_attr_unresolved.
+ * @param[in] vpt      specifying unresolved attribute to add.
+ *                     ``tmpl_da`` pointer will be updated to
+ *                     point to the #fr_dict_attr_t inserted
+ *                     into the dictionary. Lists and requests
+ *                     will be preserved.
+ * @param[in] type     to define unresolved attribute as.
+ * @param[in] flags    to define unresolved attribute with.
+ * @return
+ *     - 1 noop (did nothing) - Not possible to convert tmpl.
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int tmpl_attr_unresolved_add(fr_dict_t *dict_def, tmpl_t *vpt,
+                            fr_type_t type, fr_dict_attr_flags_t const *flags)
+{
+       fr_dict_attr_t const *da;
+
+       if (!vpt) return -1;
+
+       TMPL_VERIFY(vpt);
+
+       if (!tmpl_is_attr_unresolved(vpt)) return 1;
+
+       if (fr_dict_attr_add(dict_def,
+                            fr_dict_root(fr_dict_internal()), tmpl_attr_unresolved(vpt), -1, type, flags) < 0) {
+               return -1;
+       }
+       da = fr_dict_attr_by_name(dict_def, tmpl_attr_unresolved(vpt));
+       if (!da) return -1;
+
+       if (type != da->type) {
+               fr_strerror_printf("Attribute %s of type %s already defined with type %s",
+                                  da->name, fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"),
+                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
+               return -1;
+       }
+
+       if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
+               fr_strerror_printf("Attribute %s already defined with different flags", da->name);
+               return -1;
+       }
+
+       tmpl_attr_set_da(vpt, da);
+       vpt->type = TMPL_TYPE_ATTR;
+
+       return 0;
+}
+
+#ifdef HAVE_REGEX
+/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
+ *
+ * Other regex types become noops.
+ */
+ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures, bool runtime)
+{
+       ssize_t slen;
+       char    *unescaped = vpt->data.unescaped;
+
+       if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0;    /* Don't need compiling */
+
+       fr_assert(tmpl_is_regex_uncompiled(vpt));
+
+       slen = regex_compile(vpt, &vpt->data.reg.ex,
+                            unescaped, talloc_array_length(unescaped) - 1,
+                            &vpt->data.reg.flags, subcaptures, runtime);
+       if (slen < 0) return slen;
+       talloc_free(unescaped);
+
+       vpt->type = TMPL_TYPE_REGEX;
+
+       TMPL_VERIFY(vpt);
+
+       return slen;
+}
+#endif
+/** @} */
+
+/** @name Print the contents of a #tmpl_t
+ * @{
+ */
+
+/** Print an attribute or list #tmpl_t to a string
+ *
+ * This function is the direct counterpart to #fr_tmpl_afrom_attr_substr.
+ *
+ * @param[in] out              Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt              to print.
+ * @param[in] ar_prefix                Whether to print the '&' at the beginning of attribute
+ *                             references.
+ *                             - TMPL_ATTR_REF_PREFIX_YES      - always print.
+ *                             - TMPL_ATTR_REF_PREFIX_NO       - never print.
+ *                             - TMPL_ATTR_REF_PREFIX_AUTO     - print if the original tmpl
+ *                                                               was prefixed.
+ * @return
+ *     - >0 the number of bytes written to the out buffer.
+ *     - 0 invalid argument.
+ *     - <0 the number of bytes we would have needed to complete the print.
+ */
+ssize_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix)
+{
+       tmpl_request_t          *rr = NULL;
+       tmpl_attr_t             *ar = NULL;
+       char                    printed_rr = false;
+       fr_sbuff_t              our_out = FR_SBUFF_NO_ADVANCE(out);
+
+       if (unlikely(!vpt)) return 0;
+
+       TMPL_VERIFY(vpt);
+
+       /*
+        *      Only print things we can print...
+        */
+       switch (vpt->type) {
+       case TMPL_TYPE_LIST:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+       case TMPL_TYPE_ATTR:
+               break;
+
+       default:
+               return 0;
+       }
+
+       /*
+        *      Handle printing the request reference
+        *      prefix.
+        */
+       if ((ar_prefix == TMPL_ATTR_REF_PREFIX_YES) ||
+           ((ar_prefix == TMPL_ATTR_REF_PREFIX_AUTO) && vpt->data.attribute.ref_prefix)) {
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, '&');
+       }
+
+       /*
+        *      Print request references
+        */
+       while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+               if (rr->request == REQUEST_CURRENT) continue;   /* Don't print the default request */
+
+               FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, request_ref_table, rr->request, "<INVALID>");
+               printed_rr = true;
+       }
+
+       /*
+        *      Print list
+        */
+       if (tmpl_list(vpt) != PAIR_LIST_REQUEST) {      /* Don't print the default list */
+               if (printed_rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+
+               FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, pair_list_table, tmpl_list(vpt), "<INVALID>");
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, vpt->data.attribute.old_list_sep ? ':' : '.');
+       } else if (printed_rr) {                        /* Request qualifier with no list qualifier */
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, vpt->data.attribute.old_list_sep ? ':' : '.');
+       }
+
+       /*
+        *
+        *      If the leaf attribute is unknown and raw we
+        *      add the .raw prefix.
+        *
+        *      If the leaf attribute is unknown and not raw
+        *      we add the .unknown prefix.
+        *
+        */
+       if (tmpl_contains_attr(vpt) && (ar = fr_dlist_tail(&vpt->data.attribute.ar))) {
+               switch (ar->type) {
+               case TMPL_ATTR_TYPE_NORMAL:
+               case TMPL_ATTR_TYPE_UNKNOWN:
+                       if (ar->ar_da->flags.is_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
+                       break;
+
+               case TMPL_ATTR_TYPE_UNRESOLVED:
+                       if (ar->ar_unresolved_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
+                       break;
+               }
+               ar = NULL;
+       }
+
+       /*
+        *      Print attribute identifiers
+        */
+       while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+               switch(ar->type) {
+               /*
+                *      For normal attributes we use the name
+                */
+               case TMPL_ATTR_TYPE_NORMAL:
+                       if (ar->ar_da->flags.is_raw) goto do_raw;
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_da->name);
+                       break;
+
+               /*
+                *      For unknown attributes we use the number
+                */
+               case TMPL_ATTR_TYPE_UNKNOWN:
+               do_raw:
+                       /*
+                        *      We need some context for unknown attributes
+                        *      so print the first known attribute.
+                        */
+                       if ((fr_dlist_head(&vpt->data.attribute.ar) == ar) &&
+                           ar->da->parent && !ar->da->parent->flags.is_root) {
+                               FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->da->parent->name);
+                               FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+                       }
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", ar->ar_da->attr);
+                       break;
+
+               /*
+                *      For unresolved attribute we print the raw identifier we
+                *      got when parsing the tmpl.
+                */
+               case TMPL_ATTR_TYPE_UNRESOLVED:
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
+                       break;
+               }
+
+               /*
+                *      Add array subscript.
+                *
+                *      Will later be complex filters.
+                */
+               switch (ar->ar_num) {
+               case NUM_ANY:
+                       break;
+
+               case NUM_ALL:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
+                       break;
+
+               case NUM_COUNT:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
+                       break;
+
+               case NUM_LAST:
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
+                       break;
+
+               default:
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
+                       break;
+               }
+
+               if (fr_dlist_next(&vpt->data.attribute.ar, ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+       }
+       return fr_sbuff_set(out, &our_out);
+}
+
+/** Print a #tmpl_t to a string
+ *
+ * This function should primarily be used for regenerating vpt->name when the contents
+ * of the #tmpl_t is changed programatically, or when the #tmpl_t is being serialized
+ * in some non-standard way, i.e. as a value for a field in a database.
+ *
+ * This function is the direct counterpart to #fr_tmpl_afrom_substr.
+ *
+ * @note Does not print flags for regular expressions, as the quoting char is needed
+ *      to separate the elements of the expression.
+ *      Call regex_flags_print to write the flags values to the output buffer.
+ *
+ * @param[out] out             Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt              to print.
+ * @param[in] ar_prefix                Whether to print the '&' at the beginning of attribute
+ *                             references.
+ *                             - TMPL_ATTR_REF_PREFIX_YES      - always print.
+ *                             - TMPL_ATTR_REF_PREFIX_NO       - never print.
+ *                             - TMPL_ATTR_REF_PREFIX_AUTO     - print if the original tmpl
+ *                                                               was prefixed.
+ * @param[in] e_rules          Escaping rules used to print strings.
  * @return
- *     - 1 noop (did nothing) - Not possible to convert tmpl.
- *     - 0 on success.
- *     - -1 on failure.
+ *     - >0 the number of bytes written to the out buffer.
+ *     - 0 invalid argument.
+ *     - <0 the number of bytes we would have needed to complete the print.
  */
-int tmpl_unknown_attr_add(tmpl_t *vpt)
+ssize_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt,
+                  tmpl_attr_ref_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules)
 {
-       fr_dict_attr_t const *da;
+       fr_sbuff_t      our_out = FR_SBUFF_NO_ADVANCE(out);
 
-       if (!vpt) return 1;
+       if (unlikely(!vpt)) return 0;
 
        TMPL_VERIFY(vpt);
 
-       if (!tmpl_is_attr(vpt)) return 1;
+       switch (vpt->type) {
+       case TMPL_TYPE_LIST:
+       case TMPL_TYPE_ATTR_UNRESOLVED:
+       case TMPL_TYPE_ATTR:
+               FR_SBUFF_RETURN(tmpl_attr_print, &our_out, vpt, ar_prefix);
+               break;
 
-       if (!tmpl_da(vpt)->flags.is_unknown) return 1;
+       case TMPL_TYPE_DATA:
+               FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
+               break;
 
-       da = fr_dict_unknown_add(fr_dict_unconst(fr_dict_internal()), tmpl_da(vpt));
-       if (!da) return -1;
-       tmpl_attr_set_leaf_da(vpt, da);
+       case TMPL_TYPE_REGEX:
+               FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len);     /* Fixme - double escapes */
+               break;
 
-       return 0;
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+               FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
+               break;
+
+       case TMPL_TYPE_UNINITIALISED:
+       case TMPL_TYPE_NULL:
+       case TMPL_TYPE_MAX:
+               break;
+
+       /*
+        *      The remaining types will either
+        *      be xlat expansions, or need
+        *      resolving, in which case the
+        *      unescaped string is available
+        *      in vpt->unescaped.
+        */
+       default:
+               if (tmpl_contains_xlat(vpt)) {
+                       FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
+                       break;
+               }
+
+               if (tmpl_needs_resolving(vpt)) {
+                       FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
+                       break;
+               }
+
+               fr_assert_fail("Can't print invalid tmpl type %s",
+                              fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
+               break;
+       }
+
+       return fr_sbuff_set(out, &our_out);
 }
 
-/** Add an undefined #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+/** Print a #tmpl_t to a string with quotes
  *
- * @note fr_dict_attr_add will not return an error if the attribute already exists
- *     meaning that multiple #tmpl_t specifying the same attribute can be
- *     passed to this function to be fixed up, so long as the type and flags
- *     are identical.
+ * This function should be used when the tmpl is embedded in some other construct
+ * in the server's configuration.
  *
- * @param[in] dict_def Default dictionary to use if none is
- *                     specified by the tmpl_attr_unresolved.
- * @param[in] vpt      specifying undefined attribute to add.
- *                     ``tmpl_da`` pointer will be updated to
- *                     point to the #fr_dict_attr_t inserted
- *                     into the dictionary. Lists and requests
- *                     will be preserved.
- * @param[in] type     to define undefined attribute as.
- * @param[in] flags    to define undefined attribute with.
+ * It adds standard quoting around tmpl's used as operands in expressions and applies
+ * the correct escaping rules.
+ *
+ * @param[out] out             Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt              to print.
+ * @param[in] ar_prefix                Whether to print the '&' at the beginning of attribute
+ *                             references.
+ *                             - TMPL_ATTR_REF_PREFIX_YES      - always print.
+ *                             - TMPL_ATTR_REF_PREFIX_NO       - never print.
+ *                             - TMPL_ATTR_REF_PREFIX_AUTO     - print if the original tmpl
+ *                                                               was prefixed.
  * @return
- *     - 1 noop (did nothing) - Not possible to convert tmpl.
- *     - 0 on success.
- *     - -1 on failure.
+ *     - >0 the number of bytes written to the out buffer.
+ *     - 0 invalid argument.
+ *     - <0 the number of bytes we would have needed to complete the print.
  */
-int tmpl_unresolved_attr_add(fr_dict_t *dict_def, tmpl_t *vpt,
-                              fr_type_t type, fr_dict_attr_flags_t const *flags)
+ssize_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix)
 {
-       fr_dict_attr_t const *da;
-
-       if (!vpt) return -1;
-
-       TMPL_VERIFY(vpt);
-
-       if (!tmpl_is_attr_unresolved(vpt)) return 1;
-
-       if (fr_dict_attr_add(dict_def, fr_dict_root(fr_dict_internal()), tmpl_attr_unresolved(vpt), -1, type, flags) < 0) {
-               return -1;
-       }
-       da = fr_dict_attr_by_name(dict_def, tmpl_attr_unresolved(vpt));
-       if (!da) return -1;
+       fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
 
-       if (type != da->type) {
-               fr_strerror_printf("Attribute %s of type %s already defined with type %s",
-                                  da->name, fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"),
-                                  fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
-               return -1;
-       }
+       char quote = fr_token_quote[vpt->quote];
 
-       if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
-               fr_strerror_printf("Attribute %s already defined with different flags", da->name);
-               return -1;
-       }
+       if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
+       FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
+                       ar_prefix, fr_value_escape_by_quote[vpt->quote]);
+       if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
 
-       tmpl_attr_set_da(vpt, da);
-       vpt->type = TMPL_TYPE_ATTR;
+       /*
+        *      Optionally print the flags
+        */
+       if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
 
-       return 0;
+       return fr_sbuff_set(out, &our_out);
 }
 /** @} */
 
@@ -2061,14 +3711,12 @@ int tmpl_unresolved_attr_add(fr_dict_t *dict_def, tmpl_t *vpt,
  *                             Must not be NULL.
  * @param[out] buff            Expansion buffer, may be NULL except for the following types:
  *                             - #TMPL_TYPE_EXEC
- *                             - #TMPL_TYPE_XLAT_UNRESOLVED
  *                             - #TMPL_TYPE_XLAT
  * @param[in] bufflen          Length of expansion buffer. Must be >= 2.
  * @param[in] request          Current request.
  * @param[in] vpt              to expand. Must be one of the following types:
  *                             - #TMPL_TYPE_UNRESOLVED
  *                             - #TMPL_TYPE_EXEC
- *                             - #TMPL_TYPE_XLAT_UNRESOLVED
  *                             - #TMPL_TYPE_XLAT
  *                             - #TMPL_TYPE_ATTR
  *                             - #TMPL_TYPE_DATA
@@ -2124,33 +3772,6 @@ ssize_t _tmpl_to_type(void *out,
        }
                break;
 
-       case TMPL_TYPE_XLAT_UNRESOLVED:
-       {
-               size_t len;
-
-               RDEBUG4("EXPAND TMPL XLAT");
-               if (!buff) {
-                       fr_strerror_printf("Missing expansion buffer for XLAT");
-                       return -1;
-               }
-               /* Error in expansion, this is distinct from zero length expansion */
-               slen = xlat_eval((char *)buff, bufflen, request, vpt->name, escape, escape_ctx);
-               if (slen < 0) return slen;
-
-               /*
-                *      Undo any of the escaping that was done by the
-                *      xlat expansion function.
-                *
-                *      @fixme We need a way of signalling xlat not to escape things.
-                */
-               len = fr_value_str_unescape(&FR_SBUFF_IN((char *)buff, slen),
-                                           &FR_SBUFF_IN((char *)buff, slen), SIZE_MAX, '"');
-               fr_assert(buff);
-               fr_value_box_bstrndup_shallow(&value_to_cast, NULL, (char *)buff, len, true);
-               src_type = FR_TYPE_STRING;
-       }
-               break;
-
        case TMPL_TYPE_XLAT:
        {
                size_t len;
@@ -2195,16 +3816,9 @@ ssize_t _tmpl_to_type(void *out,
                break;
 
        case TMPL_TYPE_DATA:
-       {
-               int ret;
-
                RDEBUG4("EXPAND TMPL DATA");
-               ret = tmpl_find_vp(&vp, request, vpt);
-               if (ret < 0) return -2;
-
                to_cast = tmpl_value(vpt);
                src_type = tmpl_value_type(vpt);
-       }
                break;
 
        /*
@@ -2213,9 +3827,13 @@ ssize_t _tmpl_to_type(void *out,
        case TMPL_TYPE_UNINITIALISED:
        case TMPL_TYPE_NULL:
        case TMPL_TYPE_LIST:
+       case TMPL_TYPE_EXEC_UNRESOLVED:
        case TMPL_TYPE_ATTR_UNRESOLVED:
+       case TMPL_TYPE_XLAT_UNRESOLVED:
        case TMPL_TYPE_REGEX:
-       case TMPL_TYPE_REGEX_UNRESOLVED:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
        case TMPL_TYPE_MAX:
                fr_assert(0);
                return -1;
@@ -2378,7 +3996,6 @@ ssize_t _tmpl_to_type(void *out,
  * @param vpt          to expand. Must be one of the following types:
  *                     - #TMPL_TYPE_UNRESOLVED
  *                     - #TMPL_TYPE_EXEC
- *                     - #TMPL_TYPE_XLAT_UNRESOLVED
  *                     - #TMPL_TYPE_XLAT
  *                     - #TMPL_TYPE_ATTR
  *                     - #TMPL_TYPE_DATA
@@ -2466,6 +4083,7 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
                break;
 
        case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_REGEX_XLAT:
        {
                fr_value_box_t  tmp;
                fr_type_t               src_type = FR_TYPE_STRING;
@@ -2544,8 +4162,10 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
        case TMPL_TYPE_UNINITIALISED:
        case TMPL_TYPE_NULL:
        case TMPL_TYPE_LIST:
+       case TMPL_TYPE_EXEC_UNRESOLVED:
        case TMPL_TYPE_REGEX:
-       case TMPL_TYPE_REGEX_UNRESOLVED:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
        case TMPL_TYPE_ATTR_UNRESOLVED:
        case TMPL_TYPE_MAX:
                fr_assert(0);
@@ -2610,228 +4230,6 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
        return from_cast.datum.length;
 }
 
-/** Print an attribute or list #tmpl_t to a string
- *
- * @note Does not print preceding '&'.
- *
- * @param[out] need    The number of bytes we'd need to write out the next part
- *                     of the template string.
- * @param[out] out     Where to write the presentation format #tmpl_t string.
- * @param[in] outlen   Size of output buffer.
- * @param[in] vpt      to print.
- * @return
- *     - The number of bytes written to the out buffer.
- *     - A number >= outlen if truncation has occurred.
- */
-size_t tmpl_snprint_attr_str(size_t *need, char *out, size_t outlen, tmpl_t const *vpt)
-{
-       char const      *p;
-       char            *out_p = out, *end = out_p + outlen;
-       size_t          len;
-
-       RETURN_IF_NO_SPACE_INIT(need, 1, out_p, out, end);
-
-       if (unlikely(!vpt)) {
-               *out = '\0';
-               return 0;
-       }
-
-       TMPL_VERIFY(vpt);
-
-       switch (vpt->type) {
-       case TMPL_TYPE_LIST:
-               /*
-                *      Don't add &current.
-                */
-               if (tmpl_request(vpt) == REQUEST_CURRENT) {
-                       len = snprintf(out_p, end - out_p, "%s:", fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""));
-                       RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-                       goto inst_and_tag;
-               }
-
-               len = snprintf(out_p, end - out_p, "%s.%s:", fr_table_str_by_value(request_ref_table, tmpl_request(vpt), ""),
-                              fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""));
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-               goto inst_and_tag;
-
-       case TMPL_TYPE_ATTR_UNRESOLVED:
-               p = tmpl_attr_unresolved(vpt);
-               goto print_name;
-
-       case TMPL_TYPE_ATTR:
-               p = tmpl_da(vpt)->name;
-
-       print_name:
-               /*
-                *      Don't add &current.
-                */
-               if (tmpl_request(vpt) == REQUEST_CURRENT) {
-                       if (tmpl_list(vpt) == PAIR_LIST_REQUEST) {
-                               len = strlcpy(out_p, p, end - out_p);
-                               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-                               goto inst_and_tag;
-                       }
-
-                       /*
-                        *      Don't add &request:
-                        */
-                       len = snprintf(out_p, end - out_p, "%s:%s",
-                                      fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""), p);
-                       RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-                       goto inst_and_tag;
-               }
-
-               len = snprintf(out_p, end - out_p, "%s.%s:%s",
-                              fr_table_str_by_value(request_ref_table, tmpl_request(vpt), ""),
-                              fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""), p);
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-
-       inst_and_tag:
-               if (TAG_VALID(tmpl_tag(vpt))) {
-                       len = snprintf(out_p, end - out_p, ":%d", tmpl_tag(vpt));
-                       RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-               }
-
-               switch (tmpl_num(vpt)) {
-               case NUM_ANY:
-                       len = 0;
-                       break;
-
-               case NUM_ALL:
-                       len = snprintf(out_p, end - out_p, "[*]");
-                       break;
-
-               case NUM_COUNT:
-                       len = snprintf(out_p, end - out_p, "[#]");
-                       break;
-
-               case NUM_LAST:
-                       len = snprintf(out_p, end - out_p, "[n]");
-                       break;
-
-               default:
-                       len = snprintf(out_p, end - out_p, "[%i]", tmpl_num(vpt));
-                       break;
-               }
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-               break;
-
-       default:
-               fr_assert_fail(NULL);
-               return 0;
-       }
-
-       *out_p = '\0';
-       return (out_p - out);
-}
-
-/** Print a #tmpl_t to a string
- *
- * @param[out] need    The number of bytes we'd need to write out the next part
- *                     of the template string.
- * @param[out] out     Where to write the presentation format #tmpl_t string.
- * @param[in] outlen   Size of output buffer.
- * @param[in] vpt      to print.
- * @return
- *     - The number of bytes written to the out buffer. If truncation has
- *     ocurred. *need will be > 0.
- */
-size_t tmpl_snprint(size_t *need, char *out, size_t outlen, tmpl_t const *vpt)
-{
-       size_t          len;
-       char const      *p;
-       char            c;
-       char            *out_p = out, *end = out_p + outlen;
-
-       RETURN_IF_NO_SPACE_INIT(need, 1, out_p, out, end);
-
-       if (!vpt) {
-empty:
-               *out = '\0';
-               return 0;
-       }
-       TMPL_VERIFY(vpt);
-
-       switch (vpt->type) {
-       case TMPL_TYPE_LIST:
-       case TMPL_TYPE_ATTR_UNRESOLVED:
-       case TMPL_TYPE_ATTR:
-               *out_p++ = '&';
-               return tmpl_snprint_attr_str(need, out_p, end - out_p, vpt) + 1;
-
-       /*
-        *      Regexes have their own set of escaping rules
-        */
-       case TMPL_TYPE_REGEX_UNRESOLVED:
-       case TMPL_TYPE_REGEX:
-               if ((end - out_p) <= 3) {       /* / + <c> + / + \0 */
-               no_space:
-                       if (out_p > end) out_p = end;   /* Safety */
-                       *out_p = '\0';
-                       return outlen + 1;
-               }
-
-               *out_p++ = '/';
-               len = fr_snprint(out_p, end - out_p, vpt->name, vpt->len, '\0');
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-
-               if ((end - out_p) <= 1) goto no_space;
-               *out_p++ = '/';
-
-               len = regex_flags_snprint(out_p, end - out_p, &tmpl_regex_flags(vpt));
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-
-               goto finish;
-
-       case TMPL_TYPE_XLAT_UNRESOLVED:
-       case TMPL_TYPE_XLAT:
-               c = '"';
-               goto do_literal;
-
-       case TMPL_TYPE_EXEC:
-               c = '`';
-               goto do_literal;
-
-       case TMPL_TYPE_UNRESOLVED:
-               /*
-                *      Nasty nasty hack that needs to be fixed.
-                *
-                *      Determines what quoting to use around strings based on their content.
-                *      Should use vpt->quote, but that's not always set correctly
-                *      at the moment.
-                */
-               for (p = vpt->name; *p != '\0'; p++) {
-                       if (*p == ' ') break;
-                       if (*p == '\'') break;
-                       if (*p == '.') continue;
-                       if (!fr_dict_attr_allowed_chars[(uint8_t) *p]) break;
-               }
-               c = *p ? '"' : '\0';
-
-do_literal:
-               if ((end - out_p) <= 3) goto no_space;  /* / + <c> + / + \0 */
-               if (c != '\0') *out_p++ = c;
-               len = fr_snprint(out_p, (end - out_p) - ((c == '\0') ? 0 : 1), vpt->name, vpt->len, c);
-               RETURN_IF_TRUNCATED(need, len, out_p, out, end - ((c == '\0') ? 0 : 1));
-
-               if ((end - out_p) <= 1) goto no_space;
-               if (c != '\0') *out_p++ = c;
-               break;
-
-       case TMPL_TYPE_DATA:
-               return fr_value_box_snprint(out, end - out_p, tmpl_value(vpt), fr_token_quote[vpt->quote]);
-
-       default:
-               goto empty;
-       }
-
-finish:
-       *out_p = '\0';
-       return (out_p - out);
-}
-
-#define TMPL_TAG_MATCH(_a, _t) ((_a->da == tmpl_da(_t)) && ATTR_TAG_MATCH(_a, tmpl_tag(_t)))
-
 static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
 {
        VALUE_PAIR      *c, *p, *fc = NULL, *fp = NULL;
@@ -2854,8 +4252,8 @@ static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
                case NUM_ALL:
                case NUM_COUNT:                         /* Iterator is called multiple time to get the count */
                        for (c = curr, p = *prev; c; p = c, c = c->next) {
-                               VP_VERIFY(c);
-                               if (TMPL_TAG_MATCH(c, vpt)) {
+                               VP_VERIFY(c);
+                               if (c->da == tmpl_da(vpt)) {
                                        *prev = p;
                                        return c;
                                }
@@ -2865,8 +4263,8 @@ static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
                case NUM_LAST:                          /* Get the last instance of a VALUE_PAIR */
                        for (c = curr, p = *prev; c; p = c, c = c->next) {
                                VP_VERIFY(c);
-                               if (TMPL_TAG_MATCH(c, vpt)) {
-                                       fp = p;
+                               if (c->da == tmpl_da(vpt)) {
+                                       fp = p;
                                        fc = c;
                                }
                        }
@@ -2879,7 +4277,7 @@ static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
                             c && (num >= 0);
                             p = c, c = c->next) {
                                VP_VERIFY(c);
-                               if (TMPL_TAG_MATCH(c, vpt)) {
+                               if (c->da == tmpl_da(vpt)) {
                                        fp = p;
                                        fc = c;
                                        num--;
@@ -2945,7 +4343,7 @@ static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
  *     - -3 if context could not be found (no parent #REQUEST available).
  * @param cursor to store iterator state.
  * @param request The current #REQUEST.
- * @param vpt specifying the #VALUE_PAIR type/tag or list to iterate over.
+ * @param vpt specifying the #VALUE_PAIR type or list to iterate over.
  * @return
  *     - First #VALUE_PAIR specified by the #tmpl_t.
  *     - NULL if no matching #VALUE_PAIR found, and NULL on error.
@@ -3001,7 +4399,7 @@ VALUE_PAIR *tmpl_cursor_init(int *err, fr_cursor_t *cursor, REQUEST *request, tm
  * @param ctx to allocate new #VALUE_PAIR in.
  * @param out Where to write the copied #VALUE_PAIR (s).
  * @param request The current #REQUEST.
- * @param vpt specifying the #VALUE_PAIR type/tag or list to copy.
+ * @param vpt specifying the #VALUE_PAIR type or list to copy.
  *     Must be one of the following types:
  *     - #TMPL_TYPE_LIST
  *     - #TMPL_TYPE_ATTR
@@ -3045,7 +4443,7 @@ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, tmpl_t co
  *
  * @param[out] out where to write the retrieved vp.
  * @param[in] request The current #REQUEST.
- * @param[in] vpt specifying the #VALUE_PAIR type/tag to find.
+ * @param[in] vpt specifying the #VALUE_PAIR type to find.
  *     Must be one of the following types:
  *     - #TMPL_TYPE_LIST
  *     - #TMPL_TYPE_ATTR
@@ -3074,7 +4472,7 @@ int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, tmpl_t const *vpt)
  *
  * @param[out] out where to write the retrieved or created vp.
  * @param[in] request The current #REQUEST.
- * @param[in] vpt specifying the #VALUE_PAIR type/tag to retrieve or create.  Must be #TMPL_TYPE_ATTR.
+ * @param[in] vpt specifying the #VALUE_PAIR type to retrieve or create.  Must be #TMPL_TYPE_ATTR.
  * @return
  *     - 1 on success a pair was created.
  *     - 0 on success a pair was found.
@@ -3126,7 +4524,7 @@ int tmpl_find_or_add_vp(VALUE_PAIR **out, REQUEST *request, tmpl_t const *vpt)
  *     - Pointer to the first non-zero byte.
  *     - NULL if all bytes were zero.
  */
-static uint8_t const *not_zeroed(uint8_t const *ptr, size_t len)
+static uint8_t const *is_zeroed(uint8_t const *ptr, size_t len)
 {
        size_t i;
 
@@ -3137,7 +4535,28 @@ static uint8_t const *not_zeroed(uint8_t const *ptr, size_t len)
        return NULL;
 }
 
-#define CHECK_ZEROED(_vpt, _field) not_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
+/** Verify that unused regions of the struct are zeroed out
+ *
+ */
+#define CHECK_ZEROED(_vpt, _field) is_zeroed(((uint8_t const *)&(_vpt)->data) + sizeof((_vpt)->data._field), sizeof((_vpt)->data) - sizeof((_vpt)->data._field))
+
+
+/** Print hex data
+ *
+ */
+#define PRINT_NON_ZEROED(_vpt, _field, _nz_ptr) \
+do { \
+       DEBUG("Expected live portion %p-%p (0-%zu)", \
+             _vpt, \
+             (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
+             sizeof((_vpt)->data._field)); \
+       DEBUG("Expected zero portion %p-%p (%zu-%zu)", \
+             (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data._field), \
+             (uint8_t const *)&(_vpt)->data + sizeof((_vpt)->data), \
+             sizeof((_vpt)->data._field), sizeof((_vpt)->data)); \
+       HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data), nz - (uint8_t const *)&vpt->data, "non-zero memory", ""); \
+} while (0)
+
 
 /** Verify the attribute reference in a tmpl_t make sense
  *
@@ -3170,15 +4589,15 @@ void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
                                    "CONSISTENCY CHECK FAILED %s[%u]:  Looping reference list found.  "
                                    "Fast pointer hit slow pointer at \"%s\"",
                                    file, line,
-                                   slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->unknown.name :
+                                   slow->type == TMPL_ATTR_TYPE_UNRESOLVED ? slow->ar_unresolved :
                                    slow->da ? slow->da->name : "(null-attr)");
        }
 
        /*
         *      Lineage type check
         *
-        *      Known attribute cannot come after unparsed or unknown attributes
-        *      Unknown attributes cannot come after unparsed attributes
+        *      Known attribute cannot come after unresolved or unknown attributes
+        *      Unknown attributes cannot come after unresolved attributes
         */
        while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
                switch (ar->type) {
@@ -3197,11 +4616,11 @@ void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
                                tmpl_attr_debug(vpt);
                                fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
                                                     "TMPL_TYPE_ATTR known attribute \"%s\" "
-                                                    "occurred after unparsed attribute \"%s\""
+                                                    "occurred after unresolved attribute \"%s\""
                                                     "in attr ref list",
                                                     file, line,
                                                     ar->da->name,
-                                                    ar->unknown.name);
+                                                    ar->ar_unresolved);
                        }
                        break;
 
@@ -3215,10 +4634,10 @@ void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt)
                                tmpl_attr_debug(vpt);
                                fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: "
                                                     "TMPL_TYPE_ATTR unknown attribute \"%s\" "
-                                                    "occurred after unparsed attribute "
+                                                    "occurred after unresolved attribute "
                                                     "in attr ref list",
                                                     file, line, ar->da->name,
-                                                    ar->unknown.name);
+                                                    ar->ar_unresolved);
                        }
                        break;
                }
@@ -3271,7 +4690,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
         */
        switch (vpt->type) {
        case TMPL_TYPE_NULL:
-               if ((nz = not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data)))) {
+               if ((nz = is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data)))) {
                        HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data),
                                    nz - (uint8_t const *)&vpt->data, "non-zero memory", "");
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
@@ -3280,22 +4699,35 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
                break;
 
        case TMPL_TYPE_UNRESOLVED:
-               if ((nz = not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data)))) {
-                       HEX_MARKER1((uint8_t const *)&vpt->data, sizeof(vpt->data),
-                                   nz - (uint8_t const *)&vpt->data, "non-zero memory", "");
-
+               if (!vpt->data.unescaped) {
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_UNRESOLVED "
-                                            "has non-zero bytes in its data union", file, line);
-               }
+                                            "unescaped field is NULL", file, line);
+                }
                break;
 
        case TMPL_TYPE_XLAT_UNRESOLVED:
+               if (!tmpl_xlat_flags(vpt)->needs_resolving) {
+                       fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
+                                            "does not have xlat_flags_t -> need_pass2 set", file, line);
+               }
+               if (!tmpl_xlat(vpt)) {
+                       fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
+                                            "has a NULL xlat.ex field", file, line);
+
+               }
+               break;
+
        case TMPL_TYPE_XLAT:
+               if (!vpt->data.xlat.ex) {
+                       fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
+                                            "has a NULL xlat.ex field", file, line);
+
+               }
                break;
 
 /* @todo When regexes get converted to xlat the flags field of the regex union is used
        case TMPL_TYPE_XLAT_UNRESOLVED:
-               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
+               if (is_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_UNRESOLVED "
                                             "has non-zero bytes in its data union", file, line);
                }
@@ -3310,6 +4742,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 */
 
        case TMPL_TYPE_EXEC:
+       case TMPL_TYPE_EXEC_UNRESOLVED:
                /* tmpl_xlat(vpt) can be initialized */
                break;
 
@@ -3326,8 +4759,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
        case TMPL_TYPE_ATTR:
                if ((nz = CHECK_ZEROED(vpt, attribute))) {
-                       HEX_MARKER1((uint8_t const *)&vpt->data.attribute, sizeof(vpt->data.attribute),
-                                   nz - (uint8_t const *)&vpt->data.attribute, "non-zero memory", "");
+                       PRINT_NON_ZEROED(vpt, attribute, nz);
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
                                             "has non-zero bytes after the data.attribute struct in the union",
                                             file, line);
@@ -3346,22 +4778,6 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
                        fr_dict_attr_t const    *da;
                        fr_dict_t const         *dict;
 
-                       if (!tmpl_da(vpt)->flags.has_tag &&
-                           (tmpl_tag(vpt) != TAG_NONE) && (tmpl_tag(vpt) != TAG_ANY)) {
-                               fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                                    "da is marked as not having a tag, but the template has a tag",
-                                                    file, line);
-                       }
-
-#if 0
-                       if (tmpl_da(vpt)->flags.has_tag &&
-                           !TAG_VALID_ZERO(tmpl_tag(vpt))) {
-                               fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                                    "da is marked as not having a tag, but the template has an invalid tag",
-                                                    file, line);
-                       }
-#endif
-
                        /*
                         *      Attribute may be present with multiple names
                         */
@@ -3375,7 +4791,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
                        da = fr_dict_attr_by_name(dict, tmpl_da(vpt)->name);
                        if (!da) {
-                               if (!tmpl_da(vpt)->flags.is_raw) {
+                               if (!tmpl_da(vpt)->flags.is_unknown) {
                                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
                                                             "attribute \"%s\" (%s) not found in dictionary (%s)",
                                                             file, line, tmpl_da(vpt)->name,
@@ -3396,7 +4812,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
                                }
                        }
 
-                       if (da != tmpl_da(vpt)) {
+                       if (!tmpl_da(vpt)->flags.is_unknown && !tmpl_da(vpt)->flags.is_raw && (da != tmpl_da(vpt))) {
                                fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
                                                     "dictionary pointer %p \"%s\" (%s) "
                                                     "and global dictionary pointer %p \"%s\" (%s) differ",
@@ -3419,8 +4835,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
        case TMPL_TYPE_LIST:
                if ((nz = CHECK_ZEROED(vpt, attribute))) {
-                       HEX_MARKER1((uint8_t const *)&vpt->data.attribute, sizeof(vpt->data.attribute),
-                                   nz - (uint8_t const *)&vpt->data.attribute, "non-zero memory", "");
+                       PRINT_NON_ZEROED(vpt, attribute, nz);
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST"
                                             "has non-zero bytes after the data.attribute struct in the union",
                                             file, line);
@@ -3438,8 +4853,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
        case TMPL_TYPE_DATA:
                if ((nz = CHECK_ZEROED(vpt, literal))) {
-                       HEX_MARKER1((uint8_t const *)&vpt->data.attribute, sizeof(vpt->data.attribute),
-                                   nz - (uint8_t const *)&vpt->data.attribute, "non-zero memory", "");
+                       PRINT_NON_ZEROED(vpt, literal, nz);
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
                                             "has non-zero bytes after the data.literal struct in the union",
                                             file, line);
@@ -3476,21 +4890,18 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
                break;
 
-       case TMPL_TYPE_REGEX_UNRESOLVED:
-#ifdef HAVE_REGEX
-               if (tmpl_preg(vpt) != NULL) {
-                       fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_UNRESOLVED "
-                                            "preg field was not NULL", file, line);
-               }
-#else
-               fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_UNRESOLVED - No regex support",
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
+#ifndef HAVE_REGEX
+               fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_XLAT_UNRESOLVED - No regex support",
                                     file, line);
 #endif
                break;
 
        case TMPL_TYPE_REGEX:
 #ifdef HAVE_REGEX
-               if (tmpl_preg(vpt) == NULL) {
+               if (tmpl_regex(vpt) == NULL) {
                        fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
                                             "comp field was NULL", file, line);
                }
@@ -3511,12 +4922,12 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt)
 
 #define return_P(_x) *error = _x;goto return_p
 
-/** Preparse a string in preparation for passing it to tmpl_afrom_str()
+/** Preparse a string in preparation for passing it to tmpl_afrom_substr()
  *
  *  Note that the input string is not modified, which means that the
- *  tmpl_afrom_str() function MUST un-escape it.
+ *  tmpl_afrom_substr() function MUST un-escape it.
  *
- *  The caller should pass 'out' and 'outlen' to tmpl_afrom_str()
+ *  The caller should pass 'out' and 'outlen' to tmpl_afrom_substr()
  *  as 'in' and 'inlen'.  The caller should also pass 'type'.
  *  The caller should also pass do_unescape=true.
  *
@@ -3934,8 +5345,8 @@ ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t i
 
 /** Return whether or not async is required for this tmpl.
  *
- *     If the tmpl is async_safe, then it will never yield.
- *     If the tmpl is not async_safe, then it may yield.
+ *     If the tmpl is needs_async, then it will never yield.
+ *     If the tmpl is not needs_async, then it may yield.
  *
  *     If the tmpl yields, then async is required.
  */
index 0b0dca32142618701654962e2ee153f68b4acb9a..18b6d4aad527e16798abbf1be8887fb857a87029 100644 (file)
  * where values or #VALUE_PAIR should be copied to.
  *
  * To create new #tmpl_t use one of the tmpl_*from_* functions.  These parse
- * strings into VPTs. The main parsing function is #tmpl_afrom_str, which can produce
+ * strings into VPTs. The main parsing function is #tmpl_afrom_substr, which can produce
  * most types of VPTs. It uses the type of quoting (passed as an #fr_token_t) to determine
  * what type of VPT to parse the string as. For example a #T_DOUBLE_QUOTED_STRING will
  * produce either a #TMPL_TYPE_XLAT_UNRESOLVED or a #TMPL_TYPE_UNRESOLVED (depending if the string
  * contained a non-literal expansion).
  *
- * @see tmpl_afrom_str
+ * @see tmpl_afrom_substr
  * @see tmpl_afrom_attr_str
  *
  * In the case of #TMPL_TYPE_ATTR and #TMPL_TYPE_LIST, there are special cursor overlay
@@ -105,42 +105,108 @@ typedef enum requests_ref_e {
 extern fr_table_num_sorted_t const request_ref_table[];
 extern size_t request_ref_table_len;
 
+/** Base data type is an attribute reference
+ *
+ */
+#define TMPL_FLAG_ATTR         0x01000000
+
+/** Base data type is an xlat expansion
+ *
+ */
+#define TMPL_FLAG_XLAT         0x02000000
+
+/** Is a type of regular expression
+ *
+ */
+#define TMPL_FLAG_REGEX                0x04000000
+
+/** Needs resolution
+ *
+ */
+#define TMPL_FLAG_UNRESOLVED   0x08000000
+
 /** Types of #tmpl_t
+ *
+ * Types may be compound types made up of multiple other types.
+ *
+ * Types which are used as part of compound types are:
+ * - XLAT_TYPE_XLAT            - #tmpl_t contains xlat expansion.
+ * - XLAT_TYPE_UNRESOLVED      - #tmpl_t contains unresolved elements such as xlat functions or other expansions.
  */
 typedef enum tmpl_type_e {
-       TMPL_TYPE_UNINITIALISED = 0,    //!< Uninitialised.
+       /** Uninitialised
+        */
+       TMPL_TYPE_UNINITIALISED         = 0x0000,
+
+       /** Has no value.  Usually a placeholder in a binary expression that's really a unary expression
+        */
+       TMPL_TYPE_NULL                  = 0x0001,
+
+       /** Value in native boxed format
+        */
+       TMPL_TYPE_DATA                  = 0x0002,
+
+       /** Reference to an attribute list
+        */
+       TMPL_TYPE_LIST                  = 0x0004 | TMPL_FLAG_ATTR,
 
-       TMPL_TYPE_NULL,                 //!< Has no value.  Usually a placeholder in a binary
-                                       ///< expression that's really a unary expression.
-       TMPL_TYPE_DATA,                 //!< Value in native boxed format.
+       /** Reference to one or more attributes
+        */
+       TMPL_TYPE_ATTR                  = 0x0008 | TMPL_FLAG_ATTR,
+
+       /** Pre-parsed xlat expansion
+        */
+       TMPL_TYPE_XLAT                  = 0x0010 | TMPL_FLAG_XLAT,
 
-       TMPL_TYPE_LIST,                 //!< Reference to an attribute list.
-       TMPL_TYPE_ATTR,                 //!< Reference to one or more attributes.
+       /** Callout to an external script or program
+        */
+       TMPL_TYPE_EXEC                  = 0x0020 | TMPL_FLAG_XLAT,
 
-       TMPL_TYPE_EXEC,                 //!< Callout to an external script or program.
-       TMPL_TYPE_XLAT,                 //!< Pre-parsed xlat expansion.
+       /** Compiled (and possibly JIT'd) regular expression
+        */
+       TMPL_TYPE_REGEX                 = 0x0040 | TMPL_FLAG_REGEX,
+
+       /** Regex where compilation is possible but hasn't been performed yet
+        */
+       TMPL_TYPE_REGEX_UNCOMPILED      = 0x0080 | TMPL_FLAG_REGEX,
 
-       TMPL_TYPE_REGEX,                //!< Compiled (and possibly JIT'd) regular expression.
+       /** A regex containing xlat expansions.  Cannot be pre-compiled
+        */
+       TMPL_TYPE_REGEX_XLAT            = 0x0100 | TMPL_FLAG_REGEX | TMPL_FLAG_XLAT,
 
-       /** @name Unparsed types
+       /** @name unresolved types
         *
         * These are tmpls which could not immediately be transformed into
-        * their "parsed" or "compiled" form due to missing references,
-        * or because the tmpl is "dynamic".
+        * their "resolved" form due to missing references or because
+        * additional parsing is required.
         *
         * @{
         */
-       TMPL_TYPE_UNRESOLVED,           //!< Unparsed literal string.  May be an intermediary phase
-                                       ///< where the tmpl is created as a temporary structure
-                                       ///< during parsing.
 
-       TMPL_TYPE_ATTR_UNRESOLVED,      //!< An attribute reference that we couldn't resolve.
-                                       ///< May be resolvable later once more attributes are
-                                       ///< defined.
+       /** Unparsed literal string
+        *
+        * May be an intermediary phase where the tmpl is created as a
+        * temporary structure during parsing.
+        */
+       TMPL_TYPE_UNRESOLVED            = 0x0200 | TMPL_FLAG_UNRESOLVED,
+
+       /** An attribute reference that we couldn't resolve but looked valid
+        *
+        * May be resolvable later once more attributes are defined.
+        */
+       TMPL_TYPE_ATTR_UNRESOLVED       = TMPL_TYPE_ATTR | TMPL_FLAG_UNRESOLVED,
+
+       /** An exec with unresolved xlat function or attribute references
+        */
+       TMPL_TYPE_EXEC_UNRESOLVED       = TMPL_TYPE_EXEC | TMPL_FLAG_UNRESOLVED,
 
-       TMPL_TYPE_XLAT_UNRESOLVED,      //!< Unparsed xlat expansion.  May have a dynamic element.
+       /** A xlat expansion with unresolved xlat functions or attribute references
+        */
+       TMPL_TYPE_XLAT_UNRESOLVED       = TMPL_TYPE_XLAT | TMPL_FLAG_UNRESOLVED,
 
-       TMPL_TYPE_REGEX_UNRESOLVED,     //!< Unparsed regular expression.  May have a dynamic element.
+       /** A regular expression with unresolved xlat functions or attribute references
+        */
+       TMPL_TYPE_REGEX_XLAT_UNRESOLVED = TMPL_TYPE_REGEX_XLAT | TMPL_FLAG_UNRESOLVED,
        /** @} */
 
        TMPL_TYPE_MAX                   //!< Marker for the last tmpl type.
@@ -148,29 +214,38 @@ typedef enum tmpl_type_e {
 
 /** Helpers to verify the type of #tmpl_t
  */
-#define tmpl_is_uninitialised(vpt)     (vpt->type == TMPL_TYPE_UNINITIALISED)
+#define tmpl_is_uninitialised(vpt)             (vpt->type == TMPL_TYPE_UNINITIALISED)
 
-#define tmpl_is_null(vpt)              (vpt->type == TMPL_TYPE_NULL)
-#define tmpl_is_data(vpt)              (vpt->type == TMPL_TYPE_DATA)
+#define tmpl_is_null(vpt)                      (vpt->type == TMPL_TYPE_NULL)
+#define tmpl_is_data(vpt)                      (vpt->type == TMPL_TYPE_DATA)
 
-#define tmpl_is_attr(vpt)              (vpt->type == TMPL_TYPE_ATTR)
-#define tmpl_is_list(vpt)              (vpt->type == TMPL_TYPE_LIST)
+#define tmpl_is_attr(vpt)                      (vpt->type == TMPL_TYPE_ATTR)
+#define tmpl_is_list(vpt)                      (vpt->type == TMPL_TYPE_LIST)
 
-#define tmpl_is_xlat(vpt)              (vpt->type == TMPL_TYPE_XLAT)
-#define tmpl_is_exec(vpt)              (vpt->type == TMPL_TYPE_EXEC)
+#define tmpl_is_xlat(vpt)                      (vpt->type == TMPL_TYPE_XLAT)
+#define tmpl_is_exec(vpt)                      (vpt->type == TMPL_TYPE_EXEC)
 
-#define tmpl_is_regex(vpt)             (vpt->type == TMPL_TYPE_REGEX)
+#define tmpl_is_regex(vpt)                     (vpt->type == TMPL_TYPE_REGEX)
+#define tmpl_is_regex_uncompiled(vpt)          (vpt->type == TMPL_TYPE_REGEX_UNCOMPILED)
+#define tmpl_is_regex_xlat(vpt)                (vpt->type == TMPL_TYPE_REGEX_XLAT)
 
 #define tmpl_is_unresolved(vpt)                (vpt->type == TMPL_TYPE_UNRESOLVED)
-#define tmpl_is_attr_unresolved(vpt)   (vpt->type == TMPL_TYPE_ATTR_UNRESOLVED)
-#define tmpl_is_xlat_unresolved(vpt)   (vpt->type == TMPL_TYPE_XLAT_UNRESOLVED)
-#define tmpl_is_regex_unresolved(vpt)  (vpt->type == TMPL_TYPE_REGEX_UNRESOLVED)
+#define tmpl_is_exec_unresolved(vpt)           (vpt->type == TMPL_TYPE_EXEC_UNRESOLVED)
+#define tmpl_is_attr_unresolved(vpt)           (vpt->type == TMPL_TYPE_ATTR_UNRESOLVED)
+#define tmpl_is_xlat_unresolved(vpt)           (vpt->type == TMPL_TYPE_XLAT_UNRESOLVED)
+#define tmpl_is_regex_xlat_unresolved(vpt)     (vpt->type == TMPL_TYPE_REGEX_XLAT_UNRESOLVED)
+
+#define tmpl_needs_resolving(vpt)              (vpt->type & TMPL_FLAG_UNRESOLVED)
+#define tmpl_contains_attr(vpt)                        (vpt->type & TMPL_FLAG_ATTR)
+#define tmpl_contains_regex(vpt)               (vpt->type & TMPL_FLAG_REGEX)
+#define tmpl_contains_xlat(vpt)                        (vpt->type & TMPL_FLAG_XLAT)
 
-extern fr_table_num_sorted_t const tmpl_type_table[];
+extern fr_table_num_ordered_t const tmpl_type_table[];
 extern size_t tmpl_type_table_len;
 
-typedef struct tmpl_s tmpl_t;
+
 typedef struct tmpl_rules_s tmpl_rules_t;
+typedef struct tmpl_s tmpl_t;
 
 #include <freeradius-devel/unlang/xlat.h>
 #include <freeradius-devel/util/packet.h>
@@ -193,7 +268,7 @@ typedef struct tmpl_rules_s tmpl_rules_t;
  */
 typedef enum {
        TMPL_ATTR_REF_PREFIX_YES = 0,                   //!< Attribute refs must have '&' prefix.
-       TMPL_ATTR_REF_PREFIX_NO,                                //!< Attribute refs have no '&' prefix.
+       TMPL_ATTR_REF_PREFIX_NO,                        //!< Attribute refs have no '&' prefix.
        TMPL_ATTR_REF_PREFIX_AUTO                       //!< Attribute refs may have a '&' prefix.
 } tmpl_attr_ref_prefix_t;
 
@@ -235,11 +310,13 @@ typedef enum {
                                                        ///< dictionary, or isn't a child of
                                                        ///< the previous ref.  May be resolved
                                                        ///< later.
-       TMPL_ATTR_TYPE_UNRESOLVED                               //!< We have a name, but nothing else
+       TMPL_ATTR_TYPE_UNRESOLVED                       //!< We have a name, but nothing else
                                                        ///< to identify the attribute.
                                                        ///< may be resolved later.
 } tmpl_attr_type_t;
 
+#define TMPL_MAX_REQUEST_REF_NESTING   20
+
 #define NUM_ANY                        INT16_MIN
 #define NUM_ALL                        (INT16_MIN + 1)
 #define NUM_COUNT              (INT16_MIN + 2)
@@ -255,13 +332,17 @@ typedef struct {
        fr_dict_attr_t const    * _CONST da;            //!< Resolved dictionary attribute.
 
        union {
-               fr_dict_attr_t          * _CONST da;            //!< Unknown dictionary attribute.
-               char                    * _CONST name;          //!< Undefined attr ref type.
-       } unknown;
+               struct {
+                       fr_dict_attr_t          * _CONST da;            //!< Unknown dictionary attribute.
+               } unknown;
 
-       int16_t                 _CONST num;             //!< For array references.
-       int8_t                  _CONST tag;             //!< For tag references.
+               struct {
+                       char                    * _CONST name;          //!< Undefined  ref type.
+                       bool                    is_raw;                 //!< User wants the leaf to be raw.
+               } unresolved;
+       };
 
+       int16_t                 _CONST num;             //!< For array references.
        tmpl_attr_type_t        _CONST type;
 } tmpl_attr_t;
 
@@ -279,11 +360,12 @@ typedef struct {
  *
  * @{
  */
+#define ar_type                                type
 #define ar_da                          da
 #define ar_unknown                     unknown.da
-#define ar_unresolved                  unknown.name
+#define ar_unresolved                  unresolved.name
+#define ar_unresolved_raw              unresolved.is_raw
 #define ar_num                         num
-#define ar_tag                         tag
 /** @} */
 
 /** A source or sink of value data.
@@ -312,22 +394,31 @@ typedef struct {
  * @see vp_map_t
  */
 struct tmpl_s {
-       tmpl_type_t     type;           //!< What type of value tmpl refers to.
+       tmpl_type_t     _CONST type;            //!< What type of value tmpl refers to.
 
        char const      * _CONST name;          //!< Raw string used to create the template.
+                                               ///< this string will have any escape sequences left intact.
        size_t          _CONST len;             //!< Length of the raw string used to create the template.
        fr_token_t      _CONST quote;           //!< What type of quoting was around the raw string.
 
        union {
+               char *unescaped;                //!< Unescaped form of the name, used for TMPL_TYPE_UNRESOLVED
+                                               ///< and TMPL_TYPE_REGEX_UNCOMPILED.
+
                _CONST struct {
-                       fr_dlist_head_t         rr;     //!< Request to search or insert in.
+                       bool                    ref_prefix;     //!< true if the reference was prefixed
+                                                               ///< with a '&'.
+
+                       fr_dlist_head_t         rr;             //!< Request to search or insert in.
 
-                       pair_list_t             list;   //!< List to search or insert in.
-                                                       ///< deprecated.
+                       bool                    old_list_sep;   //!< Print ':'
 
-                       fr_dlist_head_t         ar;     //!< Head of the attribute reference list.
+                       pair_list_t             list;           //!< List to search or insert in.
+                                                               ///< deprecated.
 
-                       bool                    was_oid;
+                       fr_dlist_head_t         ar;             //!< Head of the attribute reference list.
+
+                       bool                    was_oid;        //!< Was originally a numeric OID.
                } attribute;
 
                /*
@@ -335,17 +426,37 @@ struct tmpl_s {
                 */
                fr_value_box_t  literal;                         //!< Value data.
 
-               xlat_exp_t      *xlat;                          //!< pre-parsed xlat_exp_t
-
+               _CONST struct {
+                       xlat_exp_t              *ex;            //!< pre-parsed xlat_exp_t
+                       xlat_flags_t            flags;          //!< Flags controlling evaluation
+                                                               ///< and expansion.
+               } xlat;
 #ifdef HAVE_REGEX
-               struct {
-                       regex_t                 *preg;          //!< pre-parsed regex_t
-                       fr_regex_flags_t        regex_flags;    //!< Flags for regular expressions.
-               };
+               _CONST struct {
+                       regex_t                 *ex;            //!< pre-parsed regex_t
+                       fr_regex_flags_t        flags;          //!< Flags for regular expressions.
+               } reg;
 #endif
        } data;
+
+       fr_type_t       _CONST cast;
+       tmpl_rules_t    _CONST rules;
 };
 
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_unquoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_double_unquoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_single_unquoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_unquoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_unquoted;
+extern fr_sbuff_parse_rules_t const *tmpl_parse_rules_unquoted[T_TOKEN_LAST];
+
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_quoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_double_quoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_single_quoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_quoted;
+extern fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_quoted;
+extern fr_sbuff_parse_rules_t const *tmpl_parse_rules_quoted[T_TOKEN_LAST];
+
 /** Convenience macro for printing a meaningful assert message when we get a bad tmpl type
  */
 #define tmpl_assert_type(_cond) \
@@ -365,6 +476,18 @@ static inline request_ref_t tmpl_request(tmpl_t const *vpt)
        return ((tmpl_request_t *)fr_dlist_tail(&vpt->data.attribute.rr))->request;
 }
 
+/** The number of request references contained within a tmpl
+ *
+ */
+static inline size_t tmpl_request_ref_count(tmpl_t const *vpt)
+{
+       tmpl_assert_type(tmpl_is_attr(vpt) ||
+                        tmpl_is_attr_unresolved(vpt) ||
+                        tmpl_is_list(vpt));
+
+       return fr_dlist_num_elements(&vpt->data.attribute.rr);
+}
+
 static inline fr_dict_attr_t const *tmpl_da(tmpl_t const *vpt)
 {
        tmpl_assert_type(tmpl_is_attr(vpt));
@@ -386,22 +509,26 @@ static inline char const *tmpl_attr_unresolved(tmpl_t const *vpt)
        return ((tmpl_attr_t *)fr_dlist_tail(&vpt->data.attribute.ar))->ar_unresolved;
 }
 
-static inline int16_t tmpl_num(tmpl_t const *vpt)
+/** The number of attribute references contained within a tmpl
+ *
+ */
+static inline size_t tmpl_attr_ref_count(tmpl_t const *vpt)
 {
        tmpl_assert_type(tmpl_is_attr(vpt) ||
-                        tmpl_is_attr_unresolved(vpt) ||
-                        tmpl_is_list(vpt));
+                        tmpl_is_attr_unresolved(vpt));
 
-       return ((tmpl_attr_t *)fr_dlist_tail(&vpt->data.attribute.ar))->ar_num;
+       return fr_dlist_num_elements(&vpt->data.attribute.ar);
 }
 
-static inline int8_t tmpl_tag(tmpl_t const *vpt)
+static inline int16_t tmpl_num(tmpl_t const *vpt)
 {
        tmpl_assert_type(tmpl_is_attr(vpt) ||
-                        tmpl_is_attr_unresolved(vpt) ||                        /* Remove once tags are part of ar dlist */
+                        tmpl_is_attr_unresolved(vpt) ||
                         tmpl_is_list(vpt));
 
-       return ((tmpl_attr_t *)fr_dlist_tail(&vpt->data.attribute.ar))->ar_tag;
+       if (tmpl_is_list(vpt) && (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0)) return NUM_ALL;
+
+       return ((tmpl_attr_t *)fr_dlist_tail(&vpt->data.attribute.ar))->ar_num;
 }
 
 static inline pair_list_t tmpl_list(tmpl_t const *vpt)
@@ -418,16 +545,18 @@ static inline pair_list_t tmpl_list(tmpl_t const *vpt)
  *
  * @{
  */
-#define tmpl_xlat(_tmpl)               (_tmpl)->data.xlat
+#define tmpl_xlat(_tmpl)                       (_tmpl)->data.xlat.ex
+#define tmpl_xlat_flags(_tmpl)                 (&(_tmpl)->data.xlat.flags)
 /** @} */
 
 /** @name Field accessors for #TMPL_TYPE_DATA
  *
  * @{
  */
-#define tmpl_value(_tmpl)              (&(_tmpl)->data.literal)
-#define tmpl_value_length(_tmpl)       (_tmpl)->data.literal.datum.length
-#define tmpl_value_type(_tmpl)         (_tmpl)->data.literal.type
+#define tmpl_value(_tmpl)                      (&(_tmpl)->data.literal)
+#define tmpl_value_length(_tmpl)               (_tmpl)->data.literal.datum.length
+#define tmpl_value_type(_tmpl)                 (_tmpl)->data.literal.type
+#define tmpl_value_enumv(_tmpl)                        (_tmpl)->data.literal.enumv
 
 /*
  *     Temporary macros to track where we do assignments
@@ -436,13 +565,13 @@ static inline pair_list_t tmpl_list(tmpl_t const *vpt)
 #define tmpl_value_type_set(_tmpl, _type)      (_tmpl)->data.literal.type = (_type)
 /** @} */
 
-/** @name Field accessors for #TMPL_TYPE_REGEX and #TMPL_TYPE_REGEX_UNRESOLVED
+/** @name Field accessors for #TMPL_TYPE_REGEX and #TMPL_TYPE_REGEX_XLAT_UNRESOLVED
  *
  * @{
  */
 #ifdef HAVE_REGEX
-#  define tmpl_preg(_tmpl)             (_tmpl)->data.preg      //!< #TMPL_TYPE_REGEX only.
-#  define tmpl_regex_flags(_tmpl)      (_tmpl)->data.regex_flags
+#  define tmpl_regex(_tmpl)                    (_tmpl)->data.reg.ex            //!< #TMPL_TYPE_REGEX only.
+#  define tmpl_regex_flags(_tmpl)              (&(_tmpl)->data.reg.flags)
 #endif
 /** @} */
 
@@ -460,7 +589,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt);
  *
  * Example:
  @code{.c}
-   static tmpl_t list = tmpl_initialiser_list(CURRENT_REQUEST, PAIR_LIST_REQUEST);
+   static tmpl_t list = tmpl_init_initialiser_list(CURRENT_REQUEST, PAIR_LIST_REQUEST);
    fr_cursor_t cursor;
    VALUE_PAIR *vp;
 
@@ -477,7 +606,7 @@ void tmpl_verify(char const *file, int line, tmpl_t const *vpt);
  * @see tmpl_cursor_init
  * @see tmpl_cursor_next
  */
-#define        tmpl_initialiser_list(_request, _list)\
+#define        tmpl_init_initialiser_list(_request, _list)\
 { \
        .name = "static", \
        .len = sizeof("static"), \
@@ -535,7 +664,8 @@ typedef enum {
                                                        ///< found in the dictionaries, and is disallowed
                                                        ///< because 'disallow_internal' in tmpl_rules_t
                                                        ///< is trie.
-       ATTR_REF_ERROR_UNDEFINED_ATTRIBUTE_NOT_ALLOWED, //!< Attribute couldn't be found in the dictionaries.
+       ATTR_REF_ERROR_UNRESOLVED_ATTRIBUTE_NOT_ALLOWED,        //!< Attribute couldn't be found in the dictionaries.
+       ATTR_REF_ERROR_UNQUALIFIED_ATTRIBUTE_NOT_ALLOWED,//!< Attribute must be qualified to be used here.
        ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME,          //!< Attribute ref length is zero, or longer than
                                                        ///< the maximum.
        ATTR_REF_ERROR_INTERNAL_ATTRIBUTE_NOT_ALLOWED,  //!< Attribute resolved to an internal attribute
@@ -543,8 +673,9 @@ typedef enum {
        ATTR_REF_ERROR_FOREIGN_ATTRIBUTES_NOT_ALLOWED,  //!< Attribute resolved in a dictionary different
                                                        ///< to the one specified.
        ATTR_REF_ERROR_TAGGED_ATTRIBUTE_NOT_ALLOWED,    //!< Tagged attributes not allowed here.
-       ATTR_REF_ERROR_INVALID_TAG,                     //!< Invalid tag value.
-       ATTR_REF_ERROR_INVALID_ARRAY_INDEX              //!< Invalid array index.
+       ATTR_REF_ERROR_INVALID_ARRAY_INDEX,             //!< Invalid array index.
+       ATTR_REF_ERROR_NESTING_TOO_DEEP,                //!< Too many levels of nesting.
+       ATTR_REF_ERROR_MISSING_TERMINATOR               //!< Unexpected text found after attribute reference
 } attr_ref_error_t;
 
 /** Map ptr type to a boxed type
@@ -591,6 +722,8 @@ typedef enum {
 #define tmpl_aexpand_type(_ctx, _out, _type, _request, _vpt, _escape, _escape_ctx) \
                          _tmpl_to_atype(_ctx, (void *)(_out), _request, _vpt, _escape, _escape_ctx, _type)
 
+void                   tmpl_debug(tmpl_t const *vpt);
+
 VALUE_PAIR             **radius_list(REQUEST *request, pair_list_t list);
 
 RADIUS_PACKET          *radius_packet(REQUEST *request, pair_list_t list_name);
@@ -603,13 +736,20 @@ int                       radius_request(REQUEST **request, request_ref_t name);
 
 size_t                 radius_request_name(request_ref_t *out, char const *name, request_ref_t unknown);
 
-tmpl_t         *tmpl_init(tmpl_t *vpt, tmpl_type_t type,
-                                  char const *name, ssize_t len, fr_token_t quote);
+tmpl_t                 *tmpl_init_printf(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *fmt, ...);
 
-tmpl_t         *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name,
-                                   ssize_t len, fr_token_t quote);
+tmpl_t                 *tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type,
+                                          fr_token_t quote, char const *name, ssize_t len);
 
-void                   tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...);
+tmpl_t                 *tmpl_init(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len);
+
+tmpl_t                 *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len);
+
+void                   tmpl_set_name_printf(tmpl_t *vpt, fr_token_t quote, char const *fmt, ...);
+
+void                   tmpl_set_name_shallow(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len);
+
+void                   tmpl_set_name(tmpl_t *vpt, fr_token_t quote, char const *name, ssize_t len);
 
 int                    tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal);
 
@@ -617,14 +757,8 @@ void                       tmpl_attr_debug(tmpl_t const *vpt) CC_HINT(nonnull);
 
 int                    tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src) CC_HINT(nonnull);
 
-int                    tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type) CC_HINT(nonnull);
-
-void                   tmpl_attr_to_raw(tmpl_t *vpt) CC_HINT(nonnull);
-
 int                    tmpl_attr_set_da(tmpl_t *vpt, fr_dict_attr_t const *da) CC_HINT(nonnull);
 
-int                    tmpl_attr_resolve_unresolved(tmpl_t *vpt, tmpl_rules_t const *rules) CC_HINT(nonnull);
-
 void                   tmpl_attr_set_unresolved(tmpl_t *vpt, char const *name, size_t len) CC_HINT(nonnull);
 
 int                    tmpl_attr_set_leaf_da(tmpl_t *vpt, fr_dict_attr_t const *da) CC_HINT(nonnull);
@@ -635,31 +769,77 @@ void                      tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT
 
 void                   tmpl_attr_rewrite_num(tmpl_t *vpt, int16_t from, int16_t to) CC_HINT(nonnull);
 
-void                   tmpl_attr_set_leaf_tag(tmpl_t *vpt, int8_t tag) CC_HINT(nonnull);
-
 void                   tmpl_attr_set_request(tmpl_t *vpt, request_ref_t request) CC_HINT(nonnull);
 
 void                   tmpl_attr_set_list(tmpl_t *vpt, pair_list_t list) CC_HINT(nonnull);
 
 int                    tmpl_attr_afrom_list(TALLOC_CTX *ctx, tmpl_t **out, tmpl_t const *list,
-                                            fr_dict_attr_t const *da, int8_t tag);
+                                            fr_dict_attr_t const *da);
 
+/** @name Produce a #tmpl_t from a string or substring
+ *
+ * @{
+ */
 ssize_t                        tmpl_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
-                                              tmpl_t **out, char const *name, ssize_t name_len,
-                                              tmpl_rules_t const *rules);
+                                              tmpl_t **out, fr_sbuff_t *name,
+                                              fr_sbuff_parse_rules_t const *p_rules,
+                                              tmpl_rules_t const *ar_rules);
 
 ssize_t                        tmpl_afrom_attr_str(TALLOC_CTX *ctx, attr_ref_error_t *err,
                                            tmpl_t **out, char const *name,
                                            tmpl_rules_t const *rules) CC_HINT(nonnull (3, 4));
 
-ssize_t                        tmpl_afrom_str(TALLOC_CTX *ctx, tmpl_t **out, char const *name, size_t inlen,
-                                      fr_token_t type, tmpl_rules_t const *rules, bool do_escape);
+ssize_t                        tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
+                                         fr_sbuff_t *in,
+                                         fr_token_t quote,
+                                         fr_sbuff_parse_rules_t const *p_rules,
+                                         tmpl_rules_t const *ar_rules);
+
+ssize_t                        tmpl_cast_from_substr(fr_type_t *vpt, fr_sbuff_t *in);  /* Parses cast string */
+
+int                    tmpl_cast_set(tmpl_t *vpt, fr_type_t type);             /* Sets cast type */
+
+#ifdef HAVE_REGEX
+ssize_t                        tmpl_regex_flags_substr(tmpl_t *vpt, fr_sbuff_t *in,
+                                               fr_sbuff_term_t const *terminals);
+#endif
+/** @} */
 
+/** @name Change a #tmpl_t type, usually by casting or resolving a reference
+ * @{
+ */
 int                    tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv);
 
-size_t                 tmpl_snprint_attr_str(size_t *need, char *out, size_t outlen, tmpl_t const *vpt);
+int                    tmpl_resolve(tmpl_t *vpt) CC_HINT(nonnull);
+
+void                   tmpl_unresolve(tmpl_t *vpt) CC_HINT(nonnull);
+
+int                    tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p);
 
-size_t                 tmpl_snprint(size_t *need, char *out, size_t outlen, tmpl_t const *vpt);
+void                   tmpl_attr_to_raw(tmpl_t *vpt);
+
+int                    tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type);
+
+int                    tmpl_attr_unknown_add(tmpl_t *vpt);
+
+int                    tmpl_attr_unresolved_add(fr_dict_t *dict, tmpl_t *vpt,
+                                                fr_type_t type, fr_dict_attr_flags_t const *flags);
+
+#ifdef HAVE_REGEX
+ssize_t                        tmpl_regex_compile(tmpl_t *vpt, bool subcaptures, bool runtime);
+#endif
+/** @} */
+
+/** @name Print the contents of a #tmpl_t
+ * @{
+ */
+ssize_t                        tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix);
+
+ssize_t                        tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt,
+                                  tmpl_attr_ref_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules);
+
+ssize_t                        tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix);
+/** @} */
 
 ssize_t                        _tmpl_to_type(void *out,
                                      uint8_t *buff, size_t outlen,
@@ -686,10 +866,6 @@ int                        tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, tmpl_t const *vpt);
 
 int                    tmpl_find_or_add_vp(VALUE_PAIR **out, REQUEST *request, tmpl_t const *vpt);
 
-int                    tmpl_unknown_attr_add(tmpl_t *vpt);
-
-int                    tmpl_unresolved_attr_add(fr_dict_t *dict, tmpl_t *vpt,
-                                                  fr_type_t type, fr_dict_attr_flags_t const *flags);
 
 ssize_t                        tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen,
                                      fr_token_t *type, char const **error,
index b48ac44ab4e6644e77cd53806248ef4676a5395a..0f2330877d43ec6e151ccf3da59572f9d7e5240a 100644 (file)
@@ -89,14 +89,13 @@ ssize_t trigger_xlat(UNUSED TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
                return -1;
        }
 
-       vp = fr_pair_find_by_da(head, da, TAG_ANY);
+       vp = fr_pair_find_by_da(head, da);
        if (!vp) {
                ERROR("Attribute \"%s\" is not valid for this trigger", fmt);
                return -1;
        }
-       *out = fr_pair_value_asprint(request, vp, '\0');
 
-       return talloc_array_length(*out) - 1;
+       return fr_value_box_aprint(request, out, &vp->data, NULL);
 }
 
 static int _mutex_free(pthread_mutex_t *mutex)
@@ -407,7 +406,8 @@ int trigger_exec(REQUEST *request, CONF_SECTION const *cs, char const *name, boo
                fake->log.lvl = fr_debug_lvl;
        }
 
-       slen = xlat_tokenize_argv(ctx, &ctx->xlat, ctx->name, talloc_array_length(ctx->name) - 1, NULL);
+       slen = xlat_tokenize_argv(ctx, &ctx->xlat, NULL,
+                                 &FR_SBUFF_IN(ctx->name, talloc_array_length(ctx->name) - 1), NULL, NULL);
        if (slen <= 0) {
                char *spaces, *text;
 
index 7b4683963482717043bb73e7d14f75c94937575c..ee5003d37d69100bf3183b82d7f1809b6c31d580 100644 (file)
@@ -902,6 +902,26 @@ fr_dict_t const *virtual_server_namespace(char const *virtual_server)
        return dict->dict;
 }
 
+/** Return the namespace for a given virtual server specified by a CONF_ITEM within the virtual server
+ *
+ * @param[in] ci               to look for namespace in.
+ * @return
+ *     - NULL on error.
+ *     - Namespace on success.
+ */
+fr_dict_t const *virtual_server_namespace_by_ci(CONF_ITEM *ci)
+{
+       CONF_DATA const *cd;
+       virtual_server_dict_t *dict;
+
+       cd = cf_data_find_in_parent(ci, virtual_server_dict_t, "dictionary");
+       if (!cd) return NULL;
+
+       dict = (virtual_server_dict_t *) cf_data_value(cd);
+
+       return dict->dict;
+}
+
 /** Verify that a given virtual_server exists and is of a particular namespace
  *
  * Mostly used by modules to check virtual servers specified by their configs.
@@ -1069,7 +1089,7 @@ rlm_rcode_t virtual_server_process_auth(REQUEST *request, CONF_SECTION *virtual_
        CONF_SECTION    *auth_cs = NULL;
        char const      *auth_name;
 
-       vp = fr_pair_find_by_da(request->control, attr_auth_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_auth_type);
        if (!vp) {
                RDEBUG2("No &control:Auth-Type found");
        fail:
index 15564bdd4005f9693e3ded5414e9a418b333cb05..8cea0e7f6065f3abb0dc82eab8dfb39c85b983f0 100644 (file)
@@ -79,6 +79,8 @@ int           virtual_namespace_register(char const *namespace,
 
 fr_dict_t const        *virtual_server_namespace(char const *virtual_server);
 
+fr_dict_t const *virtual_server_namespace_by_ci(CONF_ITEM *ci);
+
 int            virtual_server_has_namespace(CONF_SECTION **out,
                                             char const *virtual_server, fr_dict_t const *namespace,
                                             CONF_ITEM *ci);
index 0060592befef06a3d6489ce6e6b24eb3bae94c11..33143382c1e14f97861bcf489e87431416472b48 100644 (file)
@@ -94,13 +94,13 @@ ssize_t             fr_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
 int            fr_sim_id_type(fr_sim_id_type_t *type, fr_sim_method_hint_t *hint,
                               char const *id, size_t id_len);
 
-int            fr_sim_id_3gpp_pseudonym_encrypt(char out[SIM_3GPP_PSEUDONYM_LEN + 1],
+int            fr_sim_id_3gpp_pseudonym_encrypt(char out[static SIM_3GPP_PSEUDONYM_LEN + 1],
                                                 char const *imsi, size_t imsi_len,
                                                 uint8_t tag,  uint8_t key_ind, uint8_t const key[16]);
 
-uint8_t                fr_sim_id_3gpp_pseudonym_tag(char const encr_id[SIM_3GPP_PSEUDONYM_LEN]);
+uint8_t                fr_sim_id_3gpp_pseudonym_tag(char const encr_id[static SIM_3GPP_PSEUDONYM_LEN]);
 
-uint8_t                fr_sim_id_3gpp_pseudonym_key_index(char const encr_id[SIM_3GPP_PSEUDONYM_LEN]);
+uint8_t                fr_sim_id_3gpp_pseudonym_key_index(char const encr_id[static SIM_3GPP_PSEUDONYM_LEN]);
 
 int            fr_sim_id_3gpp_pseudonym_decrypt(char out[SIM_IMSI_MAX_LEN],
-                                                char const encr_id[SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16]);
+                                                char const encr_id[static SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16]);
index ef3e7662293ac461b168df62560590658ff72c04..b5e3a07ddab3f00b424f191f297241ec72fcc174 100644 (file)
@@ -588,39 +588,39 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                        s = "security-updates";
                                        switch (hcstatus) {
                                        case 0xff0005:
-                                               fr_pair_value_asprintf(vp, "%s ok all-installed", s);
+                                               fr_pair_value_aprintf(vp, "%s ok all-installed", s);
                                                break;
 
                                        case 0xff0006:
-                                               fr_pair_value_asprintf(vp, "%s warn some-missing", s);
+                                               fr_pair_value_aprintf(vp, "%s warn some-missing", s);
                                                break;
 
                                        case 0xff0008:
-                                               fr_pair_value_asprintf(vp, "%s warn never-started", s);
+                                               fr_pair_value_aprintf(vp, "%s warn never-started", s);
                                                break;
 
                                        case 0xc0ff000c:
-                                               fr_pair_value_asprintf(vp, "%s error no-wsus-srv", s);
+                                               fr_pair_value_aprintf(vp, "%s error no-wsus-srv", s);
                                                break;
 
                                        case 0xc0ff000d:
-                                               fr_pair_value_asprintf(vp, "%s error no-wsus-clid", s);
+                                               fr_pair_value_aprintf(vp, "%s error no-wsus-clid", s);
                                                break;
 
                                        case 0xc0ff000e:
-                                               fr_pair_value_asprintf(vp, "%s warn wsus-disabled", s);
+                                               fr_pair_value_aprintf(vp, "%s warn wsus-disabled", s);
                                                break;
 
                                        case 0xc0ff000f:
-                                               fr_pair_value_asprintf(vp, "%s error comm-failure", s);
+                                               fr_pair_value_aprintf(vp, "%s error comm-failure", s);
                                                break;
 
                                        case 0xc0ff0010:
-                                               fr_pair_value_asprintf(vp, "%s warn needs-reboot", s);
+                                               fr_pair_value_aprintf(vp, "%s warn needs-reboot", s);
                                                break;
 
                                        default:
-                                               fr_pair_value_asprintf(vp, "%s error %08x", s, hcstatus);
+                                               fr_pair_value_aprintf(vp, "%s error %08x", s, hcstatus);
                                                break;
                                        }
                                        break;
@@ -630,35 +630,35 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                        s = "auto-updates";
                                        switch (hcstatus) {
                                        case 1:
-                                               fr_pair_value_asprintf(vp, "%s warn disabled", s);
+                                               fr_pair_value_aprintf(vp, "%s warn disabled", s);
                                                break;
 
                                        case 2:
-                                               fr_pair_value_asprintf(vp, "%s ok action=check-only", s);
+                                               fr_pair_value_aprintf(vp, "%s ok action=check-only", s);
                                                break;
 
                                        case 3:
-                                               fr_pair_value_asprintf(vp, "%s ok action=download", s);
+                                               fr_pair_value_aprintf(vp, "%s ok action=download", s);
                                                break;
 
                                        case 4:
-                                               fr_pair_value_asprintf(vp, "%s ok action=install", s);
+                                               fr_pair_value_aprintf(vp, "%s ok action=install", s);
                                                break;
 
                                        case 5:
-                                               fr_pair_value_asprintf(vp, "%s warn unconfigured", s);
+                                               fr_pair_value_aprintf(vp, "%s warn unconfigured", s);
                                                break;
 
                                        case 0xc0ff0003:
-                                               fr_pair_value_asprintf(vp, "%s warn service-down", s);
+                                               fr_pair_value_aprintf(vp, "%s warn service-down", s);
                                                break;
 
                                        case 0xc0ff0018:
-                                               fr_pair_value_asprintf(vp, "%s warn never-started", s);
+                                               fr_pair_value_aprintf(vp, "%s warn never-started", s);
                                                break;
 
                                        default:
-                                               fr_pair_value_asprintf(vp, "%s error %08x", s, hcstatus);
+                                               fr_pair_value_aprintf(vp, "%s error %08x", s, hcstatus);
                                                break;
                                        }
                                        break;
@@ -675,12 +675,12 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                                         */
                                                        t = clientstatus2str(hcstatus);
                                                        if (t) {
-                                                               fr_pair_value_asprintf(vp, "%s error %s", s, t);
+                                                               fr_pair_value_aprintf(vp, "%s error %s", s, t);
                                                        } else {
-                                                               fr_pair_value_asprintf(vp, "%s error %08x", s, hcstatus);
+                                                               fr_pair_value_aprintf(vp, "%s error %08x", s, hcstatus);
                                                        }
                                                } else {
-                                                       fr_pair_value_asprintf(vp,
+                                                       fr_pair_value_aprintf(vp,
                                                                        "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i",
                                                                        s,
                                                                        (hcstatus & 0x8) ? 1 : 0,
@@ -690,7 +690,7 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                                                        );
                                                }
                                        } else {
-                                               fr_pair_value_asprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
+                                               fr_pair_value_aprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
                                        }
                                        break;
                                }
@@ -698,7 +698,7 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                MEM(pair_update_request(&vp, attr_soh_ms_health_other) >= 0);
 
                                /* FIXME: what to do with the payload? */
-                               fr_pair_value_asprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
+                               fr_pair_value_aprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
                        }
                        break;
 
index 93ad0febae76ebd4ff75925b777af895e051773a..368baa18be3dd5cee4df167a3c06e31b3574e54d 100644 (file)
@@ -361,7 +361,7 @@ static SSL_SESSION *fr_tls_cache_read(SSL *ssl,
                return NULL;
        }
 
-       vp = fr_pair_find_by_da(request->state, attr_tls_session_data, TAG_ANY);
+       vp = fr_pair_find_by_da(request->state, attr_tls_session_data);
        if (!vp) {
                RWDEBUG("No cached session found");
                return NULL;
@@ -537,7 +537,7 @@ int fr_tls_cache_disable_cb(SSL *ssl,
         */
        if (!session->allow_session_resumption) goto disable;
 
-       vp = fr_pair_find_by_da(request->control, attr_allow_session_resumption, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_allow_session_resumption);
        if (vp && (vp->vp_uint32 == 0)) {
                RDEBUG2("&control:Allow-Session-Resumption == no, disabling session resumption");
        disable:
index b39845eb68e66281f104996f94085565e31b1c90..c97ca2a87e28a9f2fb72e64f5074b24823f25638 100644 (file)
@@ -377,7 +377,7 @@ int fr_tls_ocsp_check(REQUEST *request, SSL *ssl,
        /*
         *      Allow us to cache the OCSP verified state externally
         */
-       vp = fr_pair_find_by_da(request->control, attr_tls_ocsp_cert_valid, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_tls_ocsp_cert_valid);
        if (vp) switch (vp->vp_uint32) {
        case 0: /* no */
                RDEBUG2("Found &control:TLS-OCSP-Cert-Valid = no, forcing OCSP failure");
@@ -391,7 +391,7 @@ int fr_tls_ocsp_check(REQUEST *request, SSL *ssl,
                 *      we need to run the full OCSP check.
                 */
                if (staple_response) {
-                       vp = fr_pair_find_by_da(request->control, attr_tls_ocsp_response, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->control, attr_tls_ocsp_response);
                        if (!vp) {
                                RDEBUG2("No &control:TLS-OCSP-Response attribute found, performing full OCSP check");
                                break;
index 58903e36c38986d2cfe531215d9f59aa131dd71a..cd00fa7cc0513ba44a59574e38ac38328d6957a8 100644 (file)
@@ -1768,7 +1768,7 @@ fr_tls_session_t *fr_tls_session_init_server(TALLOC_CTX *ctx, fr_tls_conf_t *con
        /*
         *      Add the session certificate to the session.
         */
-       vp = fr_pair_find_by_da(request->control, attr_tls_session_cert_file, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_tls_session_cert_file);
        if (vp) {
                RDEBUG2("Loading TLS session certificate \"%pV\"", &vp->data);
 
@@ -1840,7 +1840,7 @@ fr_tls_session_t *fr_tls_session_init_server(TALLOC_CTX *ctx, fr_tls_conf_t *con
         *      just too much.
         */
        session->mtu = conf->fragment_size;
-       vp = fr_pair_find_by_da(request->packet->vps, attr_framed_mtu, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_framed_mtu);
        if (vp && (vp->vp_uint32 > 100) && (vp->vp_uint32 < session->mtu)) {
                RDEBUG2("Setting fragment_len to %u from &Framed-MTU", vp->vp_uint32);
                session->mtu = vp->vp_uint32;
index bc295c61e5fffe806b8b79595dfc979a6482e987..27529834218c267fed74dc26e794e440ad25ab2e 100644 (file)
@@ -90,7 +90,7 @@ int unlang_init(void)
        if (unlang_subrequest_op_init() < 0) return -1;
        unlang_switch_init();
        unlang_call_init();
-       unlang_tmpl_init();
+       unlang_tmpl_init_shallow();
 
        return 0;
 }
index c455145e57627913910ed0c2c6f29e9cad591c97..d6403d4cb74c53bc89df73e6576423cbe4c9ec17 100644 (file)
@@ -47,16 +47,16 @@ RCSID("$Id$")
 /* Here's where we recognize all of our keywords: first the rcodes, then the
  * actions */
 fr_table_num_sorted_t const mod_rcode_table[] = {
-       { L("..."),        RLM_MODULE_UNKNOWN      },
-       { L("disallow"),   RLM_MODULE_DISALLOW     },
-       { L("fail"),       RLM_MODULE_FAIL              },
-       { L("handled"),    RLM_MODULE_HANDLED      },
-       { L("invalid"),    RLM_MODULE_INVALID      },
-       { L("noop"),       RLM_MODULE_NOOP              },
-       { L("notfound"),   RLM_MODULE_NOTFOUND     },
-       { L("ok"),         RLM_MODULE_OK                },
-       { L("reject"),     RLM_MODULE_REJECT       },
-       { L("updated"),    RLM_MODULE_UPDATED      },
+       { L("..."),        RLM_MODULE_UNKNOWN   },
+       { L("disallow"),   RLM_MODULE_DISALLOW  },
+       { L("fail"),       RLM_MODULE_FAIL      },
+       { L("handled"),    RLM_MODULE_HANDLED   },
+       { L("invalid"),    RLM_MODULE_INVALID   },
+       { L("noop"),       RLM_MODULE_NOOP      },
+       { L("notfound"),   RLM_MODULE_NOTFOUND  },
+       { L("ok"),         RLM_MODULE_OK        },
+       { L("reject"),     RLM_MODULE_REJECT    },
+       { L("updated"),    RLM_MODULE_UPDATED   },
        { L("yield"),      RLM_MODULE_YIELD     }
 };
 size_t mod_rcode_table_len = NUM_ELEMENTS(mod_rcode_table);
@@ -151,186 +151,35 @@ default_actions[MOD_COUNT][RLM_MODULE_NUMCODES] =
        }
 };
 
-static bool pass2_fixup_xlat(CONF_ITEM const *ci, tmpl_t **pvpt, bool convert,
-                              fr_dict_attr_t const *da, tmpl_rules_t const *rules)
+static bool pass2_fixup_tmpl(TALLOC_CTX *ctx, CONF_ITEM const *ci, tmpl_t **vpt_p)
 {
-       ssize_t slen;
-       xlat_exp_t *head;
-       tmpl_t *vpt;
-
-       vpt = *pvpt;
-
-       fr_assert(tmpl_is_xlat_unresolved(vpt));
-
-       slen = xlat_tokenize(vpt, &head, vpt->name, talloc_array_length(vpt->name) - 1, rules);
-
-       if (slen < 0) {
-               char *spaces, *text;
+       tmpl_t *vpt = *vpt_p;
 
-               fr_canonicalize_error(vpt, &spaces, &text, slen, vpt->name);
-
-               cf_log_err(ci, "Failed parsing expansion string:");
-               cf_log_err(ci, "%s", text);
-               cf_log_err(ci, "%s^ %s", spaces, fr_strerror());
-
-               talloc_free(spaces);
-               talloc_free(text);
-               return false;
-       }
+       TMPL_VERIFY(vpt);
 
        /*
-        *      Convert %{Attribute-Name} to &Attribute-Name
+        *      Convert virtual &Attr-Foo to "%{Attr-Foo}"
         */
-       if (convert) {
-               tmpl_t *attr;
-
-               attr = xlat_to_tmpl_attr(talloc_parent(vpt), head);
-               if (attr) {
-                       /*
-                        *      If it's a virtual attribute, leave it
-                        *      alone.
-                        */
-                       if (tmpl_da(attr)->flags.virtual) {
-                               talloc_free(attr);
-                               return true;
-                       }
-
-                       /*
-                        *      If the attribute is of incompatible
-                        *      type, leave it alone.
-                        */
-                       if (da && (da->type != tmpl_da(attr)->type)) {
-                               talloc_free(attr);
-                               return true;
-                       }
-
-                       if (cf_item_is_pair(ci)) {
-                               CONF_PAIR *cp = cf_item_to_pair(ci);
-
-                               WARN("%s[%d]: Please change \"%%{%s}\" to &%s",
-                                      cf_filename(cp), cf_lineno(cp),
-                                      attr->name, attr->name);
-                       } else {
-                               CONF_SECTION *cs = cf_item_to_section(ci);
-
-                               WARN("%s[%d]: Please change \"%%{%s}\" to &%s",
-                                      cf_filename(cs), cf_lineno(cs),
-                                      attr->name, attr->name);
-                       }
-                       TALLOC_FREE(*pvpt);
-                       *pvpt = attr;
-                       return true;
-               }
+       if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
+               if (tmpl_attr_to_xlat(ctx, vpt_p) < 0) return false;
+               return true;
        }
 
        /*
-        *      Re-write it to be a pre-parsed XLAT structure.
-        */
-       vpt->type = TMPL_TYPE_XLAT;
-       tmpl_xlat(vpt) = head;
-
-       return true;
-}
-
-
-#ifdef HAVE_REGEX
-static bool pass2_fixup_regex(CONF_ITEM const *ci, tmpl_t *vpt, tmpl_rules_t const *rules)
-{
-       ssize_t slen;
-       regex_t *preg;
-
-       fr_assert(tmpl_is_regex_unresolved(vpt));
-
-       /*
-        *      It's a dynamic expansion.  We can't expand the string,
-        *      but we can pre-parse it as an xlat struct.  In that
-        *      case, we convert it to a pre-compiled XLAT.
-        *
-        *      This is a little more complicated than it needs to be
-        *      because cond_eval_map() keys off of the src
-        *      template type, instead of the operators.  And, the
-        *      pass2_fixup_xlat() function expects to get passed an
-        *      XLAT instead of a REGEX.
+        *      Fixup any other tmpl types
         */
-       if (strchr(vpt->name, '%')) {
-               vpt->type = TMPL_TYPE_XLAT_UNRESOLVED;
-               return pass2_fixup_xlat(ci, &vpt, false, NULL, rules);
-       }
-
-       slen = regex_compile(vpt, &preg, vpt->name, vpt->len, &tmpl_regex_flags(vpt), true, false);
-       if (slen <= 0) {
-               char *spaces, *text;
-
-               fr_canonicalize_error(vpt, &spaces, &text, slen, vpt->name);
-
-               cf_log_err(ci, "Invalid regular expression:");
-               cf_log_err(ci, "%s", text);
-               cf_log_perr(ci, "%s^", spaces);
-
-               talloc_free(spaces);
-               talloc_free(text);
-
+       if (tmpl_resolve(vpt) < 0) {
+               cf_log_perr(ci, NULL);
                return false;
        }
 
-       vpt->type = TMPL_TYPE_REGEX;
-       tmpl_preg(vpt) = preg;
-
        return true;
 }
-#endif
 
-static bool pass2_fixup_undefined(CONF_ITEM const *ci, tmpl_t *vpt, tmpl_rules_t const *rules)
-{
-       fr_assert(tmpl_is_attr_unresolved(vpt));
-
-       if (tmpl_attr_resolve_unresolved(vpt, rules) < 0) {
-               cf_log_perr(ci, "Failed resolving undefined attribute");
-               return false;
-       }
-
-       return true;
-}
-
-
-static bool pass2_fixup_tmpl(CONF_ITEM const *ci, tmpl_t **pvpt, tmpl_rules_t const *rules, bool convert)
-{
-       tmpl_t *vpt = *pvpt;
-
-       if (tmpl_is_xlat_unresolved(vpt)) {
-               return pass2_fixup_xlat(ci, pvpt, convert, NULL, rules);
-       }
-
-       /*
-        *      The existence check might have been &Foo-Bar,
-        *      where Foo-Bar is defined by a module.
-        */
-       if (tmpl_is_attr_unresolved(vpt)) {
-               return pass2_fixup_undefined(ci, vpt, rules);
-       }
-
-       /*
-        *      Convert virtual &Attr-Foo to "%{Attr-Foo}"
-        */
-       if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
-               tmpl_xlat(vpt) = xlat_from_tmpl_attr(vpt, vpt);
-               vpt->type = TMPL_TYPE_XLAT;
-       }
-
-
-#ifndef NDEBUG
-       if (tmpl_is_exec(vpt)) {
-               fr_assert(tmpl_xlat(vpt) != NULL);
-       }
-#endif
-
-       return true;
-}
-
-static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
+static bool pass2_fixup_map(fr_cond_t *c)
 {
        tmpl_t          *vpt;
-       vp_map_t                *map;
+       vp_map_t        *map;
 
        map = c->data.map;      /* shorter */
 
@@ -361,17 +210,17 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
                 *      Resolve the attribute references first
                 */
                if (tmpl_is_attr_unresolved(map->lhs)) {
-                       if (!pass2_fixup_undefined(map->ci, map->lhs, rules)) return false;
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) return false;
                        if (!cast) cast = tmpl_da(map->lhs);
                }
 
                if (tmpl_is_attr_unresolved(map->rhs)) {
-                       if (!pass2_fixup_undefined(map->ci, map->rhs, rules)) return false;
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) return false;
                        if (!cast) cast = tmpl_da(map->rhs);
                }
 
                /*
-                *      Then fixup the other side if it was unparsed
+                *      Then fixup the other side if it was unresolved
                 */
                if (tmpl_is_unresolved(map->lhs)) {
                        switch (cast->type) {
@@ -448,11 +297,11 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
                 *      @todo v3.1: allow anything anywhere.
                 */
                if (!tmpl_is_unresolved(map->rhs)) {
-                       if (!pass2_fixup_xlat(map->ci, &map->lhs, false, NULL, rules)) {
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) {
                                return false;
                        }
                } else {
-                       if (!pass2_fixup_xlat(map->ci, &map->lhs, true, NULL, rules)) {
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) {
                                return false;
                        }
 
@@ -525,25 +374,20 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
 
                        if (!c->cast) da = tmpl_da(map->lhs);
 
-                       if (!pass2_fixup_xlat(map->ci, &map->rhs, true, da, rules)) {
-                               return false;
-                       }
-
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) return false;
                } else {
-                       if (!pass2_fixup_xlat(map->ci, &map->rhs, false, NULL, rules)) {
-                               return false;
-                       }
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) return false;
                }
        }
 
-       if (tmpl_is_exec(map->lhs)) {
-               if (!pass2_fixup_tmpl(map->ci, &map->lhs, rules, false)) {
+       if (tmpl_is_exec_unresolved(map->lhs)) {
+               if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) {
                        return false;
                }
        }
 
-       if (tmpl_is_exec(map->rhs)) {
-               if (!pass2_fixup_tmpl(map->ci, &map->rhs, rules, false)) {
+       if (tmpl_is_exec_unresolved(map->rhs)) {
+               if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) {
                        return false;
                }
        }
@@ -557,8 +401,12 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
                ssize_t slen;
 
                fmt = talloc_typed_asprintf(map->lhs, "%%{%s}", map->lhs->name);
-               slen = tmpl_afrom_str(map, &vpt, fmt, talloc_array_length(fmt) - 1, T_DOUBLE_QUOTED_STRING,
-                                     &(tmpl_rules_t){ .allow_unknown = true }, true);
+               slen = tmpl_afrom_substr(map, &vpt, &FR_SBUFF_IN(fmt, talloc_array_length(fmt) - 1),
+                                        T_DOUBLE_QUOTED_STRING,
+                                        NULL,
+                                        &(tmpl_rules_t){
+                                               .allow_unknown = true
+                                        });
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -579,12 +427,12 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
        }
 
 #ifdef HAVE_REGEX
-       if (tmpl_is_regex_unresolved(map->rhs)) {
-               if (!pass2_fixup_regex(map->ci, map->rhs, rules)) {
+       if (tmpl_is_regex_xlat_unresolved(map->rhs)) {
+               if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) {
                        return false;
                }
        }
-       fr_assert(!tmpl_is_regex_unresolved(map->lhs));
+       fr_assert(!tmpl_is_regex_xlat_unresolved(map->lhs));
 #endif
 
        /*
@@ -595,9 +443,7 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
         */
        vpt = c->data.map->lhs;
        if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
-               if (!c->cast) c->cast = tmpl_da(vpt);
-               tmpl_xlat(vpt) = xlat_from_tmpl_attr(vpt, vpt);
-               vpt->type = TMPL_TYPE_XLAT;
+               if (tmpl_attr_to_xlat(c, &vpt) < 0) return false;
        }
 
        /*
@@ -617,13 +463,14 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
 
        if (!paircmp_find(tmpl_da(map->lhs))) return true;
 
-       if (tmpl_is_regex_unresolved(map->rhs)) {
+       if (tmpl_is_regex_xlat_unresolved(map->rhs)) {
                cf_log_err(map->ci, "Cannot compare virtual attribute %s via a regex", map->lhs->name);
                return false;
        }
 
        if (c->cast) {
-               cf_log_err(map->ci, "Cannot cast virtual attribute %s", map->lhs->name);
+               cf_log_err(map->ci, "Cannot cast virtual attribute %s to %s", map->lhs->name,
+                          fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "<INVALID>"));
                return false;
        }
 
@@ -641,16 +488,15 @@ static bool pass2_fixup_map(fr_cond_t *c, tmpl_rules_t const *rules)
        return true;
 }
 
-static bool pass2_cond_callback(fr_cond_t *c, void *uctx)
+static bool pass2_cond_callback(fr_cond_t *c, UNUSED void *uctx)
 {
-       unlang_compile_t        *unlang_ctx = uctx;
-
        switch (c->type) {
        /*
         *      These don't get optimized.
         */
        case COND_TYPE_TRUE:
        case COND_TYPE_FALSE:
+       case COND_TYPE_RCODE:
                return true;
 
        /*
@@ -663,14 +509,14 @@ static bool pass2_cond_callback(fr_cond_t *c, void *uctx)
         *      Fix up the template.
         */
        case COND_TYPE_EXISTS:
-               fr_assert(!tmpl_is_regex_unresolved(c->data.vpt));
-               return pass2_fixup_tmpl(c->ci, &c->data.vpt, unlang_ctx->rules, true);
+               fr_assert(!tmpl_is_regex_xlat_unresolved(c->data.vpt));
+               return pass2_fixup_tmpl(c, c->ci, &c->data.vpt);
 
        /*
         *      Fixup the map
         */
        case COND_TYPE_MAP:
-               return pass2_fixup_map(c, unlang_ctx->rules);
+               return pass2_fixup_map(c);
 
        /*
         *      Nothing else has pass2 fixups
@@ -690,13 +536,13 @@ static bool pass2_fixup_update_map(vp_map_t *map, tmpl_rules_t const *rules, fr_
                 *      FIXME: compile to attribute && handle
                 *      the conversion in map_to_vp().
                 */
-               if (!pass2_fixup_xlat(map->ci, &map->lhs, false, NULL, rules)) {
+               if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) {
                        return false;
                }
        }
 
        if (tmpl_is_exec(map->lhs)) {
-               if (!pass2_fixup_tmpl(map->ci, &map->lhs, rules, false)) {
+               if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) {
                        return false;
                }
        }
@@ -705,7 +551,7 @@ static bool pass2_fixup_update_map(vp_map_t *map, tmpl_rules_t const *rules, fr_
         *      Deal with undefined attributes now.
         */
        if (tmpl_is_attr_unresolved(map->lhs)) {
-               if (!pass2_fixup_undefined(map->ci, map->lhs, rules)) return false;
+               if (!pass2_fixup_tmpl(map, map->ci, &map->lhs)) return false;
        }
 
        /*
@@ -752,19 +598,19 @@ static bool pass2_fixup_update_map(vp_map_t *map, tmpl_rules_t const *rules, fr_
                         *      FIXME: compile to attribute && handle
                         *      the conversion in map_to_vp().
                         */
-                       if (!pass2_fixup_xlat(map->ci, &map->rhs, false, NULL, rules)) {
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) {
                                return false;
                        }
                }
 
-               fr_assert(!tmpl_is_regex_unresolved(map->rhs));
+               fr_assert(!tmpl_is_regex_xlat_unresolved(map->rhs));
 
                if (tmpl_is_attr_unresolved(map->rhs)) {
-                       if (!pass2_fixup_undefined(map->ci, map->rhs, rules)) return false;
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) return false;
                }
 
                if (tmpl_is_exec(map->rhs)) {
-                       if (!pass2_fixup_tmpl(map->ci, &map->rhs, rules, false)) {
+                       if (!pass2_fixup_tmpl(map, map->ci, &map->rhs)) {
                                return false;
                        }
                }
@@ -820,7 +666,7 @@ static bool pass2_fixup_map_rhs(unlang_group_t *g, tmpl_rules_t const *rules)
         */
        if (!g->vpt) return true;
 
-       return pass2_fixup_tmpl(g->map->ci, &g->vpt, rules, false);
+       return pass2_fixup_tmpl(g->map->ci, cf_section_to_item(g->cs), &g->vpt);
 }
 
 static void unlang_dump(unlang_t *instruction, int depth)
@@ -855,7 +701,7 @@ static void unlang_dump(unlang_t *instruction, int depth)
 
                        g = unlang_generic_to_group(c);
                        for (map = g->map; map != NULL; map = map->next) {
-                               map_snprint(NULL, buffer, sizeof(buffer), map);
+                               map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
                                DEBUG("%.*s%s", depth + 1, unlang_spaces, buffer);
                        }
 
@@ -1022,7 +868,7 @@ int unlang_fixup_update(vp_map_t *map, UNUSED void *ctx)
 
                TALLOC_FREE(map->rhs);
 
-               map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, NULL, 0, T_INVALID);
+               map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, T_INVALID, NULL, 0);
        }
 
        /*
@@ -1216,7 +1062,7 @@ static int unlang_fixup_filter(vp_map_t *map, UNUSED void *ctx)
 
                TALLOC_FREE(map->rhs);
 
-               map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, NULL, 0, T_INVALID);
+               map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, T_INVALID, NULL, 0);
        }
 
        /*
@@ -1431,8 +1277,11 @@ static unlang_t *compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CON
                /*
                 *      Try to parse the template.
                 */
-               slen = tmpl_afrom_str(cs, &vpt, tmpl_str, talloc_array_length(tmpl_str) - 1, type,
-                                     &parse_rules, true);
+               slen = tmpl_afrom_substr(cs, &vpt,
+                                        &FR_SBUFF_IN(tmpl_str, talloc_array_length(tmpl_str) - 1),
+                                        type,
+                                        NULL,
+                                        &parse_rules);
                if (slen < 0) {
                        cf_log_perr(cs, "Failed parsing map");
                        return NULL;
@@ -1444,9 +1293,11 @@ static unlang_t *compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CON
                switch (vpt->type) {
                case TMPL_TYPE_UNRESOLVED:
                case TMPL_TYPE_ATTR:
-               case TMPL_TYPE_XLAT_UNRESOLVED:
                case TMPL_TYPE_ATTR_UNRESOLVED:
+               case TMPL_TYPE_XLAT:
+               case TMPL_TYPE_XLAT_UNRESOLVED:
                case TMPL_TYPE_EXEC:
+               case TMPL_TYPE_EXEC_UNRESOLVED:
                        break;
 
                default:
@@ -2047,7 +1898,11 @@ static unlang_t *compile_switch(UNUSED unlang_t *parent, unlang_compile_t *unlan
         *      that the data types match.
         */
        type = cf_section_name2_quote(cs);
-       slen = tmpl_afrom_str(g, &g->vpt, name2, strlen(name2), type, &parse_rules, true);
+       slen = tmpl_afrom_substr(g, &g->vpt,
+                                &FR_SBUFF_IN(name2, strlen(name2)),
+                                type,
+                                NULL,
+                                &parse_rules);
        if (slen < 0) {
                char *spaces, *text;
 
@@ -2073,7 +1928,7 @@ static unlang_t *compile_switch(UNUSED unlang_t *parent, unlang_compile_t *unlan
         *      This is so that compile_case() can do attribute type
         *      checks / casts against us.
         */
-       if (!pass2_fixup_tmpl(cf_section_to_item(g->cs), &g->vpt, unlang_ctx->rules, true)) {
+       if (!pass2_fixup_tmpl(g, cf_section_to_item(cs), &g->vpt)) {
                talloc_free(g);
                return NULL;
        }
@@ -2179,7 +2034,11 @@ static unlang_t *compile_case(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
 
                type = cf_section_name2_quote(cs);
 
-               slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, &parse_rules, true);
+               slen = tmpl_afrom_substr(cs, &vpt,
+                                        &FR_SBUFF_IN(name2, strlen(name2)),
+                                        type,
+                                        NULL,
+                                        &parse_rules);
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -2196,7 +2055,7 @@ static unlang_t *compile_case(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
                }
 
                if (tmpl_is_attr_unresolved(vpt)) {
-                       if (!pass2_fixup_undefined(cf_section_to_item(cs), vpt, unlang_ctx->rules)) {
+                       if (!pass2_fixup_tmpl(parent, cf_section_to_item(cs), &vpt)) {
                                talloc_free(vpt);
                                return NULL;
                        }
@@ -2238,14 +2097,14 @@ static unlang_t *compile_case(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
                         *      Don't expand xlat's into an
                         *      attribute of a different type.
                         */
-                       if (!pass2_fixup_xlat(cf_section_to_item(cs), &vpt, true, da, unlang_ctx->rules)) {
+                       if (!pass2_fixup_tmpl(parent, cf_section_to_item(cs), &vpt)) {
                                talloc_free(vpt);
                                return NULL;
                        }
                }
 
                if (tmpl_is_exec(vpt)) {
-                       if (!pass2_fixup_tmpl(cf_section_to_item(cs), &vpt, unlang_ctx->rules, false)) {
+                       if (!pass2_fixup_tmpl(parent, cf_section_to_item(cs), &vpt)) {
                                talloc_free(vpt);
                                return NULL;
                        }
@@ -2311,7 +2170,11 @@ static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx,
         *      will fix it up.
         */
        type = cf_section_name2_quote(cs);
-       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, &parse_rules, true);
+       slen = tmpl_afrom_substr(cs, &vpt,
+                                &FR_SBUFF_IN(name2, strlen(name2)),
+                                type,
+                                NULL,
+                                &parse_rules);
        if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                char *spaces, *text;
 
@@ -2447,12 +2310,11 @@ static unlang_t *compile_return(unlang_t *parent, unlang_compile_t *unlang_ctx,
 static unlang_t *compile_tmpl(unlang_t *parent,
                              unlang_compile_t *unlang_ctx, CONF_PAIR *cp)
 {
-       unlang_t *c;
-       unlang_tmpl_t *ut;
-       ssize_t slen;
-       char const *p = cf_pair_attr(cp);
-       tmpl_t *vpt;
-       xlat_exp_t *head;
+       unlang_t        *c;
+       unlang_tmpl_t   *ut;
+       ssize_t         slen;
+       char const      *p = cf_pair_attr(cp);
+       tmpl_t          *vpt;
 
        ut = talloc_zero(parent, unlang_tmpl_t);
 
@@ -2463,15 +2325,14 @@ static unlang_t *compile_tmpl(unlang_t *parent,
        c->debug_name = c->name;
        c->type = UNLANG_TYPE_TMPL;
 
-       if (cf_pair_attr_quote(cp) == T_BACK_QUOTED_STRING) {
-               ut->inline_exec = true;
-       }
-
-       slen = tmpl_afrom_str(ut, &vpt, p, talloc_array_length(p) - 1, T_DOUBLE_QUOTED_STRING, unlang_ctx->rules, true);
+       slen = tmpl_afrom_substr(ut, &vpt,
+                                &FR_SBUFF_IN(p, talloc_array_length(p) - 1),
+                                cf_pair_attr_quote(cp),
+                                NULL,
+                                unlang_ctx->rules);
        if (slen <= 0) {
                char *spaces, *text;
 
-       error:
                fr_canonicalize_error(cp, &spaces, &text, slen, fr_strerror());
 
                cf_log_err(cp, "Syntax error");
@@ -2484,26 +2345,6 @@ static unlang_t *compile_tmpl(unlang_t *parent,
 
                return NULL;
        }
-
-       /*
-        *      Ensure that the expansions are precompiled.
-        */
-       if (ut->inline_exec) {
-               slen = xlat_tokenize_argv(vpt, &head, vpt->name, talloc_array_length(vpt->name) - 1, unlang_ctx->rules);
-       } else {
-               slen = xlat_tokenize(vpt, &head, vpt->name, talloc_array_length(vpt->name) - 1, unlang_ctx->rules);
-       }
-       if (slen <= 0) {
-               p = vpt->name;
-               goto error;
-       }
-
-       /*
-        *      Re-write it to be a pre-parsed XLAT structure.
-        */
-       vpt->type = TMPL_TYPE_XLAT;
-       tmpl_xlat(vpt) = head;
-
        ut->tmpl = vpt; /* const issues */
 
        compile_action_defaults(c, unlang_ctx);
@@ -2708,7 +2549,11 @@ static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compil
                 *      defined by now.
                 */
                type = cf_section_name2_quote(cs);
-               slen = tmpl_afrom_str(g, &g->vpt, name2, strlen(name2), type, &parse_rules, true);
+               slen = tmpl_afrom_substr(g, &g->vpt,
+                                        &FR_SBUFF_IN(name2, strlen(name2)),
+                                        type,
+                                        NULL,
+                                        &parse_rules);
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -2730,7 +2575,7 @@ static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compil
                /*
                 *      Fixup the templates
                 */
-               if (!pass2_fixup_tmpl(cf_section_to_item(g->cs), &g->vpt, unlang_ctx->rules, true)) {
+               if (!pass2_fixup_tmpl(g, cf_section_to_item(cs), &g->vpt)) {
                        talloc_free(g);
                        return NULL;
                }
@@ -3511,12 +3356,12 @@ int unlang_compile(CONF_SECTION *cs, rlm_components_t component, tmpl_rules_t co
 
        c = compile_section(NULL,
                            &(unlang_compile_t){
-                                   .component = component,
-                                           .section_name1 = cf_section_name1(cs),
-                                           .section_name2 = cf_section_name2(cs),
-                                           .actions = &default_actions[component],
-                                           .rules = rules
-                                           },
+                               .component = component,
+                               .section_name1 = cf_section_name1(cs),
+                               .section_name2 = cf_section_name2(cs),
+                               .actions = &default_actions[component],
+                               .rules = rules
+                           },
                            cs, UNLANG_TYPE_GROUP);
        if (!c) return -1;
 
index e84ef80cd20d700157414e4f7e16f02aafa84a2f..d393552a6b28d6b71f58f3863ca70c0849d28440 100644 (file)
@@ -212,8 +212,7 @@ static ssize_t unlang_foreach_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t ou
        pvp = (VALUE_PAIR **) request_data_reference(request, FOREACH_REQUEST_DATA, *(int const *) mod_inst);
        if (!pvp || !*pvp) return 0;
 
-       *out = fr_pair_value_asprint(ctx, *pvp, '\0');
-       return  talloc_array_length(*out) - 1;
+       return fr_value_box_aprint(ctx, out, &(*pvp)->data, NULL);
 }
 
 void unlang_foreach_init(void)
index 69828501e5ed61ce07f1ed9b48098261d5e483ff..bd2c2335971df55c12531bbb68612c67c74e50da 100644 (file)
@@ -154,8 +154,10 @@ static unlang_action_t list_mod_create(REQUEST *request, rlm_rcode_t *presult)
                                                 request, tmpl_xlat(map->lhs), false);
                                return UNLANG_ACTION_PUSHED_CHILD;
 
-                       case TMPL_TYPE_REGEX_UNRESOLVED:
+                       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
                        case TMPL_TYPE_REGEX:
+                       case TMPL_TYPE_REGEX_UNCOMPILED:
+                       case TMPL_TYPE_REGEX_XLAT:
                        case TMPL_TYPE_XLAT_UNRESOLVED:
                                fr_assert(0);
                        error:
@@ -189,8 +191,10 @@ static unlang_action_t list_mod_create(REQUEST *request, rlm_rcode_t *presult)
                                                 request, tmpl_xlat(map->rhs), false);
                                return UNLANG_ACTION_PUSHED_CHILD;
 
-                       case TMPL_TYPE_REGEX_UNRESOLVED:
                        case TMPL_TYPE_REGEX:
+                       case TMPL_TYPE_REGEX_UNCOMPILED:
+                       case TMPL_TYPE_REGEX_XLAT:
+                       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
                        case TMPL_TYPE_XLAT_UNRESOLVED:
                                fr_assert(0);
                                goto error;
@@ -339,8 +343,11 @@ static unlang_action_t unlang_map_state_init(REQUEST *request, rlm_rcode_t *pres
                                 request, tmpl_xlat(inst->src), false);
                return UNLANG_ACTION_PUSHED_CHILD;
 
-       case TMPL_TYPE_REGEX_UNRESOLVED:
+
        case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_REGEX_UNCOMPILED:
+       case TMPL_TYPE_REGEX_XLAT:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
        case TMPL_TYPE_XLAT_UNRESOLVED:
                fr_assert(0);
                goto error;
index 267732ab9e48a52570b486744544207acddfc805..23be11fa34f5b4c9e103ddc2e3ade83837e1e0b1 100644 (file)
@@ -277,7 +277,7 @@ int unlang_detached_child_init(REQUEST *request)
        /*
         *      Set Request Lifetime
         */
-       vp = fr_pair_find_by_da(request->control, attr_request_lifetime, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_request_lifetime);
        if (!vp || (vp->vp_uint32 > 0)) {
                fr_time_delta_t when = 0;
                const fr_event_timer_t **ev_p;
index df5839ecbb8ac794be9b8d72b13e17c487840c9f..85ff2c0c938eed6e962e12e7b929c01a2eb03fa1 100644 (file)
@@ -36,14 +36,14 @@ static unlang_action_t unlang_switch(REQUEST *request, UNUSED rlm_rcode_t *presu
        unlang_t                *this, *found, *null_case;
        unlang_group_t          *g, *h;
        fr_cond_t               cond;
-       fr_value_box_t          data;
        vp_map_t                map;
-       tmpl_t          vpt;
+       tmpl_t                  vpt;
 
        g = unlang_generic_to_group(instruction);
 
        memset(&cond, 0, sizeof(cond));
        memset(&map, 0, sizeof(map));
+       memset(&vpt, 0, sizeof(vpt));
 
        cond.type = COND_TYPE_MAP;
        cond.data.map = &map;
@@ -54,7 +54,6 @@ static unlang_action_t unlang_switch(REQUEST *request, UNUSED rlm_rcode_t *presu
        fr_assert(g->vpt != NULL);
 
        null_case = found = NULL;
-       data.datum.ptr = NULL;
 
        /*
         *      The attribute doesn't exist.  We can skip
@@ -88,8 +87,9 @@ static unlang_action_t unlang_switch(REQUEST *request, UNUSED rlm_rcode_t *presu
 
                len = tmpl_aexpand(request, &p, request, g->vpt, NULL, NULL);
                if (len < 0) goto find_null_case;
-               data.vb_strvalue = p;
-               tmpl_init(&vpt, TMPL_TYPE_UNRESOLVED, data.vb_strvalue, len, T_SINGLE_QUOTED_STRING);
+
+               tmpl_init_shallow(&vpt, TMPL_TYPE_DATA, T_SINGLE_QUOTED_STRING, p, len);
+               fr_value_box_bstrndup_shallow(&vpt.data.literal, NULL, p, len, false);
        }
 
        /*
@@ -148,8 +148,7 @@ static unlang_action_t unlang_switch(REQUEST *request, UNUSED rlm_rcode_t *presu
                        cond.cast = NULL;
                }
 
-               if (cond_eval_map(request, RLM_MODULE_UNKNOWN, 0,
-                                       &cond) == 1) {
+               if (cond_eval_map(request, 0, &cond) == 1) {
                        found = this;
                        break;
                }
@@ -158,7 +157,7 @@ static unlang_action_t unlang_switch(REQUEST *request, UNUSED rlm_rcode_t *presu
        if (!found) found = null_case;
 
 do_null_case:
-       talloc_free(data.datum.ptr);
+       if (vpt.type == TMPL_TYPE_DATA) fr_value_box_clear_value(&vpt.data.literal);
 
        /*
         *      Nothing found.  Just continue, and ignore the "switch"
index f72d89fae38358e4b7559aa16d90a4c5998090bb..c98201f85f9c6c4e6ad9f1a25a8189bf35cf3ba9 100644 (file)
@@ -526,7 +526,7 @@ static unlang_action_t unlang_tmpl(REQUEST *request, rlm_rcode_t *presult)
        }
 
        if (ut->tmpl->type == TMPL_TYPE_XLAT_UNRESOLVED) {
-               REDEBUG("Xlat expansions MUST be compiled before being run asynchronously");
+               REDEBUG("Xlat expansions MUST be fully resolved before being run asynchronously");
                *presult = RLM_MODULE_FAIL;
                return UNLANG_ACTION_CALCULATE_RESULT;
        }
@@ -536,36 +536,12 @@ static unlang_action_t unlang_tmpl(REQUEST *request, rlm_rcode_t *presult)
         */
        if (ut->tmpl->type != TMPL_TYPE_EXEC) {
                REDEBUG("Internal error - template '%s' should not require async", ut->tmpl->name);
-       fail:
                *presult = RLM_MODULE_FAIL;
                return UNLANG_ACTION_CALCULATE_RESULT;
        }
 
        xlat = tmpl_xlat(ut->tmpl);
-
-       /*
-        *      No pre-parsed xlat, do that now.
-        */
-       if (!xlat) {
-               ssize_t slen;
-               xlat_exp_t *head = NULL;
-
-               slen = xlat_tokenize_argv(state->ctx, &head, ut->tmpl->name, talloc_array_length(ut->tmpl->name) - 1, NULL);
-               if (slen <= 0) {
-                       char *spaces, *text;
-
-                       fr_canonicalize_error(state->ctx, &spaces, &text, slen, ut->tmpl->name);
-                       REDEBUG("Failed parsing expansion string:");
-                       REDEBUG("%s", text);
-                       RPEDEBUG("%s^", spaces);
-
-                       talloc_free(spaces);
-                       talloc_free(text);
-                       goto fail;
-               }
-
-               xlat = head;    /* const issues */
-       }
+       fr_assert(xlat);
 
        /*
         *      Expand the arguments to the program we're executing.
@@ -573,11 +549,12 @@ static unlang_action_t unlang_tmpl(REQUEST *request, rlm_rcode_t *presult)
        frame->interpret = unlang_tmpl_exec_wait_resume;
        repeatable_set(frame);
        unlang_xlat_push(state->ctx, &state->box, request, xlat, false);
+
        return UNLANG_ACTION_PUSHED_CHILD;
 }
 
 
-void unlang_tmpl_init(void)
+void unlang_tmpl_init_shallow(void)
 {
        unlang_register(UNLANG_TYPE_TMPL,
                           &(unlang_op_t){
index e76e5ebc08bd1d88376b00647518113725b7a1f8..712bd73c916f14f0802237860a50ace7c71ecd33 100644 (file)
@@ -173,6 +173,7 @@ typedef struct {
        unlang_t                self;
        tmpl_t const            *tmpl;
        bool                    inline_exec;
+       xlat_flags_t            flags;
 } unlang_tmpl_t;
 
 /** State of a redundant operation
@@ -379,7 +380,7 @@ void                unlang_subrequest_op_free(void);
 
 void           unlang_switch_init(void);
 
-void           unlang_tmpl_init(void);
+void           unlang_tmpl_init_shallow(void);
  /** @} */
 
 #ifdef __cplusplus
index 55aa7ab171be9498873fe5aaa30de1e014122f17..d29e712a6112f48776a89f9b3cf0b8cfb0f14432 100644 (file)
@@ -165,7 +165,7 @@ int unlang_xlat_event_timeout_add(REQUEST *request, fr_unlang_xlat_timeout_t cal
        ev->request = request;
        ev->fd = -1;
        ev->timeout = callback;
-       ev->inst = state->exp->inst;
+       ev->inst = state->exp->call.inst;
        ev->thread = xlat_thread_instance_find(state->exp);
        ev->ctx = ctx;
 
index 41b69b308dbeb520aa01d8c3f81e79f406d702fc..2dfa6c045af5d89f555cd5d6c5d9e5ddcbdf3558 100644 (file)
@@ -51,6 +51,7 @@ typedef struct xlat_exp xlat_exp_t;
 #include <freeradius-devel/util/time.h>
 #include <freeradius-devel/util/pair.h>
 #include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/sbuff.h>
 
 /** Instance data for an xlat expansion node
  *
@@ -73,6 +74,15 @@ struct xlat_thread_inst {
 
 typedef struct xlat_s xlat_t;
 
+/** Flags that control resolution and evaluation
+ *
+ */
+typedef struct {
+       bool                    needs_resolving;        //!< Needs pass2 resolution.
+       bool                    needs_async;    //!< Node and all child nodes are guaranteed to not
+                                               ///< require asynchronous expansion.
+} xlat_flags_t;
+
 extern fr_table_num_sorted_t const xlat_action_table[];
 extern size_t xlat_action_table_len;
 
@@ -282,15 +292,29 @@ int               xlat_eval_pair(REQUEST *request, VALUE_PAIR *vp);
 
 bool           xlat_async_required(xlat_exp_t const *xlat);
 
-ssize_t                xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, REQUEST *request,
-                                       char const *fmt, tmpl_rules_t const *rules);
+ssize_t                xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, REQUEST *request,
+                                       fr_sbuff_t *in,
+                                       fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules);
+
+ssize_t        xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                  fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules);
+
+ssize_t                xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                             fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules);
+
+ssize_t                xlat_print(fr_sbuff_t *in, xlat_exp_t const *node, fr_sbuff_escape_rules_t const *e_rules);
 
-ssize_t                xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, ssize_t inlen, tmpl_rules_t const *rules);
+static inline size_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_t const *node, fr_sbuff_escape_rules_t const *e_rules)
+               SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(xlat_print, node, e_rules);
 
-ssize_t                xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                  tmpl_rules_t const *rules);
+void           xlat_debug(xlat_exp_t const *node);
+
+bool           xlat_is_literal(xlat_exp_t const *head);
+
+bool           xlat_to_literal(TALLOC_CTX *ctx, char **str, xlat_exp_t **head);
+
+int            xlat_resolve(xlat_exp_t **head, xlat_flags_t *flags, bool allow_unresolved);
 
-size_t         xlat_snprint(char *buffer, size_t bufsize, xlat_exp_t const *node);
 
 #define XLAT_DEFAULT_BUF_LEN   2048
 
@@ -343,9 +367,11 @@ void               xlat_free(void);
 /*
  *     xlat_tokenize.c
  */
-tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *xlat);
+void           xlat_exp_free(xlat_exp_t **head);
 
-xlat_exp_t     *xlat_from_tmpl_attr(TALLOC_CTX *ctx, tmpl_t *vpt);
+tmpl_t         *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *xlat);
+
+int            xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, tmpl_t **vpt_p);
 
 /*
  *     xlat_inst.c
@@ -360,6 +386,8 @@ int         xlat_instantiate(void);
 
 void           xlat_thread_detach(void);
 
+int            xlat_bootstrap_func(xlat_exp_t *node);
+
 int            xlat_bootstrap(xlat_exp_t *root);
 
 void           xlat_instances_free(void);
index b8ae4ecb7351152369d0b43854359ac21d8ed7ec..cbd4a42dca3760b978aa1f852ebdf36b89114da3 100644 (file)
@@ -58,6 +58,10 @@ static rbtree_t *xlat_root = NULL;
 
 static char const hextab[] = "0123456789abcdef";
 
+static fr_sbuff_parse_rules_t const xlat_arg_parse_rules = {
+       .terminals = &FR_SBUFF_TERM(" ")
+};
+
 
 /** Return a VP from the specified request.
  *
@@ -307,7 +311,7 @@ int xlat_register_legacy(void *mod_inst, char const *name,
        c->mod_inst = mod_inst;
        c->instantiate = instantiate;
        c->inst_size = inst_size;
-       c->async_safe = true;
+       c->needs_async = false;
 
        DEBUG3("%s: %s", __FUNCTION__, c->name);
 
@@ -353,7 +357,7 @@ xlat_t const *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func,
                        return NULL;
                }
 
-               if ((c->type != XLAT_FUNC_NORMAL) || c->async_safe) {
+               if ((c->type != XLAT_FUNC_NORMAL) || (c->needs_async != needs_async)) {
                        ERROR("%s: Cannot change async capability of %s", __FUNCTION__, name);
                        return NULL;
                }
@@ -375,7 +379,7 @@ xlat_t const *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func,
 
        c->func.async = func;
        c->type = XLAT_FUNC_NORMAL;
-       c->async_safe = !needs_async;   /* this function may yield */
+       c->needs_async = needs_async;   /* this function may yield */
 
        DEBUG3("%s: %s", __FUNCTION__, c->name);
 
@@ -411,7 +415,7 @@ int xlat_internal(char const *name)
 
 /** Set global instantiation/detach callbacks
  *
- * All functions registered must be async_safe.
+ * All functions registered must be needs_async.
  *
  * @param[in] xlat             to set instantiation callbacks for.
  * @param[in] instantiate      Instantiation function. Called whenever a xlat is
@@ -442,7 +446,7 @@ void _xlat_async_instantiate_set(xlat_t const *xlat,
 
 /** Register an async xlat
  *
- * All functions registered must be async_safe.
+ * All functions registered must be needs_async.
  *
  * @param[in] xlat                     to set instantiation callbacks for.
  * @param[in] thread_instantiate       Instantiation function. Called for every compiled xlat
@@ -516,7 +520,7 @@ void xlat_unregister_module(void *instance)
  *     Internal redundant handler for xlats
  */
 typedef enum xlat_redundant_type_t {
-       XLAT_INVALID = 0,
+       XLAT_REDUNDANT_INVALID = 0,
        XLAT_REDUNDANT,
        XLAT_LOAD_BALANCE,
        XLAT_REDUNDANT_LOAD_BALANCE,
@@ -863,30 +867,31 @@ static ssize_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED char **out, U
                fr_table_num_ordered_t const    *type;
                size_t                          i;
 
-               if (vp->da->flags.has_tag) {
-                       RIDEBUG2("&%s:%s:%i %s %pV",
-                               fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
-                               vp->da->name,
-                               vp->tag,
-                               fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
-                               &vp->data);
-               } else {
-                       RIDEBUG2("&%s:%s %s %pV",
-                               fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
-                               vp->da->name,
-                               fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
-                               &vp->data);
-               }
+               RIDEBUG2("&%s:%s %s %pV",
+                       fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
+                       vp->da->name,
+                       fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
+                       &vp->data);
 
                if (!RDEBUG_ENABLED3) continue;
 
+               RIDEBUG3("da         : %p", vp->da);
+               RIDEBUG3("is_raw     : %pV", fr_box_bool(vp->da->flags.is_raw));
+               RIDEBUG3("is_unknown : %pV", fr_box_bool(vp->da->flags.is_unknown));
+
+               if (RDEBUG_ENABLED3) {
+                       RIDEBUG3("parent     : %s (%p)", vp->da->parent->name, vp->da->parent);
+               } else {
+                       RIDEBUG2("parent     : %s", vp->da->parent->name);
+               }
+               RIDEBUG3("attr       : %u", vp->da->attr);
                vendor = fr_dict_vendor_by_da(vp->da);
-               if (vendor) RIDEBUG2("Vendor : %i (%s)", vendor->pen, vendor->name);
-               RIDEBUG2("Type   : %s", fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
+               if (vendor) RIDEBUG2("vendor     : %i (%s)", vendor->pen, vendor->name);
+               RIDEBUG3("type       : %s", fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
 
                switch (vp->vp_type) {
                case FR_TYPE_VARIABLE_SIZE:
-                       RIDEBUG2("Length : %zu", vp->vp_length);
+                       RIDEBUG3("length     : %zu", vp->vp_length);
                        break;
 
                default:
@@ -921,7 +926,7 @@ static ssize_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED char **out, U
                        if ((pad = (11 - type->name.len)) < 0) pad = 0;
 
                        RINDENT();
-                       RDEBUG2("as %s%*s: %pV", type->name.str, pad, " ", dst);
+                       RDEBUG4("as %s%*s: %pV", type->name.str, pad, " ", dst);
                        REXDENT();
 
                next_type:
@@ -980,7 +985,10 @@ static ssize_t xlat_func_explode(TALLOC_CTX *ctx, char **out, size_t outlen,
         */
        fr_skip_whitespace(p);
 
-       slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt, p, -1, &(tmpl_rules_t){ .dict_def = request->dict });
+       slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt,
+                                     &FR_SBUFF_IN(p, strlen(p)),
+                                     &xlat_arg_parse_rules,
+                                     &(tmpl_rules_t){ .dict_def = request->dict });
        if (slen <= 0) {
                RPEDEBUG("Invalid input");
                return -1;
@@ -1037,8 +1045,6 @@ static ssize_t xlat_func_explode(TALLOC_CTX *ctx, char **out, size_t outlen,
                        }
 
                        MEM(nvp = fr_pair_afrom_da(talloc_parent(vp), vp->da));
-                       nvp->tag = vp->tag;
-
                        switch (vp->vp_type) {
                        case FR_TYPE_OCTETS:
                                MEM(fr_pair_value_memdup(nvp, (uint8_t const *)p, q - p, vp->vp_tainted) == 0);
@@ -1205,11 +1211,12 @@ static ssize_t xlat_func_integer(UNUSED TALLOC_CTX *ctx, char **out, size_t outl
  */
 static ssize_t parse_pad(tmpl_t **vpt_p, size_t *pad_len_p, char *pad_char_p, REQUEST *request, char const *fmt)
 {
-       ssize_t         slen;
-       unsigned long   pad_len;
-       char const      *p;
-       char            *end;
-       tmpl_t  *vpt;
+       ssize_t                 slen;
+       unsigned long           pad_len;
+       char const              *p;
+       char                    *end;
+       tmpl_t                  *vpt;
+
 
        *pad_char_p = ' ';              /* the default */
 
@@ -1223,7 +1230,10 @@ static ssize_t parse_pad(tmpl_t **vpt_p, size_t *pad_len_p, char *pad_char_p, RE
                return 0;
        }
 
-       slen = tmpl_afrom_attr_substr(request, NULL, &vpt, p, -1, &(tmpl_rules_t){ .dict_def = request->dict });
+       slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
+                                     &FR_SBUFF_IN(p, strlen(p)),
+                                     &xlat_arg_parse_rules,
+                                     &(tmpl_rules_t){ .dict_def = request->dict });
        if (slen <= 0) {
                RPEDEBUG("Failed parsing input string");
                return slen;
@@ -1352,12 +1362,12 @@ static ssize_t xlat_func_map(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        vp_map_t        *map = NULL;
        int             ret;
 
-       tmpl_rules_t parse_rules = {
+       tmpl_rules_t    attr_rules = {
                .dict_def = request->dict,
                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
        };
 
-       if (map_afrom_attr_str(request, &map, fmt, &parse_rules, &parse_rules) < 0) {
+       if (map_afrom_attr_str(request, &map, fmt, &attr_rules, &attr_rules) < 0) {
                RPEDEBUG("Failed parsing \"%s\" as map", fmt);
                return -1;
        }
@@ -1365,7 +1375,7 @@ static ssize_t xlat_func_map(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        switch (map->lhs->type) {
        case TMPL_TYPE_ATTR:
        case TMPL_TYPE_LIST:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
+       case TMPL_TYPE_XLAT:
                break;
 
        default:
@@ -1379,9 +1389,9 @@ static ssize_t xlat_func_map(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        case TMPL_TYPE_EXEC:
        case TMPL_TYPE_DATA:
        case TMPL_TYPE_LIST:
-       case TMPL_TYPE_REGEX_UNRESOLVED:
+       case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
        case TMPL_TYPE_UNRESOLVED:
-       case TMPL_TYPE_XLAT_UNRESOLVED:
+       case TMPL_TYPE_XLAT:
                break;
 
        default:
@@ -1567,9 +1577,8 @@ static ssize_t xlat_func_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
         *      If it's not a string, treat it as a literal
         */
        } else {
-               *out = fr_pair_value_asprint(ctx, vp, '\0');
+               slen = fr_value_box_aprint(ctx, out, &vp->data, NULL);
                if (!*out) return -1;
-               slen = talloc_array_length(*out) - 1;
        }
 
        REXDENT();
@@ -1708,7 +1717,7 @@ static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_cursor_t *out,
         */
        if (!*in) return XLAT_ACTION_DONE;
 
-       buff = fr_value_box_list_asprint(NULL, *in, NULL, '\0');
+       buff = fr_value_box_list_aprint(NULL, *in, NULL, NULL);
        if (!buff) return XLAT_ACTION_FAIL;
 
        len = talloc_array_length(buff) - 1;
@@ -1753,7 +1762,7 @@ finish:
  * Example:
 @verbatim
 "%{concat:, %{User-Name}%{Calling-Station-Id}" == "bob, aa:bb:cc:dd:ee:ff"
-"%{concat:, %{request:[*]}" == "<attr1value>, <attr2value>, <attr3value>, ..."
+"%{concat:, %{request[*]}" == "<attr1value>, <attr2value>, <attr3value>, ..."
 @endverbatim
  *
  * @ingroup xlat_functions
@@ -1791,7 +1800,7 @@ static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_cursor_t *out,
                return XLAT_ACTION_FAIL;
        }
 
-       buff = fr_value_box_list_asprint(result, (*in)->next, sep, '\0');
+       buff = fr_value_box_list_aprint(result, (*in)->next, sep, NULL);
        if (!buff) goto error;
 
        fr_value_box_bstrdup_buffer_shallow(NULL, result, NULL, buff, fr_value_box_list_tainted((*in)->next));
@@ -1826,7 +1835,7 @@ static xlat_action_t xlat_func_hex(TALLOC_CTX *ctx, fr_cursor_t *out,
        if (!*in) return XLAT_ACTION_DONE;
 
        /*
-        * Concatenate all input
+        *      Concatenate all input
         */
        if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_OCTETS, true) < 0) {
                RPEDEBUG("Failed concatenating input");
@@ -2194,7 +2203,7 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_cursor_t *out,
                MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL, false));
 
                vp->op = T_OP_EQ;
-               buff = fr_pair_asprint(vb, vp, '"');
+               fr_pair_aprint(vb, &buff, vp);
                vp->op = op;
 
                vb->vb_strvalue = buff;
@@ -2826,7 +2835,7 @@ static xlat_action_t xlat_func_sub_regex(TALLOC_CTX *ctx, fr_cursor_t *out,
 
        memset(&flags, 0, sizeof(flags));
 
-       slen = regex_flags_parse(NULL, &flags, p, q - p, true);
+       slen = regex_flags_parse(NULL, &flags, &FR_SBUFF_IN(p, q), NULL, true);
        if (slen < 0) {
                RPEDEBUG("Failed parsing regex flags");
                return XLAT_ACTION_FAIL;
@@ -3004,54 +3013,6 @@ static xlat_action_t xlat_func_sub(TALLOC_CTX *ctx, fr_cursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-
-/** Return the tag of an attribute reference
- *
- * Example (Tunnel-Server-Endpoint:1 = "192.0.2.1"):
-@verbatim
-"%{tag:Tunnel-Server-Endpoint}" == "1"
-@endverbatim
- *
- * @ingroup xlat_functions
- */
-static xlat_action_t xlat_func_tag(TALLOC_CTX *ctx, fr_cursor_t *out,
-                                  REQUEST *request, UNUSED void const *xlat_inst,
-                                  UNUSED void *xlat_thread_inst, fr_value_box_t **in)
-{
-       fr_value_box_t  *vb;
-       VALUE_PAIR      *vp;
-       fr_cursor_t     *cursor;
-
-       if (!*in) {
-               REDEBUG("Missing attribute reference");
-               return XLAT_ACTION_FAIL;
-       }
-
-       /*
-        *      Concatenate all input
-        */
-       if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
-               RPEDEBUG("Failed concatenating input");
-               return XLAT_ACTION_FAIL;
-       }
-
-       if (xlat_fmt_to_cursor(NULL, &cursor, NULL, request, (*in)->vb_strvalue) < 0) return XLAT_ACTION_FAIL;
-
-       for (vp = fr_cursor_head(cursor);
-            vp;
-            vp = fr_cursor_next(cursor)) {
-               if (!vp->da->flags.has_tag || !TAG_VALID(vp->tag)) continue;
-
-               MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_INT8, NULL, false));
-               vb->vb_int8 = vp->tag;
-               fr_cursor_append(out, vb);
-       }
-       talloc_free(cursor);
-
-       return XLAT_ACTION_DONE;
-}
-
-
 /** Change case of a string
  *
  * If upper is true, change to uppercase, otherwise, change to lowercase
@@ -3389,7 +3350,6 @@ int xlat_init(void)
        xlat_register(NULL, "string", xlat_func_string, false);
        xlat_register(NULL, "strlen", xlat_func_strlen, false);
        xlat_register(NULL, "sub", xlat_func_sub, false);
-       xlat_register(NULL, "tag", xlat_func_tag, false);
        xlat_register(NULL, "tolower", xlat_func_tolower, false);
        xlat_register(NULL, "toupper", xlat_func_toupper, false);
        xlat_register(NULL, "urlquote", xlat_func_urlquote, false);
index 9c3befa376ad3ee5ec25235614020ce373ca676e..950f5aa6871547ead5d8dbaf89bf7eca0c879b5a 100644 (file)
@@ -84,36 +84,16 @@ static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
 };
 
 fr_table_num_sorted_t const xlat_action_table[] = {
-       { L("done"),    XLAT_ACTION_DONE        },
-       { L("fail"),    XLAT_ACTION_FAIL        },
+       { L("done"),            XLAT_ACTION_DONE        },
+       { L("fail"),            XLAT_ACTION_FAIL        },
        { L("push-child"),      XLAT_ACTION_PUSH_CHILD  },
-       { L("yield"),   XLAT_ACTION_YIELD       }
+       { L("yield"),           XLAT_ACTION_YIELD       }
 };
 size_t xlat_action_table_len = NUM_ELEMENTS(xlat_action_table);
 
 static size_t xlat_process(TALLOC_CTX *ctx, char **out, REQUEST *request, xlat_exp_t const * const head,
                           xlat_escape_legacy_t escape, void  const *escape_ctx);
 
-/** Check to see if the expansion consists entirely of literal elements
- *
- * @param[in] exp      to check.
- * @return
- *     - true if expansion contains only literal elements.
- *     - false if expansion contains expandable elements.
- */
-static inline bool xlat_is_literal(xlat_exp_t const *exp)
-{
-       xlat_exp_t const *node;
-
-       for (node = exp;
-            node;
-            node = node->next) {
-               if (node->type != XLAT_LITERAL) return false;
-       }
-
-       return true;
-}
-
 /** Reconstruct the original expansion string from an xlat tree
  *
  * @param[in] ctx      to allocate result in.
@@ -126,7 +106,7 @@ static char *xlat_fmt_aprint(TALLOC_CTX *ctx, xlat_exp_t const *node)
 {
        switch (node->type) {
        case XLAT_LITERAL:
-       case XLAT_CHILD:
+       case XLAT_GROUP:
                return talloc_asprintf(ctx, "%s", node->fmt);
 
        case XLAT_ONE_LETTER:
@@ -136,7 +116,7 @@ static char *xlat_fmt_aprint(TALLOC_CTX *ctx, xlat_exp_t const *node)
                return talloc_asprintf(ctx, "%%{%s}", node->fmt);
 
        case XLAT_VIRTUAL:
-               return talloc_asprintf(ctx, "%%{%s}", node->xlat->name);;
+               return talloc_asprintf(ctx, "%%{%s}", node->call.func->name);;
 
        case XLAT_FUNC:
        {
@@ -144,9 +124,9 @@ static char *xlat_fmt_aprint(TALLOC_CTX *ctx, xlat_exp_t const *node)
                char                    *out, *n_out;
                TALLOC_CTX              *pool;
 
-               if (!child) return talloc_asprintf(ctx, "%%{%s:}", node->xlat->name);
+               if (!child) return talloc_asprintf(ctx, "%%{%s:}", node->call.func->name);
 
-               out = talloc_asprintf(ctx, "%%{%s:", node->xlat->name);
+               out = talloc_asprintf(ctx, "%%{%s:", node->call.func->name);
                pool = talloc_pool(NULL, 128);  /* Size of a single child (probably ok...) */
                do {
                        char *child_str;
@@ -213,7 +193,7 @@ static inline void xlat_debug_log_expansion(REQUEST *request, xlat_exp_t const *
         *      well as the original fmt string.
         */
        if ((node->type == XLAT_FUNC) && !xlat_is_literal(node->child)) {
-               RDEBUG2("      (%%{%s:%pM})", node->xlat->name, args);
+               RDEBUG2("      (%%{%s:%pM})", node->call.func->name, args);
        }
        talloc_free(str);
 }
@@ -687,7 +667,7 @@ static const char xlat_spaces[] = "
 void xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp,
                 REQUEST *request, void *rctx, fr_state_signal_t action)
 {
-       signal(request, exp->inst, xlat_thread_instance_find(exp)->data, rctx, action);
+       signal(request, exp->call.inst, xlat_thread_instance_find(exp)->data, rctx, action);
 }
 
 /** Call an xlat's resumption method
@@ -715,10 +695,10 @@ xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_cursor_t *out,
         *      and don't remove them from the list.
         */
        if (*result) (void) talloc_list_get_type_abort(*result, fr_value_box_t);
-       xa = resume(ctx, out, request, exp->inst, thread_inst->data, result, rctx);
+       xa = resume(ctx, out, request, exp->call.inst, thread_inst->data, result, rctx);
        if (*result) (void) talloc_list_get_type_abort(*result, fr_value_box_t);
 
-       RDEBUG2("EXPAND %%{%s:...}", exp->xlat->name);
+       RDEBUG2("EXPAND %%{%s:...}", exp->call.func->name);
        switch (xa) {
        default:
                break;
@@ -766,7 +746,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
 
        switch (node->type) {
        case XLAT_FUNC:
-               switch (node->xlat->type) {
+               switch (node->call.func->type) {
                case XLAT_FUNC_LEGACY:
                {
                        fr_value_box_t  *value;
@@ -776,23 +756,23 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
 
                        if (*result) {
                                (void) talloc_list_get_type_abort(*result, fr_value_box_t);
-                               result_str = fr_value_box_list_asprint(NULL, *result, NULL, '\0');
+                               result_str = fr_value_box_list_aprint(NULL, *result, NULL, NULL);
                                if (!result_str) return XLAT_ACTION_FAIL;
                        } else {
                                result_str = talloc_typed_strdup(NULL, "");
                        }
 
                        MEM(value = fr_value_box_alloc_null(ctx));
-                       if (node->xlat->buf_len > 0) {
-                               fr_value_box_bstr_alloc(value, &str, value, NULL, node->xlat->buf_len, false);
+                       if (node->call.func->buf_len > 0) {
+                               fr_value_box_bstr_alloc(value, &str, value, NULL, node->call.func->buf_len, false);
                        }
 
                        XLAT_DEBUG("** [%i] %s(func) - %%{%s:%pV}", unlang_interpret_stack_depth(request), __FUNCTION__,
                                   node->fmt,
                                   fr_box_strvalue_len(result_str, talloc_array_length(result_str) - 1));
 
-                       slen = node->xlat->func.sync(value, &str, node->xlat->buf_len,
-                                                    node->xlat->mod_inst, NULL, request, result_str);
+                       slen = node->call.func->func.sync(value, &str, node->call.func->buf_len,
+                                                    node->call.func->mod_inst, NULL, request, result_str);
                        xlat_debug_log_expansion(request, *in, *result);
                        if (slen < 0) {
                                talloc_free(value);
@@ -809,7 +789,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
                        /*
                         *      Shrink the buffer
                         */
-                       if (node->xlat->buf_len > 0) {
+                       if (node->call.func->buf_len > 0) {
                                if (slen > 0) fr_value_box_bstr_realloc(value, &str, value, slen);
                        } else {
                                fr_value_box_bstrdup_buffer_shallow(NULL, value, NULL, str, false);
@@ -839,7 +819,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
                        if (RDEBUG_ENABLED2) fr_value_box_list_acopy(NULL, &result_copy, *result);
 
                        if (*result) (void) talloc_list_get_type_abort(*result, fr_value_box_t);
-                       xa = node->xlat->func.async(ctx, out, request, node->inst->data, thread_inst->data, result);
+                       xa = node->call.func->func.async(ctx, out, request, node->call.inst->data, thread_inst->data, result);
                        if (*result) (void) talloc_list_get_type_abort(*result, fr_value_box_t);
 
                        if (RDEBUG_ENABLED2) {
@@ -911,7 +891,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
        }
                break;
 
-       case XLAT_CHILD:
+       case XLAT_GROUP:
        {
                fr_value_box_t  *value;
                fr_cursor_t from;
@@ -935,7 +915,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
 
                        if (*result) {
                                (void) talloc_list_get_type_abort(*result, fr_value_box_t);
-                               str = fr_value_box_list_asprint(value, *result, NULL, '\0');
+                               str = fr_value_box_list_aprint(value, *result, NULL, NULL);
                                if (!str) return XLAT_ACTION_FAIL;
                        } else {
                                str = talloc_typed_strdup(value, "");
@@ -1017,6 +997,15 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t cons
                case XLAT_LITERAL:
                        XLAT_DEBUG("** [%i] %s(literal) - %s", unlang_interpret_stack_depth(request), __FUNCTION__, node->fmt);
 
+                       /*
+                        *      Empty literals are only allowed if
+                        *      they're the only node in the expansion.
+                        *
+                        *      If they're found anywhere else the xlat
+                        *      parser has an error.
+                        */
+                       fr_assert(((node == *in) && !node->next) || (talloc_array_length(node->fmt) > 1));
+
                        /*
                         *      We unfortunately need to dup the buffer
                         *      because references aren't threadsafe.
@@ -1059,7 +1048,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t cons
 
                        xlat_debug_log_expansion(request, node, NULL);
                        MEM(value = fr_value_box_alloc_null(ctx));
-                       slen = node->xlat->func.sync(value, &str, node->xlat->buf_len, node->xlat->mod_inst,
+                       slen = node->call.func->func.sync(value, &str, node->call.func->buf_len, node->call.func->mod_inst,
                                                     NULL, request, NULL);
                        if (slen < 0) {
                                talloc_free(value);
@@ -1129,7 +1118,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t cons
                        xa = XLAT_ACTION_PUSH_CHILD;
                        goto finish;
 
-               case XLAT_CHILD:
+               case XLAT_GROUP:
                        XLAT_DEBUG("** [%i] %s(child) - %%{%s ...}", unlang_interpret_stack_depth(request), __FUNCTION__,
                                   node->fmt);
 
@@ -1140,7 +1129,17 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t cons
                        *child = node->child;
                        xa = XLAT_ACTION_PUSH_CHILD;
                        goto finish;
+
+               /*
+                *      Should have been fixed up during pass2
+                */
+               case XLAT_INVALID:
+               case XLAT_FUNC_UNRESOLVED:
+               case XLAT_VIRTUAL_UNRESOLVED:
+                       fr_assert(0);
+                       return XLAT_ACTION_FAIL;
                }
+
        }
 
 finish:
@@ -1150,8 +1149,8 @@ finish:
        return xa;
 }
 
-static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
-                        xlat_escape_legacy_t escape, void const *escape_ctx,
+static char *xlat_sync_eval(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
+                           xlat_escape_legacy_t escape, void const *escape_ctx,
 #ifndef DEBUG_XLAT
                         UNUSED
 #endif
@@ -1168,20 +1167,20 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
        XLAT_DEBUG("%.*sxlat aprint %d %s", lvl, xlat_spaces, node->type, node->fmt);
 
        switch (node->type) {
-               /*
-                *      Don't escape this.
-                */
+       /*
+        *      Don't escape this.
+        */
        case XLAT_LITERAL:
-               XLAT_DEBUG("%.*sxlat_aprint LITERAL", lvl, xlat_spaces);
+               XLAT_DEBUG("%.*sxlat_sync_eval LITERAL", lvl, xlat_spaces);
                return talloc_typed_strdup(ctx, node->fmt);
 
-       case XLAT_CHILD:
-               XLAT_DEBUG("%.*sxlat_aprint CHILD", lvl, xlat_spaces);
+       case XLAT_GROUP:
+               XLAT_DEBUG("%.*sxlat_sync_eval CHILD", lvl, xlat_spaces);
                return talloc_typed_strdup(ctx, node->fmt);
 
-               /*
-                *      Do a one-character expansion.
-                */
+       /*
+        *      Do a one-character expansion.
+        */
        case XLAT_ONE_LETTER:
                if (xlat_eval_one_letter(ctx, &cursor, request, node->fmt[0]) == XLAT_ACTION_FAIL) return NULL;
 
@@ -1200,7 +1199,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                break;
 
        case XLAT_ATTRIBUTE:
-               XLAT_DEBUG("xlat_aprint ATTR");
+               XLAT_DEBUG("xlat_sync_eval ATTR");
                if (xlat_eval_pair_real(ctx, &cursor, request, node->attr) == XLAT_ACTION_FAIL) return NULL;
 
                value = fr_cursor_head(&cursor);
@@ -1211,7 +1210,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                 *      cast to a string until we're actually doing
                 *      the concatenation.
                 */
-               str = fr_value_box_asprint(ctx, value, '"');
+               fr_value_box_aprint(ctx, &str, value, &fr_value_escape_double);
                if (!str) {
                attr_error:
                        RPERROR("Printing box to string failed");
@@ -1227,7 +1226,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                while ((value = fr_cursor_next(&cursor))) {
                        char *more;
 
-                       more = fr_value_box_asprint(ctx, value, '"');
+                       fr_value_box_aprint(ctx, &more, value, &fr_value_escape_double);
                        if (!more) goto attr_error;
                        str = talloc_strdup_append_buffer(str, ",");
                        str = talloc_strdup_append_buffer(str, more);
@@ -1237,30 +1236,30 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                break;
 
        case XLAT_VIRTUAL:
-               XLAT_DEBUG("xlat_aprint VIRTUAL");
+               XLAT_DEBUG("xlat_sync_eval VIRTUAL");
 
-               if (node->xlat->buf_len > 0) {
-                       str = talloc_array(ctx, char, node->xlat->buf_len);
+               if (node->call.func->buf_len > 0) {
+                       str = talloc_array(ctx, char, node->call.func->buf_len);
                        str[0] = '\0';  /* Be sure the string is \0 terminated */
                }
-               slen = node->xlat->func.sync(ctx, &str, node->xlat->buf_len, node->xlat->mod_inst, NULL, request, NULL);
+               slen = node->call.func->func.sync(ctx, &str, node->call.func->buf_len, node->call.func->mod_inst, NULL, request, NULL);
                if (slen < 0) {
                        talloc_free(str);
                        return NULL;
                }
-               RDEBUG2("EXPAND X %s", node->xlat->name);
+               RDEBUG2("EXPAND X %s", node->call.func->name);
                RDEBUG2("   --> %s", str);
                break;
 
        case XLAT_FUNC:
-               XLAT_DEBUG("xlat_aprint MODULE");
+               XLAT_DEBUG("xlat_sync_eval MODULE");
 
                /*
                 *      Temporary hack to use the new API.
                 *
                 *      Will not handle yields.
                 */
-               if (node->xlat->type == XLAT_FUNC_NORMAL) {
+               if (node->call.func->type == XLAT_FUNC_NORMAL) {
                        fr_value_box_t  *result = NULL;
                        TALLOC_CTX      *pool = talloc_new(NULL);
 
@@ -1288,7 +1287,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        }
 
                        if (result) {
-                               str = fr_value_box_list_asprint(ctx, result, NULL, '"');
+                               str = fr_value_box_list_aprint(ctx, result, NULL, &fr_value_escape_double);
                                if (!str) {
                                        RPEDEBUG("Failed concatenating xlat result string");
                                        talloc_free(pool);
@@ -1301,7 +1300,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        return str;
                } else if (node->child) {
                        if (xlat_process(ctx, &child, request,
-                                        node->child, node->xlat->escape, node->xlat->mod_inst) == 0) {
+                                        node->child, node->call.func->escape, node->call.func->mod_inst) == 0) {
                                return NULL;
                        }
 
@@ -1361,11 +1360,11 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        *q = '\0';
                }
 
-               if (node->xlat->buf_len > 0) {
-                       str = talloc_array(ctx, char, node->xlat->buf_len);
+               if (node->call.func->buf_len > 0) {
+                       str = talloc_array(ctx, char, node->call.func->buf_len);
                        str[0] = '\0';  /* Be sure the string is \0 terminated */
                }
-               slen = node->xlat->func.sync(ctx, &str, node->xlat->buf_len, node->xlat->mod_inst, NULL, request, child);
+               slen = node->call.func->func.sync(ctx, &str, node->call.func->buf_len, node->call.func->mod_inst, NULL, request, child);
                talloc_free(child);
                if (slen < 0) {
                        talloc_free(str);
@@ -1375,14 +1374,14 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
 
 #ifdef HAVE_REGEX
        case XLAT_REGEX:
-               XLAT_DEBUG("%.*sxlat_aprint REGEX", lvl, xlat_spaces);
+               XLAT_DEBUG("%.*sxlat_sync_eval REGEX", lvl, xlat_spaces);
                if (regex_request_to_sub(ctx, &str, request, node->regex_index) < 0) return NULL;
 
                break;
 #endif
 
        case XLAT_ALTERNATE:
-               XLAT_DEBUG("%.*sxlat_aprint ALTERNATE", lvl, xlat_spaces);
+               XLAT_DEBUG("%.*sxlat_sync_eval ALTERNATE", lvl, xlat_spaces);
                fr_assert(node->child != NULL);
                fr_assert(node->alternate != NULL);
 
@@ -1398,6 +1397,15 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        XLAT_DEBUG("%.*sALTERNATE got alternate string %s", lvl, xlat_spaces, str);
                }
                break;
+
+       /*
+        *      Should have been caught by pass2
+        */
+       case XLAT_FUNC_UNRESOLVED:
+       case XLAT_VIRTUAL_UNRESOLVED:
+       case XLAT_INVALID:
+               fr_assert(0);
+               return NULL;
        }
 
        /*
@@ -1460,7 +1468,7 @@ static size_t xlat_process(TALLOC_CTX *ctx, char **out, REQUEST *request, xlat_e
                 *      calls will call node-specific escape
                 *      functions.
                 */
-               answer = xlat_aprint(ctx, request, head, escape, escape_ctx, 0);
+               answer = xlat_sync_eval(ctx, request, head, escape, escape_ctx, 0);
                if (!answer) {
                        *out = talloc_zero_array(ctx, char, 1);
                        return 0;
@@ -1478,7 +1486,7 @@ static size_t xlat_process(TALLOC_CTX *ctx, char **out, REQUEST *request, xlat_e
        if (!array) return -1;
 
        for (node = head, i = 0; node != NULL; node = node->next, i++) {
-               array[i] = xlat_aprint(array, request, node, escape, escape_ctx, 0); /* may be NULL */
+               array[i] = xlat_sync_eval(array, request, node, escape, escape_ctx, 0); /* may be NULL */
 
                /*
                 *      Nasty temporary hack
@@ -1489,7 +1497,7 @@ static size_t xlat_process(TALLOC_CTX *ctx, char **out, REQUEST *request, xlat_e
                 *      Break here to avoid nodes being evaluated multiple times
                 *      and parts of strings being duplicated.
                 */
-               if ((node->type == XLAT_FUNC) && (node->xlat->type == XLAT_FUNC_NORMAL)) {
+               if ((node->type == XLAT_FUNC) && (node->call.func->type == XLAT_FUNC_NORMAL)) {
                        i++;
                        break;
                }
@@ -1599,7 +1607,7 @@ static ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, REQUEST *r
        /*
         *      Give better errors than the old code.
         */
-       len = xlat_tokenize_ephemeral(ctx, &node, request, fmt, NULL);
+       len = xlat_tokenize_ephemeral(ctx, &node, NULL, request, &FR_SBUFF_IN(fmt, strlen(fmt)), NULL, NULL);
        if (len == 0) {
                if (*out) {
                        **out = '\0';
@@ -1678,14 +1686,17 @@ ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, REQUEST *request,
 int xlat_aeval_compiled_argv(TALLOC_CTX *ctx, char ***argv, REQUEST *request,
                                 xlat_exp_t const *xlat, xlat_escape_legacy_t escape, void const *escape_ctx)
 {
-       int i;
-       ssize_t slen;
-       char **my_argv;
-       xlat_exp_t const *node;
+       int                     i;
+       ssize_t                 slen;
+       char                    **my_argv;
+       size_t                  count;
+       xlat_exp_t const        *node;
+
+       if (xlat->type != XLAT_GROUP) return -1;
 
-       if (xlat->type != XLAT_CHILD) return -1;
+       for (count = 0, node = xlat; node != NULL; node = node->next) count++;
 
-       MEM(my_argv = talloc_zero_array(ctx, char *, xlat->count + 1));
+       MEM(my_argv = talloc_zero_array(ctx, char *, count + 1));
        *argv = my_argv;
 
        fr_assert(done_init);
@@ -1697,7 +1708,7 @@ int xlat_aeval_compiled_argv(TALLOC_CTX *ctx, char ***argv, REQUEST *request,
                if (slen < 0) return -i;
        }
 
-       return xlat->count;
+       return count;
 }
 
 /** Turn xlat_tokenize_argv() into an argv[] array
@@ -1706,13 +1717,16 @@ int xlat_aeval_compiled_argv(TALLOC_CTX *ctx, char ***argv, REQUEST *request,
  */
 int xlat_flatten_compiled_argv(TALLOC_CTX *ctx, xlat_exp_t const ***argv, xlat_exp_t const *xlat)
 {
-       int i;
-       xlat_exp_t const **my_argv;
-       xlat_exp_t const *node;
+       int                     i;
+       xlat_exp_t const        **my_argv;
+       xlat_exp_t const        *node;
+       size_t                  count;
+
+       if (xlat->type != XLAT_GROUP) return -1;
 
-       if (xlat->type != XLAT_CHILD) return -1;
+       for (count = 0, node = xlat; node != NULL; node = node->next) count++;
 
-       MEM(my_argv = talloc_zero_array(ctx, xlat_exp_t const *, xlat->count + 1));
+       MEM(my_argv = talloc_zero_array(ctx, xlat_exp_t const *, count + 1));
        *argv = my_argv;
 
        fr_assert(done_init);
@@ -1721,7 +1735,7 @@ int xlat_flatten_compiled_argv(TALLOC_CTX *ctx, xlat_exp_t const ***argv, xlat_e
                my_argv[i] = node->child;
        }
 
-       return xlat->count;
+       return count;
 }
 
 
@@ -1828,8 +1842,8 @@ int xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_type_t type, void
                        if (ret < 0) return ret;
                        break;
 
-               case XLAT_CHILD:
-                       if (!type || (type & XLAT_CHILD)) {
+               case XLAT_GROUP:
+                       if (!type || (type & XLAT_GROUP)) {
                                ret = walker(node, uctx);
                                if (ret < 0) return ret;
                        }
@@ -1881,8 +1895,8 @@ void xlat_eval_free(void)
 
 /** Return whether or not async is required for this xlat.
  *
- *     If the xlat is async_safe, then it will never yield.
- *     If the xlat is not async_safe, then it may yield.
+ *     If the xlat is needs_async, then it will never yield.
+ *     If the xlat is not needs_async, then it may yield.
  *
  *     If the xlat yields, then async is required.
  */
@@ -1890,15 +1904,15 @@ bool xlat_async_required(xlat_exp_t const *xlat)
 {
        xlat_exp_t const *node;
 
-       if (xlat->type != XLAT_CHILD) {
-               return !xlat->async_safe;
+       if (xlat->type != XLAT_GROUP) {
+               return !xlat->flags.needs_async;
        }
 
        /*
-        *      Set async_safe on the entire list.
+        *      Set needs_async on the entire list.
         */
        for (node = xlat; node != NULL; node = node->next) {
-               if (!node->async_safe) return true;
+               if (!node->flags.needs_async) return true;
        }
 
        return false;
index 18a69e1c2a2052a5248b3d1a241f56ef37d1d5c4..48e4ba20ddc3f28522b02094839f54ced9016c84 100644 (file)
@@ -84,8 +84,8 @@ static int _xlat_thread_inst_detach(xlat_thread_inst_t *thread_inst)
 {
        fr_assert(thread_inst->node->type == XLAT_FUNC);
 
-       if (thread_inst->node->xlat->thread_detach) {
-               (void) thread_inst->node->xlat->thread_detach(thread_inst->data, thread_inst->node->xlat->thread_uctx);
+       if (thread_inst->node->call.func->thread_detach) {
+               (void) thread_inst->node->call.func->thread_detach(thread_inst->data, thread_inst->node->call.func->thread_uctx);
        }
 
        return 0;
@@ -118,9 +118,9 @@ static xlat_thread_inst_t *xlat_thread_inst_alloc(TALLOC_CTX *ctx, xlat_inst_t *
 
        (void)talloc_get_type_abort(inst, xlat_inst_t);
 
-       if (inst->node->xlat->thread_inst_size) {
+       if (inst->node->call.func->thread_inst_size) {
                MEM(thread_inst = talloc_zero_pooled_object(ctx, xlat_thread_inst_t,
-                                                           1, inst->node->xlat->thread_inst_size));
+                                                           1, inst->node->call.func->thread_inst_size));
        } else {
                MEM(thread_inst = talloc_zero(ctx, xlat_thread_inst_t));
        }
@@ -128,18 +128,18 @@ static xlat_thread_inst_t *xlat_thread_inst_alloc(TALLOC_CTX *ctx, xlat_inst_t *
        thread_inst->node = inst->node;
 
        fr_assert(inst->node->type == XLAT_FUNC);
-       fr_assert(!inst->node->thread_inst);            /* May be missing inst, but this is OK */
+       fr_assert(!inst->node->call.thread_inst);               /* May be missing inst, but this is OK */
 
        talloc_set_destructor(thread_inst, _xlat_thread_inst_detach);
-       if (inst->node->xlat->thread_inst_size) {
-               MEM(thread_inst->data = talloc_zero_array(thread_inst, uint8_t, inst->node->xlat->thread_inst_size));
+       if (inst->node->call.func->thread_inst_size) {
+               MEM(thread_inst->data = talloc_zero_array(thread_inst, uint8_t, inst->node->call.func->thread_inst_size));
 
                /*
                 *      This is expensive, only do it if we might
                 *      might be using it.
                 */
 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
-               talloc_set_name_const(thread_inst->data, inst->node->xlat->thread_inst_type);
+               talloc_set_name_const(thread_inst->data, inst->node->call.func->thread_inst_type);
 #endif
        }
 
@@ -164,12 +164,12 @@ static int _xlat_inst_detach(xlat_inst_t *inst)
        /*
         *      Remove permanent data from the instance tree.
         */
-       if (!inst->node->ephemeral) {
+       if (!inst->node->call.ephemeral) {
                rbtree_deletebydata(xlat_inst_tree, inst);
                if (rbtree_num_elements(xlat_inst_tree) == 0) TALLOC_FREE(xlat_inst_tree);
        }
 
-       if (inst->node->xlat->detach) (void) inst->node->xlat->detach(inst->data, inst->node->xlat->uctx);
+       if (inst->node->call.func->detach) (void) inst->node->call.func->detach(inst->data, inst->node->call.func->uctx);
 
        return 0;
 }
@@ -195,10 +195,10 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node)
 
        fr_assert(xlat_inst_tree);              /* xlat_inst_init must have been called */
        fr_assert(node->type == XLAT_FUNC);
-       fr_assert(!node->inst);
+       fr_assert(!node->call.inst);
 
-       if (node->xlat->inst_size) {
-               MEM(inst = talloc_zero_pooled_object(node, xlat_inst_t, 1, node->xlat->inst_size));
+       if (node->call.func->inst_size) {
+               MEM(inst = talloc_zero_pooled_object(node, xlat_inst_t, 1, node->call.func->inst_size));
        } else {
                MEM(inst = talloc_zero(node, xlat_inst_t));
        }
@@ -210,15 +210,15 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node)
         *      node is freed.
         */
        talloc_set_destructor(inst, _xlat_inst_detach);
-       if (node->xlat->inst_size) {
-               MEM(inst->data = talloc_zero_array(inst, uint8_t, node->xlat->inst_size));
+       if (node->call.func->inst_size) {
+               MEM(inst->data = talloc_zero_array(inst, uint8_t, node->call.func->inst_size));
 
                /*
                 *      This is expensive, only do it if we might
                 *      might be using it.
                 */
 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
-               talloc_set_name_const(inst->data, node->xlat->inst_type);
+               talloc_set_name_const(inst->data, node->call.func->inst_type);
 #endif
        }
 
@@ -237,37 +237,37 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node)
  */
 static int _xlat_instantiate_ephemeral_walker(xlat_exp_t *node, UNUSED void *uctx)
 {
-       fr_assert(!node->inst && !node->thread_inst);
+       fr_assert(!node->call.inst && !node->call.thread_inst);
 
-       node->inst = xlat_inst_alloc(node);
-       if (!node->inst) return -1;
+       node->call.inst = xlat_inst_alloc(node);
+       if (!node->call.inst) return -1;
 
        /*
         *      Instantiate immediately unlike permanent XLATs
         *      Where it's a separate phase.
         */
-       if (node->xlat->instantiate &&
-           (node->xlat->instantiate(node->inst->data, node, node->xlat->uctx) < 0)) {
+       if (node->call.func->instantiate &&
+           (node->call.func->instantiate(node->call.inst->data, node, node->call.func->uctx) < 0)) {
        error:
-               TALLOC_FREE(node->inst);
+               TALLOC_FREE(node->call.inst);
                return -1;
        }
 
        /*
         *      Create a thread instance too.
         */
-       node->thread_inst = xlat_thread_inst_alloc(node, node->inst);
-       if (!node->thread_inst) goto error;
+       node->call.thread_inst = xlat_thread_inst_alloc(node, node->call.inst);
+       if (!node->call.thread_inst) goto error;
 
-       if (node->xlat->thread_instantiate &&
-           node->xlat->thread_instantiate(node->inst, node->thread_inst->data,
-                                          node, node->xlat->thread_uctx) < 0) goto error;
+       if (node->call.func->thread_instantiate &&
+           node->call.func->thread_instantiate(node->call.inst, node->call.thread_inst->data,
+                                          node, node->call.func->thread_uctx) < 0) goto error;
 
        /*
         *      Mark this up as an ephemeral node, so the destructors
         *      don't search for it in the xlat_inst_tree.
         */
-       node->ephemeral = true;
+       node->call.ephemeral = true;
 
        return 0;
 }
@@ -295,13 +295,13 @@ static int _xlat_thread_instantiate(void *data, void *uctx)
        if (!thread_inst) return -1;
 
        DEBUG3("Instantiating xlat \"%s\" node %p, instance %p, new thread instance %p",
-              inst->node->xlat->name, inst->node, inst, thread_inst);
+              inst->node->call.func->name, inst->node, inst, thread_inst);
 
-       if (inst->node->xlat->thread_instantiate) {
+       if (inst->node->call.func->thread_instantiate) {
                int ret;
 
-               ret = inst->node->xlat->thread_instantiate(inst->data, thread_inst->data,
-                                                          inst->node, inst->node->xlat->thread_uctx);
+               ret = inst->node->call.func->thread_instantiate(inst->data, thread_inst->data,
+                                                          inst->node, inst->node->call.func->thread_uctx);
                if (ret < 0) {
                        talloc_free(thread_inst);
                        return -1;
@@ -327,7 +327,7 @@ xlat_thread_inst_t *xlat_thread_instance_find(xlat_exp_t const *node)
        fr_assert(xlat_thread_inst_tree);
        fr_assert(node->type == XLAT_FUNC);
 
-       if (node->ephemeral) return node->thread_inst;
+       if (node->call.ephemeral) return node->call.thread_inst;
 
        found = rbtree_finddata(xlat_thread_inst_tree, &(xlat_thread_inst_t){ .node = node });
        fr_assert(found);
@@ -389,8 +389,8 @@ static int _xlat_instantiate_walker(void *data, UNUSED void *uctx)
 {
        xlat_inst_t *inst = talloc_get_type_abort(data, xlat_inst_t);
 
-       if (inst->node->xlat->instantiate &&
-           (inst->node->xlat->instantiate(inst->data, inst->node, inst->node->xlat->uctx) < 0)) return -1;
+       if (inst->node->call.func->instantiate &&
+           (inst->node->call.func->instantiate(inst->data, inst->node, inst->node->call.func->uctx) < 0)) return -1;
 
        return 0;
 }
@@ -427,32 +427,36 @@ int xlat_instantiate(void)
  * between the module instantiation phase and the xlat instantiation phase.
  *
  * @param[in] node     to create "permanent" instance data for.
- * @param[in] uctx     UNUSED.
  * @return
  *     - 0 if instantiation functions were successful.
  *     - -1 if either instantiation function failed.
  */
-static int _xlat_bootstrap_walker(xlat_exp_t *node, UNUSED void *uctx)
+int xlat_bootstrap_func(xlat_exp_t *node)
 {
        bool ret;
 
        fr_assert(node->type == XLAT_FUNC);
-       fr_assert(!node->inst && !node->thread_inst);
+       fr_assert(!node->call.inst && !node->call.thread_inst);
 
-       node->inst = xlat_inst_alloc(node);
-       if (!node->inst) return -1;
+       node->call.inst = xlat_inst_alloc(node);
+       if (!node->call.inst) return -1;
 
-       DEBUG3("Instantiating xlat \"%s\" node %p, new instance %p", node->xlat->name, node, node->inst);
+       DEBUG3("Instantiating xlat \"%s\" node %p, new instance %p", node->call.func->name, node, node->call.inst);
 
-       ret = rbtree_insert(xlat_inst_tree, node->inst);
+       ret = rbtree_insert(xlat_inst_tree, node->call.inst);
        if (!fr_cond_assert(ret)) {
-               TALLOC_FREE(node->inst);
+               TALLOC_FREE(node->call.inst);
                return -1;
        }
 
        return 0;
 }
 
+static int _xlat_bootstrap_walker(xlat_exp_t *node, UNUSED void *uctx)
+{
+       return xlat_bootstrap_func(node);
+}
+
 /** Create instance data for "permanent" xlats
  *
  * @note This must only be used for xlats created during startup.
index 24b545576c5783db75d43d2eebcc25266d13560a..6dd1c59402f56006d08c713d1f679c9df5775c03 100644 (file)
@@ -40,57 +40,69 @@ extern "C" {
  *
  */
 typedef enum {
-       XLAT_FUNC_LEGACY,                                       //!< Ingests and excretes strings.
-       XLAT_FUNC_NORMAL                                        //!< Ingests and excretes value boxes (and may yield)
+       XLAT_FUNC_LEGACY,                               //!< Ingests and excretes strings.
+       XLAT_FUNC_NORMAL                                //!< Ingests and excretes value boxes (and may yield)
 } xlat_func_legacy_type_t;
 
 typedef struct xlat_s {
-       char const              *name;                          //!< Name of xlat function.
+       char const              *name;                  //!< Name of xlat function.
 
        union {
-               xlat_func_legacy_t      sync;                   //!< synchronous xlat function (async safe).
-               xlat_func_t     async;                          //!< async xlat function (async unsafe).
+               xlat_func_legacy_t      sync;           //!< synchronous xlat function (async safe).
+               xlat_func_t             async;          //!< async xlat function (async unsafe).
        } func;
-       xlat_func_legacy_type_t type;                           //!< Type of xlat function.
+       xlat_func_legacy_type_t type;                   //!< Type of xlat function.
 
-       xlat_instantiate_t      instantiate;                    //!< Instantiation function.
-       xlat_thread_instantiate_t thread_instantiate;           //!< Thread instantiation function.
+       bool                    internal;               //!< If true, cannot be redefined.
 
-       xlat_detach_t           detach;                         //!< Destructor for when xlat instances are freed.
-       xlat_thread_detach_t    thread_detach;                  //!< Destructor for when xlat thread instance data
-                                                               ///< is freed.
+       xlat_instantiate_t      instantiate;            //!< Instantiation function.
+       xlat_detach_t           detach;                 //!< Destructor for when xlat instances are freed.
+       char const              *inst_type;             //!< C type of instance structure.
+       size_t                  inst_size;              //!< Size of instance data to pre-allocate.
+       void                    *uctx;                  //!< uctx to pass to instantiation functions.
 
-       bool                    internal;                       //!< If true, cannot be redefined.
+       xlat_thread_instantiate_t thread_instantiate;   //!< Thread instantiation function.
+       xlat_thread_detach_t    thread_detach;          //!< Destructor for when xlat thread instance data
+                                                       ///< is freed.
+       char const              *thread_inst_type;      //!< C type of thread instance structure.
+       size_t                  thread_inst_size;       //!< Size of the thread instance data to pre-allocate.
+       void                    *thread_uctx;           //!< uctx to pass to instantiation functions.
 
-       char const              *inst_type;                     //!< C name of instance structure.
-       size_t                  inst_size;                      //!< Size of instance data to pre-allocate.
-       void                    *uctx;                          //!< uctx to pass to instantiation functions.
+       bool                    needs_async;            //!< If true, is async safe
 
-       char const              *thread_inst_type;              //!< C name of thread instance structure.
-       size_t                  thread_inst_size;               //!< Size of the thread instance data to pre-allocate.
-       void                    *thread_uctx;                   //!< uctx to pass to instantiation functions.
-
-       bool                    async_safe;                     //!< If true, is async safe
-
-       size_t                  buf_len;                        //!< Length of output buffer to pre-allocate.
-       void                    *mod_inst;                      //!< Module instance passed to xlat
-       xlat_escape_legacy_t            escape;                         //!< Escape function to apply to dynamic input to func.
+       size_t                  buf_len;                //!< Length of output buffer to pre-allocate.
+       void                    *mod_inst;              //!< Module instance passed to xlat
+       xlat_escape_legacy_t    escape;                 //!< Escape function to apply to dynamic input to func.
 } xlat_t;
 
-
 typedef enum {
-       XLAT_LITERAL            = 0x01,         //!< Literal string
-       XLAT_ONE_LETTER         = 0x02,         //!< Literal string with %v
-       XLAT_FUNC               = 0x04,         //!< xlat module
-       XLAT_VIRTUAL            = 0x08,         //!< virtual attribute
-       XLAT_ATTRIBUTE          = 0x10,         //!< xlat attribute
+       XLAT_INVALID            = 0x0000,               //!< Bad expansion
+       XLAT_LITERAL            = 0x0001,               //!< Literal string
+       XLAT_ONE_LETTER         = 0x0002,               //!< Special "one-letter" expansion
+       XLAT_FUNC               = 0x0004,               //!< xlat module
+       XLAT_FUNC_UNRESOLVED    = 0x0008,               //!< func needs resolution during pass2.
+       XLAT_VIRTUAL            = 0x0010,               //!< virtual attribute
+       XLAT_VIRTUAL_UNRESOLVED = 0x0020,               //!< virtual attribute needs resolution during pass2.
+       XLAT_ATTRIBUTE          = 0x0040,               //!< xlat attribute
 #ifdef HAVE_REGEX
-       XLAT_REGEX              = 0x20,         //!< regex reference
+       XLAT_REGEX              = 0x0080,               //!< regex reference
 #endif
-       XLAT_ALTERNATE          = 0x40,         //!< xlat conditional syntax :-
-       XLAT_CHILD              = 0x80          //!< encapsulated string of xlats
+       XLAT_ALTERNATE          = 0x0100,               //!< xlat conditional syntax :-
+       XLAT_GROUP              = 0x0200                //!< encapsulated string of xlats
 } xlat_type_t;
 
+/** An xlat function call
+ *
+ */
+typedef struct {
+       xlat_t const            *func;                  //!< The xlat expansion to expand format with.
+       bool                    ephemeral;              //!< Instance data is ephemeral (not inserted)
+                                                       ///< into the instance tree.
+       xlat_inst_t             *inst;                  //!< Instance data for the #xlat_t.
+       xlat_thread_inst_t      *thread_inst;           //!< Thread specific instance.
+                                                       ///< ONLY USED FOR EPHEMERAL XLATS.
+} xlat_call_t;
+
 /** An xlat expansion node
  *
  * These nodes form a tree which represents one or more nested expansions.
@@ -98,35 +110,29 @@ typedef enum {
 struct xlat_exp {
        char const      *fmt;           //!< The original format string.
        size_t          len;            //!< Length of the format string.
+       fr_token_t      quote;          //!< Type of quoting around XLAT_GROUP types.
 
-       bool            async_safe;     //!< carried from all of the children
+       xlat_flags_t    flags;          //!< Flags that control resolution and evaluation.
 
        xlat_type_t     type;           //!< type of this expansion.
        xlat_exp_t      *next;          //!< Next in the list.
 
-       xlat_exp_t      *child;         //!< Nested expansion.
-
-       union {
-               xlat_exp_t      *alternate;     //!< Alternative expansion if this one expanded to a zero length string.
-
-               tmpl_t  *attr;          //!< An attribute template.
+       xlat_exp_t      *child;         //!< Nested expansion, i.e. arguments for an xlat function.
+       xlat_exp_t      *alternate;     //!< Alternative expansion if this expansion produced no values.
 
-               int             regex_index;    //!< for %{1} and friends.
+       /** An attribute reference
+        *
+        * May be an attribute to expand, or provide context for a call.
+        */
+       tmpl_t          *attr;
 
-               int             count;          //!< for XLAT_CHILD
-       };
+       /** A capture group, i.e. for %{1} and friends
+        */
+       int             regex_index;
 
-       /*
-        *      An xlat function
+       /** An xlat function call
         */
-       struct {
-               xlat_t const            *xlat;          //!< The xlat expansion to expand format with.
-               bool                    ephemeral;      //!< Instance data is ephemeral (not inserted)
-                                                       ///< into the instance tree.
-               xlat_inst_t             *inst;          //!< Instance data for the #xlat_t.
-               xlat_thread_inst_t      *thread_inst;   //!< Thread specific instance.
-                                                       ///< ONLY USED FOR EPHEMERAL XLATS.
-       };
+       xlat_call_t     call;
 };
 
 typedef struct {
index ad5e768f5d589f93e864468dd3310d4a1b25514d..6760e08542cf77f330ce40660bc1347ae2a36dde 100644 (file)
  * @file xlat_tokenize.c
  * @brief String expansion ("translation").  Tokenizes xlat expansion strings.
  *
- * @copyright 2000,2006 The FreeRADIUS server project
  * @copyright 2000 Alan DeKok (aland@freeradius.org)
+ * @copyright 2000,2006 The FreeRADIUS server project
+ * @copyright 2017-2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  */
 
 RCSID("$Id$")
 
 #include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/sbuff.h>
 #include <freeradius-devel/server/request.h>
 #include <freeradius-devel/server/regex.h>
 #include <freeradius-devel/unlang/xlat_priv.h>
@@ -34,161 +36,231 @@ RCSID("$Id$")
 #include <ctype.h>
 
 #undef XLAT_DEBUG
+#undef XLAT_HEXDUMP
 #ifdef DEBUG_XLAT
-#  define XLAT_DEBUG DEBUG3
+#  define XLAT_DEBUG(_fmt, ...)                        DEBUG3("%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
+#  define XLAT_HEXDUMP(_data, _len, _fmt, ...) HEXDUMP3(_data, _len, "%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
 #else
 #  define XLAT_DEBUG(...)
+#  define XLAT_HEXDUMP(...)
 #endif
 
+/** These rules apply to literals and function arguments inside of an expansion
+ *
+ */
+static fr_sbuff_unescape_rules_t const xlat_unescape = {
+       .name = "xlat",
+       .chr = '\\',
+       .subs = {
+               ['a'] = '\a',
+               ['b'] = '\b',
+               ['e'] = '\\',
+               ['n'] = '\n',
+               ['r'] = '\r',
+               ['t'] = '\t',
+               ['v'] = '\v',
+               ['\\'] = '\\',
+               ['%'] = '%',    /* Expansion begin */
+               ['}'] = '}'     /* Expansion end */
+       },
+       .do_hex = true,
+       .do_oct = true
+};
+
+/** These rules apply to literals and function arguments inside of an expansion
+ *
+ */
+static fr_sbuff_escape_rules_t const xlat_escape = {
+       .name = "xlat",
+       .chr = '\\',
+       .subs = {
+               ['\a'] = 'a',
+               ['\b'] = 'b',
+               ['\n'] = 'n',
+               ['\r'] = 'r',
+               ['\t'] = 't',
+               ['\v'] = 'v',
+               ['\\'] = '\\',
+               ['%'] = '%',    /* Expansion begin */
+               ['}'] = '}'     /* Expansion end */
+       },
+       .esc = {
+               SBUFF_CHAR_UNPRINTABLES_LOW,
+               SBUFF_CHAR_UNPRINTABLES_EXTENDED
+       },
+       .do_utf8 = true,
+       .do_oct = true
+};
+
+/** Parse rules for literals inside of an expansion
+ *
+ * These rules are used to parse literals as arguments to functions and
+ * on the RHS of alternations.
+ *
+ * The caller sets the literal parse rules for outside of expansions when they
+ * call xlat_tokenize.
+ */
+static fr_sbuff_parse_rules_t const xlat_rules = {
+       .escapes = &xlat_unescape,
+       .terminals = &FR_SBUFF_TERM("}")        /* These get merged with other literal terminals */
+};
+
+/** Allocate an xlat node with no name, and no type set
+ *
+ * @param[in] ctx      to allocate node in.
+ * @return A new xlat node.
+ */
+static inline CC_HINT(always_inline) xlat_exp_t *xlat_exp_alloc_null(TALLOC_CTX *ctx)
+{
+       xlat_exp_t *node;
+
+       MEM(node = talloc_zero(ctx, xlat_exp_t));
+
+       return node;
+}
+
 /** Allocate an xlat node
  *
  * @param[in] ctx      to allocate node in.
  * @param[in] type     of the node.
  * @param[in] in       original input string.
- * @param[in] inlen    Portion of the input string that this node represents.
  * @return A new xlat node.
  */
-static inline xlat_exp_t *xlat_exp_alloc(TALLOC_CTX *ctx, xlat_type_t type, char const *in, size_t inlen)
+static inline CC_HINT(always_inline) xlat_exp_t *xlat_exp_alloc(TALLOC_CTX *ctx, xlat_type_t type,
+                                                               char const *in, size_t inlen)
 {
        xlat_exp_t *node;
 
-       MEM(node = talloc_zero(ctx, xlat_exp_t));
+       node = xlat_exp_alloc_null(ctx);
        node->type = type;
        if (in) node->fmt = talloc_bstrndup(node, in, inlen);
 
        return node;
 }
 
-/** Try to convert an xlat to a tmpl for efficiency
+/** Set the type of an xlat node
  *
- * @param ctx to allocate new tmpl_t in.
- * @param node to convert.
- * @return
- *     - NULL if unable to convert (not necessarily error).
- *     - A new #tmpl_t.
+ * @param[in] node     to set type for.
+ * @param[in] type     to set.
  */
-tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
+static inline CC_HINT(always_inline) void xlat_exp_set_type(xlat_exp_t *node, xlat_type_t type)
 {
-       tmpl_t *vpt;
-
-       if (node->next || (node->type != XLAT_ATTRIBUTE) || !tmpl_is_attr(node->attr)) return NULL;
-
-       /*
-        *   Concat means something completely different as an attribute reference
-        *   Count isn't implemented.
-        */
-       if ((tmpl_num(node->attr) == NUM_COUNT) || (tmpl_num(node->attr) == NUM_ALL)) return NULL;
-
-       vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1, T_BARE_WORD);
-       if (!vpt) return NULL;
-
-       tmpl_attr_copy(vpt, node->attr);
-
-       TMPL_VERIFY(vpt);
+       node->type = type;
+}
 
-       return vpt;
+#if 0
+/** Set the format string for an xlat node
+ *
+ * @param[in] node     to set fmt for.
+ * @param[in] fmt      talloced buffer to set as the fmt string.
+ */
+static inline CC_HINT(always_inline) void xlat_exp_set_name_buffer(xlat_exp_t *node, char const *fmt)
+{
+       if (node->fmt) talloc_const_free(node->fmt);
+       node->fmt = talloc_bstrdup(node, fmt);
 }
+#endif
 
-/** Convert attr tmpl to an xlat for &attr[*]
+/** Set the format string for an xlat node
  *
- * @param ctx to allocate new xlat_expt_t in.
- * @param vpt to convert.
- * @return
- *     - NULL if unable to convert (not necessarily error).
- *     - a new #tmpl_t.
+ * @param[in] node     to set fmt for.
+ * @param[in] fmt      talloced buffer to set as the fmt string.
  */
-xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, tmpl_t *vpt)
+static inline CC_HINT(always_inline) void xlat_exp_set_name_buffer_shallow(xlat_exp_t *node, char const *fmt)
 {
-       xlat_exp_t *node;
+       if (node->fmt) talloc_const_free(node->fmt);
+       node->fmt = fmt;
+}
 
-       if (!tmpl_is_attr(vpt)) return NULL;
+/** Merge flags from child to parent
+ *
+ * For pass2, if either the parent or child is marked up for pass2, then the parent
+ * is marked up for pass2.
+ *
+ * For needs_async, if both the parent and the child are needs_async, the parent is
+ * needs_async.
+ */
+static inline CC_HINT(always_inline) void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
+{
+       parent->needs_async |= child->needs_async;
+       parent->needs_resolving |= child->needs_resolving;
+}
 
-       node = xlat_exp_alloc(ctx, XLAT_ATTRIBUTE, vpt->name, vpt->len);
-       node->attr = tmpl_alloc(node, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1, T_BARE_WORD);
-       tmpl_attr_copy(node->attr, vpt);
+/** Free a linked list of xlat nodes
+ *
+ * @param[in,out] head to free.  Will be set to NULL
+ */
+void xlat_exp_free(xlat_exp_t **head)
+{
+       xlat_exp_t *to_free = *head, *next;
 
-       return node;
+       while (to_free) {
+               next = to_free->next;
+               talloc_free(to_free);
+               to_free = next;
+       };
+       *head = NULL;
 }
 
-static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                      tmpl_rules_t const *rules);
-static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                    bool brace, tmpl_rules_t const *rules);
+static int xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                  tmpl_rules_t const *ar_rules);
 
-static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                        tmpl_rules_t const *rules)
+static int xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                bool brace,
+                                fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules);
+
+static inline int xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                           tmpl_rules_t const *ar_rules)
 {
-       ssize_t         slen;
-       char const      *p = in;
        xlat_exp_t      *node;
 
-       fr_assert(in[0] == '%');
-       fr_assert(in[1] == '{');
-       fr_assert(in[2] == '%');
-       fr_assert(in[3] == '{');
-
-       XLAT_DEBUG("ALTERNATE <-- %s", in);
-
-       node = xlat_exp_alloc(ctx, XLAT_ALTERNATE, NULL, 0);
-       p += 2;
-       slen = xlat_tokenize_expansion(node, &node->child, p, in + inlen - p, rules);
-       if (slen <= 0) {
-               talloc_free(node);
-               return slen - (p - in);
-       }
-       p += slen;
+       XLAT_DEBUG("ALTERNATE <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
-       if (p[0] != ':') {
+       node = xlat_exp_alloc_null(ctx);
+       xlat_exp_set_type(node, XLAT_ALTERNATE);
+       if (xlat_tokenize_expansion(node, &node->child, &node->flags, in, ar_rules) < 0) {
+       error:
+               *head = NULL;
                talloc_free(node);
-               fr_strerror_printf("Expected ':' after first expansion, got '%pV'",
-                                  fr_box_strvalue_len(p, 1));
-               return -(p - in);
+               return -1;
        }
-       p++;
 
-       if (p[0] != '-') {
-               talloc_free(node);
-               fr_strerror_printf("Expected '-' after ':'");
-               return -(p - in);
+       if (!fr_sbuff_adv_past_str_literal(in, ":-")) {
+               fr_strerror_printf("Expected ':-' after first expansion");
+               goto error;
        }
-       p++;
 
        /*
         *      Allow the RHS to be empty as a special case.
         */
-       switch (*p) {
-       case '}':
+       if (fr_sbuff_next_if_char(in, '}')) {
                node->alternate = xlat_exp_alloc(node, XLAT_LITERAL, "", 0);
-               node->async_safe = node->child->async_safe;
+               xlat_flags_merge(&node->flags, &node->child->flags);
                *head = node;
-               return (p + 1) - in;
-
-       case '\0':
-               fr_strerror_printf("No matching closing brace");
-               talloc_free(node);
-               return -2;
+               return 0;
        }
 
        /*
         *      Parse the alternate expansion.
         */
-       slen = xlat_tokenize_literal(node, &node->alternate, p, in + inlen - p, true, rules);
-       if (slen <= 0) {
-               talloc_free(node);
-               return slen - (p - in);
-       }
+       if (xlat_tokenize_literal(node, &node->alternate, &node->flags, in,
+                                 true, &xlat_rules, ar_rules) < 0) goto error;
 
        if (!node->alternate) {
                talloc_free(node);
                fr_strerror_printf("Empty expansion is invalid");
-               return -(p - in);
+               goto error;
+       }
+
+       if (!fr_sbuff_next_if_char(in, '}')) {
+               fr_strerror_printf("Missing closing brace");
+               goto error;
        }
-       p += slen;
 
-       node->async_safe = (node->child->async_safe && node->alternate->async_safe);
+       xlat_flags_merge(flags, &node->flags);
        *head = node;
 
-       return p - in;
+       return 0;
 }
 
 #ifdef HAVE_REGEX
@@ -198,62 +270,52 @@ static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, cha
  * @verbatim %{<num>} @endverbatim
  *
  */
-static inline ssize_t xlat_tokenize_regex(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen)
+static inline int xlat_tokenize_regex(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in)
 {
-       unsigned long   num;
-       char const      *p, *end;
-       xlat_exp_t      *node;
-
-       /*
-        *      Must be at least %{0} in length
-        */
-       if (inlen < 4) return 0;
+       uint8_t                 num;
+       xlat_exp_t              *node;
+       fr_sbuff_parse_error_t  err;
+       fr_sbuff_marker_t       m_s;
 
-       fr_assert(in[0] == '%');
-       fr_assert(in[1] == '{');
+       XLAT_DEBUG("REGEX <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
-       p = in + 2;
-       end = in + inlen;
+       fr_sbuff_marker(&m_s, in);
 
-       if (*p == '}') {
-       fail:
+       fr_sbuff_out(&err, &num, in);
+       if (err != FR_SBUFF_PARSE_OK) {
+       invalid_ref:
                fr_strerror_printf("Invalid regex reference.  Must be in range 0-%u", REQUEST_MAX_REGEX);
-               return -(p - in);               /* error */
+               fr_sbuff_marker_release(&m_s);
+               return -1;
        }
 
-       num = 0;
-       while (p < end) {
-               if (*p == '}') break;
-
-               /*
-                *      We allow numbers followed by letters for attributes such as 3GPP-...
-                */
-               if (!((p[0] >= '0') && (p[0] <= '9'))) return 0;
-               num *= 10;
-               num += p[0] - '0';
-               p++;
-
-               if (num > REQUEST_MAX_REGEX) {
-                       p = in + 2;
-                       goto fail;
-               };
+       if (num > REQUEST_MAX_REGEX) {
+               fr_sbuff_set(in, &m_s);
+               goto invalid_ref;
        }
 
-       if (*p != '}') {
-               fr_strerror_printf("No matching closing brace");
-               return -1;
+       if (!fr_sbuff_is_char(in, '}')) {
+               if (!fr_sbuff_remaining(in)) {
+                       fr_strerror_printf("Missing closing brace");
+                       fr_sbuff_marker_release(&m_s);
+                       return -1;
+               }
+               fr_sbuff_set(in, &m_s);
+               fr_sbuff_marker_release(&m_s);
+               return 1;
        }
 
-       XLAT_DEBUG("REGEX <-- %pV", fr_box_strvalue_len(in, (p - in) + 1));
-
-       node = xlat_exp_alloc(ctx, XLAT_REGEX, p, p - (in + 2));
+       node = xlat_exp_alloc(ctx, XLAT_REGEX, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
        node->regex_index = num;
-       node->async_safe = true;
-       *head = node;
+       node->flags.needs_async = false;
+
+       fr_sbuff_marker_release(&m_s);
+       fr_sbuff_next(in);      /* Skip '}' */
 
-       p++;    /* Skip over '}' */
+       xlat_flags_merge(flags, &node->flags);
+       *head = node;
 
-       return p - in;
+       return 0;
 }
 #endif
 
@@ -262,85 +324,102 @@ static inline ssize_t xlat_tokenize_regex(TALLOC_CTX *ctx, xlat_exp_t **head, ch
  * Parses a function call string in the format
  * @verbatim %{<func>:<arguments} @endverbatim
  *
+ * @return
+ *     - 0 if the string was parsed into a function.
+ *     - <0 on parse error.
  */
-static inline ssize_t xlat_tokenize_function(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                            tmpl_rules_t const *rules)
+static inline int xlat_tokenize_function(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                        tmpl_rules_t const *rules)
 {
-       ssize_t         slen;
-       char const      *p, *q, *end;
-       xlat_exp_t      *node;
-       xlat_t          *func;
-
-       if (inlen < 2) return 0;
+       xlat_exp_t              *node;
+       xlat_t                  *func;
+       fr_sbuff_marker_t       m_s;
 
-       fr_assert(in[0] == '%');
-       fr_assert(in[1] == '{');
+       /*
+        *      Special characters, spaces, etc. cannot be
+        *      module names.
+        */
+       static bool const       func_chars[UINT8_MAX + 1] = {
+                                       SBUFF_CHAR_CLASS_ALPHA_NUM,
+                                       ['.'] = true, ['-'] = true, ['_'] = true,
+                               };
 
-       p = in + 2;
-       end = in + inlen;
+       XLAT_DEBUG("FUNC <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
        /*
         *      %{module:args}
         */
-       for (q = p; q < end; q++) {
-               if (*q == ':') break;
-
-               /*
-                *      Special characters, spaces, etc. cannot be
-                *      module names.
-                */
-               if ((*q < '0') && (*q != '.') && (*q != '-')) return 0;
+       fr_sbuff_marker(&m_s, in);
+       fr_sbuff_adv_past_allowed(in, SIZE_MAX, func_chars);
+
+       if (!fr_sbuff_is_char(in, ':')) {
+               fr_strerror_printf("Can't find function/argument separator");
+       bad_function:
+               *head = NULL;
+               fr_sbuff_set(in, &m_s);         /* backtrack */
+               fr_sbuff_marker_release(&m_s);
+               return -1;
        }
 
-       if (q >= end) return 0;
-
-       func = xlat_func_find(p, q - p);
-       if (!func) return 0;
+       func = xlat_func_find(fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
 
        /*
         *      Allocate a node to hold the function
         */
-       node = xlat_exp_alloc(ctx, XLAT_FUNC, p, q - p);
-       node->xlat = func;
+       node = xlat_exp_alloc(ctx, XLAT_FUNC, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
+       if (!func) {
+               if (!rules || !rules->allow_unresolved) {
+                       fr_strerror_printf("Unresolved expansion functions are not allowed here");
+                       goto bad_function;
+               }
+               xlat_exp_set_type(node, XLAT_FUNC_UNRESOLVED);
+               node->flags.needs_resolving = true;     /* Needs resolution during pass2 */
+       } else {
+               node->call.func = func;
+       }
 
-       p = q + 1;
-       XLAT_DEBUG("FUNC <-- %s ... %s", node->fmt, p);
+       fr_sbuff_next(in);                      /* Skip the ':' */
+       XLAT_DEBUG("FUNC-ARGS <-- %s ... %pV",
+                  node->fmt, fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
+
+       fr_sbuff_marker_release(&m_s);
 
        /*
         *      Now parse the child nodes that form the
         *      function's arguments.
         */
-       slen = xlat_tokenize_literal(node, &node->child, p, in + inlen - p, true, rules);
-       if (slen < 0) {
+       if (xlat_tokenize_literal(node, &node->child, &node->flags, in, true, &xlat_rules, rules) < 0) {
+       error:
+               *head = NULL;
                talloc_free(node);
-               return slen - (p - in); /* error */
+               return -1;
        }
 
-       p += slen;
-       if (*(p - 1) != '}') {  /* @todo: xlat_tokenize_literal should not consume the closing brace */
-               fr_strerror_printf("No matching closing brace");
-               talloc_free(node);
-               return -1;                                              /* error at the second character of format string */
+       if (!fr_sbuff_next_if_char(in, '}')) {
+               fr_strerror_printf("Missing closing brace");
+               goto error;
        }
 
-       node->async_safe = (func->async_safe && (!node->child || node->child->async_safe));
+       xlat_flags_merge(flags, &node->flags);
        *head = node;
 
-       return p - in;
+       return 0;
 }
 
 /** Parse an attribute ref or a virtual attribute
  *
  */
-static inline ssize_t xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                             tmpl_rules_t const *rules)
+static inline int xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                         fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
 {
        ssize_t                 slen;
-       char const              *p, *q;
        attr_ref_error_t        err;
-       tmpl_t          *vpt = NULL;
+       tmpl_t                  *vpt = NULL;
        xlat_exp_t              *node;
        xlat_t                  *func;
+       fr_sbuff_marker_t       m_s;
+
+       XLAT_DEBUG("ATTRIBUTE <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
        /*
         *      We need a local copy as we always allow unknowns.
@@ -349,101 +428,161 @@ static inline ssize_t xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head
         *      and instead are "virtual" attributes like
         *      Foreach-Variable-N.
         */
-       tmpl_rules_t our_rules;
+       tmpl_rules_t             our_ar_rules;
 
-       if (inlen < 2) return 0;
-
-       fr_assert(in[0] == '%');
-       fr_assert(in[1] == '{');
-
-       if (rules) {
-               memcpy(&our_rules, rules, sizeof(our_rules));
+       if (ar_rules) {
+               memcpy(&our_ar_rules, ar_rules, sizeof(our_ar_rules));
        } else {
-               memset(&our_rules, 0, sizeof(our_rules));
+               memset(&our_ar_rules, 0, sizeof(our_ar_rules));
        }
 
-       p = in + 2;
+       our_ar_rules.allow_unresolved = true;           /* So we can check for virtual attributes later */
+       our_ar_rules.prefix = TMPL_ATTR_REF_PREFIX_NO;  /* Must be NO to stop %{&User-Name} */
 
-       our_rules.allow_unresolved = true;              /* So we can check for virtual attributes later */
-       our_rules.prefix = TMPL_ATTR_REF_PREFIX_NO;     /* Must be NO to stop %{&User-Name} */
-       slen = tmpl_afrom_attr_substr(NULL, &err, &vpt, p, inlen - 2, &our_rules);
+       fr_sbuff_marker(&m_s, in);
+
+       MEM(node = xlat_exp_alloc_null(ctx));
+       slen = tmpl_afrom_attr_substr(node, &err, &vpt, in, p_rules, &our_ar_rules);
        if (slen <= 0) {
+               fr_sbuff_advance(in, slen * -1);
+
                /*
                 *      If the parse error occurred before the ':'
                 *      then the error is changed to 'Unknown module',
                 *      as it was more likely to be a bad module name,
                 *      than a request qualifier.
                 */
-               if (err == ATTR_REF_ERROR_INVALID_LIST_QUALIFIER) {
-                       fr_strerror_printf("Unknown expansion function or invalid list qualifier");
-               }
-               return slen - (p - in);         /* error somewhere after second character */
-       }
-       q = p + slen;
-
-       if (*q != '}') {
-               fr_strerror_printf("No matching closing brace");
-               talloc_free(vpt);
-               return -1;                                              /* error @ second character of format string */
+               if (err == ATTR_REF_ERROR_MISSING_TERMINATOR) fr_sbuff_set(in, &m_s);
+       error:
+               *head = NULL;
+               fr_sbuff_marker_release(&m_s);
+               talloc_free(node);
+               return -1;
        }
 
-       q++;    /* Skip over the closing brace */
-
        /*
         *      Might be a virtual XLAT attribute, which is identical
         *      to a normal function but called without an argument
         *      list.
+        *
+        *      We only consider virtual attributes at the top level
+        *      but we do allow request references and lists to be
+        *      specified so that the virtual attribute can operate
+        *      in different contexts (i.e. on the parent request).
         */
-       if (tmpl_is_attr_unresolved(vpt)) {
-               func = xlat_func_find(tmpl_attr_unresolved(vpt), -1);
-               if (func && (func->type == XLAT_FUNC_LEGACY)) {
-                       node = xlat_exp_alloc(ctx, XLAT_VIRTUAL,
-                                             tmpl_attr_unresolved(vpt), talloc_array_length(tmpl_attr_unresolved(vpt)) - 1);
-                       talloc_free(vpt);       /* Free the tmpl, we don't need it */
-
-                       XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
-                       node->xlat = func;
-                       node->async_safe = func->async_safe;
-                       *head = node;
+       if ((tmpl_is_attr_unresolved(vpt) || (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual)) &&
+           (tmpl_attr_ref_count(vpt) == 1)) {
+               if (tmpl_is_attr(vpt)) {
+                       func = xlat_func_find(tmpl_da(vpt)->name, -1);
+               } else {
+                       func = xlat_func_find(tmpl_attr_unresolved(vpt), -1);
+               }
+               if (func) {
+                       xlat_exp_set_type(node, XLAT_VIRTUAL);
+                       xlat_exp_set_name_buffer_shallow(node, vpt->name);
+
+                       XLAT_DEBUG("VIRTUAL <-- %pV",
+                                  fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
+                       node->call.func = func;
+                       node->attr = vpt;       /* Store for context */
+                       node->flags.needs_async = func->needs_async;
+
+                       if (!fr_sbuff_next_if_char(in, '}')) {
+                               fr_strerror_printf("Missing closing brace");
+                               goto error;
+                       }
 
-                       return q - in;
+                       goto done;
                }
-               talloc_free(vpt);
 
-               fr_strerror_printf("Unknown attribute");
-               return -2;                                              /* error @ third character of format string */
-       }
+               /*
+                *      Hack - We need a proper virtual attribute
+                *      registry.  There seems to have been a
+                *      partial conversion at some point which
+                *      needs completing.
+                *
+                *      This means we fall back to creating the
+                *      tmpl at a normal attribute which gets
+                *      processed correctly by the eval code
+                *      because of the virtual flag in the dictionary.
+                */
+               if (tmpl_is_attr(vpt)) goto do_attr;
 
-       node = xlat_exp_alloc(ctx, XLAT_ATTRIBUTE, vpt->name, vpt->len);
-       node->attr = talloc_steal(node, vpt);
-       node->async_safe = true; /* attribute expansions are always async-safe */
-       *head = node;
+               /*
+                *      Not a normal attribute, not a virtual attribute
+                *      and we're not allowing unresolved attributes.
+                */
+               if (!ar_rules || !ar_rules->allow_unresolved) {
+                       talloc_free(vpt);
 
-       return q - in;
-}
+                       fr_strerror_printf("Unresolved attributes not allowed in expansions here");
+                       fr_sbuff_set(in, &m_s);         /* Error at the start of the attribute */
+                       goto error;
+               }
 
-static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                      tmpl_rules_t const *rules)
-{
-       ssize_t         slen;
-       char const      *p = in, *q, *end;
+               /*
+                *      Mark the xlat exp as needing pass2 resolution.
+                */
+               xlat_exp_set_type(node, XLAT_VIRTUAL_UNRESOLVED);
+               xlat_exp_set_name_buffer_shallow(node, vpt->name);      /* vpt may be freed */
+
+               node->attr = vpt;
+               node->flags.needs_resolving = true;
+       /*
+        *      It's a straight attribute, nothing special
+        */
+       } else {
+       do_attr:
+               xlat_exp_set_type(node, XLAT_ATTRIBUTE);
+               xlat_exp_set_name_buffer_shallow(node, vpt->name);
+
+               node->attr = vpt;
+       }
 
-       XLAT_DEBUG("EXPANSION <-- %s", in);
+       if (!fr_sbuff_next_if_char(in, '}')) {
+               fr_strerror_printf("Missing closing brace");
+               goto error;
+       }
 
-       if (inlen < 2) return 0;
+done:
+       xlat_flags_merge(flags, &node->flags);
+       *head = node;
+       fr_sbuff_marker_release(&m_s);
+       return 0;
+}
 
-       fr_assert(in[0] == '%');
-       fr_assert(in[1] == '{');
+static int xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                                  tmpl_rules_t const *ar_rules)
+{
+       size_t                  len;
+       fr_sbuff_marker_t       s_m;
+       char                    hint;
+       int                     ret;
+       fr_sbuff_term_t         hint_tokens = FR_SBUFF_TERMS(
+                                       L(" "),         /* First special token is a ' ' - Likely a syntax error */
+                                       L(":"),         /* First special token is a ':' i.e. '%{func:' */
+                                       L("["),         /* First special token is a '[' i.e. '%{attr[<idx>]}' */
+                                       L("}")          /* First special token is a '}' i.e. '%{<attrref>}' */
+                               );
+
+       fr_sbuff_parse_rules_t  attr_p_rules = {
+                                       .escapes = &xlat_unescape,
+                                       .terminals = &FR_SBUFF_TERM("}")
+                               };
+
+       XLAT_DEBUG("EXPANSION <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
        /*
-        *      %{%{...}:-bar}
+        *      %{...}:-bar}
         */
-       if ((in[2] == '%') && (in[3] == '{')) return xlat_tokenize_alternation(ctx, head, in, inlen, rules);
+       if (fr_sbuff_adv_past_str_literal(in, "%{")) {
+               return xlat_tokenize_alternation(ctx, head, flags, in, ar_rules);
+       }
 
        /*
-        *      %{:-bar}
+        *      :-bar}
         */
-       if ((in[2] == ':') && (in[3] == '-')) {
+       if (fr_sbuff_is_str_literal(in, ":-")) {
                fr_strerror_printf("First item in alternation cannot be empty");
                return -2;
        }
@@ -452,58 +591,53 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char
        /*
         *      Handle regex's %{<num>} specially.
         */
-       if (isdigit(in[2])) {
-               slen = xlat_tokenize_regex(ctx, head, in, inlen);
-               if (slen != 0) return slen;     /* If slen == 0 means this wasn't a regex */
+       if (fr_sbuff_is_digit(in)) {
+               ret = xlat_tokenize_regex(ctx, head, flags, in);
+               if (ret <= 0) return ret;
        }
 #endif /* HAVE_REGEX */
 
        /*
         *      %{Attr-Name}
         *      %{Attr-Name[#]}
-        *      %{Tunnel-Password:1}
-        *      %{Tunnel-Password:1[#]}
-        *      %{request:Attr-Name}
-        *      %{request:Tunnel-Password:1}
-        *      %{request:Tunnel-Password:1[#]}
+        *      %{request.Attr-Name}
         *      %{mod:foo}
         */
 
        /*
-        *      This is for efficiency, so we don't search for an xlat,
-        *      when what's being referenced is obviously an attribute.
+        *      Check for empty expressions %{} %{: %{[
         */
-       p = in;
-       end = in + inlen;
-
-       for (q = p; (q < end) && *q; q++) {
-               if (*q == ':') break;                   /* First special token is a ':' i.e. '%{func:' */
+       fr_sbuff_marker(&s_m, in);
+       len = fr_sbuff_adv_until(in, SIZE_MAX, &hint_tokens, '\0');
 
-               if (isspace((int) *q)) break;           /* First special token is a ' ' - Likely a syntax error */
-
-               if (*q == '[') break;                   /* First special token is a '[' i.e. '%{attr[<idx>]}' */
-
-               if (*q == '}') break;                   /* First special token is a '}' i.e. '%{<attrref>}' */
+       /*
+        *      This means the end of a string not containing any of the other
+        *      tokens was reached.
+        *
+        *      e.g. '%{myfirstxlat'
+        */
+       if (!fr_sbuff_remaining(in)) {
+               fr_strerror_printf("Missing closing brace");
+               fr_sbuff_marker_release(&s_m);
+               return -1;
        }
 
-       XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", *q);
+       hint = *fr_sbuff_current(in);
 
-       /*
-        *      Check for empty expressions %{} %{: %{[
-        */
-       if (q == (p + 2)) {
-               switch (*q) {
+       XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", hint);
+       if (len == 0) {
+               switch (hint) {
                case '}':
                        fr_strerror_printf("Empty expression is invalid");
-                       return -2;                              /* error @ third character of format string */
+                       return -1;
 
                case ':':
-                       fr_strerror_printf("Missing expansion function or list qualifier");
-                       return -2;
+                       fr_strerror_printf("Missing expansion function");
+                       return -1;
 
                case '[':
                        fr_strerror_printf("Missing attribute name");
-                       return -2;
+                       return -1;
 
                default:
                        break;
@@ -511,42 +645,33 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char
        }
 
        /*
-        *      Hint token is a ':' it's either:
-        *      - An xlat function %{<func>:<args}
-        *      - An attribute reference with a list separator %{<list>:<attr>}
+        *      Hint token is a ':' it's an xlat function %{<func>:<args}
         */
-       switch (*q) {
+       switch (hint) {
        case ':':
-               slen = xlat_tokenize_function(ctx, head, in, inlen, rules);
-               if (slen != 0) return slen;
-               FALL_THROUGH;
+       {
+               fr_sbuff_set(in, &s_m);         /* backtrack */
+               fr_sbuff_marker_release(&s_m);
+
+               ret = xlat_tokenize_function(ctx, head, flags, in, ar_rules);
+               if (ret <= 0) return ret;
+       }
+               break;
 
        /*
         *      Hint token is a:
         *      - '[' - Which is an attribute index, so it must be an attribute.
         *      - '}' - The end of the expansion, which means it was a bareword.
         */
+       case '.':
        case '}':
        case '[':
-               slen = xlat_tokenize_attribute(ctx, head, in, inlen, rules);
-               if (slen < 0) return slen;
-               fr_assert(slen != 0);
+               fr_sbuff_set(in, &s_m);         /* backtrack */
+               fr_sbuff_marker_release(&s_m);
 
-               p += slen;
+               if (xlat_tokenize_attribute(ctx, head, flags, in, &attr_p_rules, ar_rules) < 0) return -1;
                break;
 
-       /*
-        *      Hint token is a '\0'
-        *
-        *      This means the end of a string not containing any of the other
-        *      tokens was reached.
-        *
-        *      e.g. '%{myfirstxlat'
-        */
-       case '\0':
-               fr_strerror_printf("No matching closing brace");
-               return -1;                                      /* error @ second character of format string */
-
        /*
         *      Hint token was whitespace
         *
@@ -554,560 +679,406 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char
         */
        default:
                /*
-                *      Box is so we get \t \n etc..
+                *      Box print is so we get \t \n etc..
                 */
-               fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(q, 1));
-               return -(q - in);
+               fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(fr_sbuff_current(in), 1));
+               return -1;
        }
 
-       return (p - in);
+       return 0;
 }
 
-
-static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                                    bool brace, tmpl_rules_t const *rules)
+/** Parse an xlat literal i.e. a non-expansion or non-function
+ *
+ * When this function is being used outside of an xlat expansion, i.e. on a string
+ * which contains one or more xlat expansions, it uses the terminal grammar and
+ * escaping rules of that string type.
+ *
+ * Which this function is being used inside of an xlat expansion, it uses xlat specific
+ * terminal grammar and escaping rules.
+ *
+ * This allows us to be smart about processing quotes within the expansions themselves.
+ *
+ * @param[in] ctx              to allocate nodes in.  Note: All nodes will be
+ *                             allocated in the same ctx.  This is to allow
+ *                             manipulation by xlat instantiation functions
+ *                             later.
+ * @param[out] head            Where to write the first child node.
+ * @param[out] flags           where we store flags information for the parent.
+ * @param[in] in               sbuff to parse.
+ * @param[in] brace            true if we're inside a braced expansion, else false.
+ * @param[in] p_rules          that control parsing.
+ * @param[in] ar_rules         that control attribute reference and xlat function parsing.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags,
+                                fr_sbuff_t *in, bool brace,
+                                fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
 {
-       char const      *p, *end;
-       xlat_exp_t      *node;
-       char            *start;
-
-       if (!*in) return 0;
+       xlat_exp_t                      *node = NULL;
+       size_t                          len;
+       fr_sbuff_term_t                 expansions = FR_SBUFF_TERMS(
+                                               L("%C"),
+                                               L("%D"),
+                                               L("%G"),
+                                               L("%H"),
+                                               L("%I"),
+                                               L("%M"),
+                                               L("%S"),
+                                               L("%T"),
+                                               L("%Y"),
+                                               L("%c"),
+                                               L("%d"),
+                                               L("%e"),
+                                               L("%l"),
+                                               L("%m"),
+                                               L("%n"),
+                                               L("%s"),
+                                               L("%t"),
+                                               L("%v"),
+                                               L("%{")
+                                       );
+       fr_sbuff_term_t                 *tokens;
+       fr_cursor_t                     cursor;
+       fr_sbuff_unescape_rules_t const *escapes;
 
-       XLAT_DEBUG("LITERAL <-- %s", in);
-
-       node = talloc_zero(ctx, xlat_exp_t);
-       node->fmt = start = talloc_bstrndup(node, in, inlen);
-       node->len = 0;
-       node->type = XLAT_LITERAL;
+       *head = NULL;
 
-       p = in;
-       end = in + inlen;
+       fr_cursor_init(&cursor, head);
+       escapes = p_rules ? p_rules->escapes : NULL;
+       tokens = p_rules && p_rules->terminals ?
+                       fr_sbuff_terminals_amerge(NULL, p_rules->terminals, &expansions) : &expansions;
 
-       while (p < end) {
-               if (*p == '\\') {
-                       if (((p + 1) >= end) || !p[1]) {
-                               talloc_free(node);
-                               fr_strerror_printf("Invalid escape at end of string");
-                               return -(p - in);
-                       }
+       for (;;) {
+               char *str;
 
-                       p += 2;
-                       node->len += 2;
-                       continue;
-               }
+               /*
+                *      pre-allocate the node so we don't have to steal it later.
+                */
+               node = xlat_exp_alloc_null(ctx);
 
-               if ((p[0] == '%') && (p + 1) >= end) goto invalid_variable;
+               /*
+                *      Find the next token
+                */
+               len = fr_sbuff_out_aunescape_until(node, &str, in, SIZE_MAX, tokens, escapes);
 
                /*
-                *      Process the expansion.  Which should be at least %{0} in length
+                *      It's a literal, create a literal node...
                 */
-               if ((p[0] == '%') && (p[1] == '{')) {
-                       ssize_t slen;
+               if (len > 0) {
+                       xlat_exp_set_type(node, XLAT_LITERAL);
+                       xlat_exp_set_name_buffer_shallow(node, str);
+
+                       XLAT_DEBUG("LITERAL (%s)<-- %pV",
+                                  escapes ? escapes->name : "(none)",
+                                  fr_box_strvalue_len(str, talloc_array_length(str) - 1));
+                       XLAT_HEXDUMP((uint8_t const *)str, talloc_array_length(str) - 1, " LITERAL ");
+                       node->flags.needs_async = false; /* literals are always true */
+                       fr_cursor_insert(&cursor, node);
+                       node = NULL;
+               }
 
-                       XLAT_DEBUG("EXPANSION-2 <-- %s", node->fmt);
+               if (fr_sbuff_adv_past_str_literal(in, "%{")) {
+                       if (len == 0) TALLOC_FREE(node); /* Free the empty node */
 
-                       slen = xlat_tokenize_expansion(node, &node->next, p, end - p, rules);
-                       if (slen <= 0) {
+                       if (xlat_tokenize_expansion(ctx, &node, flags, in, ar_rules) < 0) {
+                       error:
                                talloc_free(node);
-                               return slen - (p - in);
-                       }
-                       p += slen;
-
-                       fr_assert(node->next != NULL);
-
-                       /*
-                        *      Short-circuit the recursive call.
-                        *      This saves another function call and
-                        *      memory allocation.
-                        */
-                       if (!*p || (p >= end)) break;
+                               fr_cursor_head(&cursor);
+                               fr_cursor_free_list(&cursor);
 
-                       /*
-                        *      "foo %{User-Name} bar"
-                        *      LITERAL         "foo "
-                        *      EXPANSION       User-Name
-                        *      LITERAL         " bar"
-                        */
-                       slen = xlat_tokenize_literal(node->next, &(node->next->next), p, end - p, brace, rules);
-                       fr_assert(slen != 0);
-                       if (slen < 0) {
-                               talloc_free(node);
-                               return slen - (p - in);
+                               /*
+                                *      Free our temporary array of terminals
+                                */
+                               if (tokens != &expansions) talloc_free(tokens);
+                               return -1;
                        }
-
-                       brace = false; /* it was found above, or else the above code errored out */
-                       p += slen;
-                       break;  /* stop processing the string */
+                       fr_cursor_insert(&cursor, node);
+                       node = NULL;
+                       continue;
                }
 
                /*
-                *      Check for valid single-character expansions.
+                *      %[a-z] - A one letter expansion
                 */
-               if (p[0] == '%') {
-                       ssize_t         slen;
-                       xlat_exp_t      *next;
-
-                       if (!p[1] || !strchr("%}cdlmnsetCDGHIMSTYv", p[1])) {
-                       invalid_variable:
-                               talloc_free(node);
-                               fr_strerror_printf("Invalid variable expansion");
-                               p++;
-                               return -(p - in);
+               if (fr_sbuff_next_if_char(in, '%') && fr_sbuff_is_alpha(in)) {
+                       XLAT_DEBUG("ONE-LETTER <-- %pV",
+                                  fr_box_strvalue_len(str, talloc_array_length(str) - 1));
+
+                       if (len == 0) {
+                               talloc_free_children(node);     /* re-use empty nodes */
+                       } else {
+                               node = xlat_exp_alloc_null(ctx);
                        }
 
-                       next = talloc_zero(node, xlat_exp_t);
-                       next->len = 1;
+                       fr_sbuff_out_abstrncpy(node, &str, in, 1);
+                       xlat_exp_set_type(node, XLAT_ONE_LETTER);
+                       xlat_exp_set_name_buffer_shallow(node, str);
 
-                       switch (p[1]) {
-                       case '%':
-                       case '}':
-                               next->fmt = talloc_bstrndup(next, p + 1, 1);
-
-                               XLAT_DEBUG("LITERAL-ESCAPED <-- %s", next->fmt);
-                               next->type = XLAT_LITERAL;
-                               break;
+                       node->flags.needs_async = false; /* literals are always true */
+                       xlat_flags_merge(flags, &node->flags);
+                       fr_cursor_insert(&cursor, node);
+                       node = NULL;
+                       continue;
+               }
 
-                       default:
-                               next->fmt = p + 1;
+               /*
+                *      We were told to look for a brace, but we ran off of
+                *      the end of the string before we found one.
+                */
+               if (brace) {
+                       if (len == 0) TALLOC_FREE(node); /* Free the empty node */
 
-                               XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
-                               next->type = XLAT_ONE_LETTER;
-                               break;
+                       if (!fr_sbuff_is_char(in, '}')) {
+                               fr_strerror_printf("Missing closing brace");
+                               goto error;
                        }
-
-                       node->next = next;
-                       p += 2;
-
-                       if (!*p || (p >= end)) break;
-
-                       /*
-                        *      And recurse.
+               /*
+                *      We're parsing the string *containing* the xlat
+                *      expansions.
+                */
+               } else {
+                       /*      If we have an empty node, finish building it and
+                        *      emit it.
+                        *
+                        *      We're about to return, and it's a useful
+                        *      indication to the caller that this wasn't a parse
+                        *      error but just an empty string.
                         */
-                       slen = xlat_tokenize_literal(node->next, &(node->next->next), p, end - p, brace, rules);
-                       fr_assert(slen != 0);
-                       if (slen < 0) {
-                               talloc_free(node);
-                               return slen - (p - in);
-                       }
+                       if (len == 0) {
+                               /*
+                                *      This isn't the only node in the sequence
+                                *      don't emit an empty trailing literal.
+                                */
+                               if (*head) {
+                                       talloc_free(node);
+                                       break;
+                               }
 
-                       brace = false; /* it was found above, or else the above code errored out */
-                       p += slen;
-                       break;  /* stop processing the string */
-               }
+                               xlat_exp_set_type(node, XLAT_LITERAL);
+                               xlat_exp_set_name_buffer_shallow(node, str);
 
-               /*
-                *      If required, eat the brace.
-                */
-               if (brace && (*p == '}')) {
-                       brace = false;
-                       p++;
-                       break;
+                               XLAT_DEBUG("LITERAL <-- (empty)");
+                               node->flags.needs_async = false; /* literals are always true */
+                               xlat_flags_merge(flags, &node->flags);
+                               fr_cursor_insert(&cursor, node);
+                       }
                }
-
-               p++;
-               node->len++;
+               break;
        }
 
        /*
-        *      We were told to look for a brace, but we ran off of
-        *      the end of the string before we found one.
+        *      Free our temporary array of terminals
         */
-       if (brace) {
-               fr_strerror_printf("Missing closing brace at end of string");
-               talloc_free(node);
-               return -(p - in);
-       }
+       if (tokens != &expansions) talloc_free(tokens);
 
-       /*
-        *      Squash zero-width literals
-        */
-       if (node->len <= 0) {
-               (void) talloc_steal(ctx, node->next);
-               *head = node->next;
-               talloc_free(node);
-               return p - in;
-       }
-
-       node->async_safe = true; /* literals are always true */
-       *head = node;
-
-       /*
-        *      Shrink the buffer to the right size
-        */
-       MEM(start = talloc_bstr_realloc(ctx, start, node->len));
-       node->fmt = start;
-
-       return p - in;
+       return 0;
 }
 
+static fr_table_num_sorted_t const xlat_quote_table[] = {
+       { L("\""),      T_DOUBLE_QUOTED_STRING  },      /* Don't re-order, backslash throws off ordering */
+       { L("'"),       T_SINGLE_QUOTED_STRING  },
+       { L("`"),       T_BACK_QUOTED_STRING    }
+};
+static size_t xlat_quote_table_len = NUM_ELEMENTS(xlat_quote_table);
 
-/** skip xlat expansions, included embedded strings and nested xlats.
- *
- */
-static ssize_t skip_xlat(char const *start, char const *end)
-{
-       char const *p = start + 2;
-       ssize_t slen;
-
-       while (p < end) {
-               if (*p == '}') {
-                       p++;
-                       return p - start;
-               }
-
-               if ((*p == '"') || (*p == '\'')) {
-                       slen = fr_skip_string(p, end);
-                       if (slen < 0) return slen - (p - start);
-
-                       p += slen;
-                       continue;
-               }
-
-               if ((*p == '%') && ((p + 1) < end) && (p[1] == '{')) {
-                       slen = skip_xlat(p, end);
-                       if (slen < 0) return slen - (p - start);
-                       p += slen;
-                       continue;
-               }
-
-               p++;
-       }
-
-       return -(p - start);
-}
-
-/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
- *
- * @param[in] ctx      to allocate dynamic buffers in.
- * @param[out] head    the head of the xlat list / tree structure.
- * @param[in] in       the format string to expand.
- * @param[in] inlen    the length of the input string.  If the string is \0 terminated, inlen may be -1.
- * @param[in] rules    controlling how attribute references are parsed.
- * @return
- *     - <=0 on error.
- *     - >0  on success which is the number of characters parsed.
- */
-ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
-                          tmpl_rules_t const *rules)
-{
-       char const *p, *end;
-       ssize_t slen;
-       xlat_exp_t *node, *my_head, **last;
-       TALLOC_CTX *my_ctx;
-       int count = 0;
-
-       p = in;
-       end = in + inlen;
-
-       my_head = NULL;
-       last = &my_head;
-       my_ctx = ctx;
-
-       while (p < end) {
-               /*
-                *      Skip spaces between command-line arguments.
-                */
-               if (isspace((int) *p)) {
-                       while ((p < end) && isspace((int) *p)) p++;
-
-                       continue;
-               }
-
-               *last = node = talloc_zero(my_ctx, xlat_exp_t);
-               node->fmt = NULL;
-               node->len = 0;
-               node->type = XLAT_CHILD;
-               count++;
-
-               /*
-                *      Back-tick quoted strings are forbidden.
-                */
-               if (*p == '`') {
-                       fr_strerror_printf("Unexpected `...` string");
-                       talloc_free(my_head);
-                       return -(p - in);
-               }
-
-               /*
-                *      Look for quoted strings.
-                */
-               if ((*p == '"') || (*p == '\'')) {
-                       slen = fr_skip_string(p, end);
-                       if (slen < 0) return slen - (p - in);
-
-               } else {
-                       char const *q = p;
-
-                       /*
-                        *      Bare words can contain xlat expansions.
-                        *
-                        *      --foo=%{Bar}
-                        */
-                       while ((q < end) && !isspace((int) *q)) {
-                               /*
-                                *      Things like: echo foo"bar"
-                                *      are forbidden.
-                                *
-                                *      The Unix shell typically allows this
-                                *      kind of nonsense, but there's no reason
-                                *      for us to do so.
-                                */
-                               if ((*q == '`') || (*q == '"') || (*q == '\'')) {
-                                       fr_strerror_printf("Unexpected quoted string");
-                                       talloc_free(my_head);
-                                       return -(q - in);
-                               }
-
-                               if (*q == '%') {
-                                       if (q[1] != '{') {
-                                               slen = 2;
-                                       } else {
-                                               slen = skip_xlat(q, end);
-                                               if (slen < 0) return slen - (q - in);
-                                       }
-
-                                       q += slen;
-                                       continue;
-                               }
-
-                               q++;
-                       }
-                       slen = q - p;
-               }
-
-               /*
-                *      Tokenize this literal as a child.
-                */
-               slen = xlat_tokenize_literal(node, &node->child, p, slen, false, rules);
-               if (slen <= 0) {
-                       talloc_free(my_head);
-                       return slen - (p - in);
-               }
-
-               /*
-                *      Arguments MUST be separated by spaces.
-                */
-               if (((p + slen) < end) && !isspace((int) p[slen])) {
-                       talloc_free(my_head);
-                       fr_strerror_printf("Unexpected text after argument");
-                       p += slen;
-                       return -(p - in);
-               }
-
-               /*
-                *      Remember how much of the current string we allocated.
-                */
-               node->fmt = talloc_bstrndup(node, p, slen);
-               node->len = slen;
-               node->async_safe = node->child->async_safe;
-               p += slen;
-
-               /*
-                *      Parent the next node from the previous one.
-                *      This lets us free everything in one swell foop
-                *      on error.
-                */
-               my_ctx = node;
-               last = &(node->next);
-       }
-
-       if (!my_head) {
-               fr_strerror_printf("Empty string is invalid");
-               return -1;
-       }
-
-       *head = my_head;
-       my_head->count = count;
-       return p - in;
-}
-
-static void xlat_tokenize_debug(REQUEST *request, xlat_exp_t const *node)
+void xlat_debug(xlat_exp_t const *node)
 {
        fr_assert(node != NULL);
-
-       RINDENT();
        while (node) {
                switch (node->type) {
                case XLAT_LITERAL:
-                       RDEBUG3("literal --> %s", node->fmt);
+                       INFO("literal --> %s", node->fmt);
                        break;
 
-               case XLAT_CHILD:
-                       RDEBUG3("child --> %s", node->fmt);
-                       RDEBUG3("{");
-                       RINDENT();
-                       xlat_tokenize_debug(request, node->child);
-                       REXDENT();
-                       RDEBUG3("}");
+               case XLAT_GROUP:
+                       INFO("child --> %s", node->fmt);
+                       INFO("{");
+                       xlat_debug(node->child);
+                       INFO("}");
                        break;
 
                case XLAT_ONE_LETTER:
-                       RDEBUG3("percent --> %c", node->fmt[0]);
+                       INFO("percent --> %c", node->fmt[0]);
                        break;
 
                case XLAT_ATTRIBUTE:
                        fr_assert(tmpl_da(node->attr) != NULL);
-                       RDEBUG3("attribute --> %s", tmpl_da(node->attr)->name);
+                       INFO("attribute --> %s", tmpl_da(node->attr)->name);
                        fr_assert(node->child == NULL);
-                       if ((tmpl_tag(node->attr) != TAG_ANY) || (tmpl_num(node->attr) != NUM_ANY)) {
-                               RDEBUG3("{");
-
-                               RINDENT();
-                               RDEBUG3("ref  %d", tmpl_request(node->attr));
-                               RDEBUG3("list %d", tmpl_list(node->attr));
-
-                               if (tmpl_tag(node->attr) != TAG_ANY) {
-                                       RDEBUG3("tag %d", tmpl_tag(node->attr));
-                               }
+                       if (tmpl_num(node->attr) != NUM_ANY) {
+                               INFO("{");
+                               INFO("ref  %d", tmpl_request(node->attr));
+                               INFO("list %d", tmpl_list(node->attr));
                                if (tmpl_num(node->attr) != NUM_ANY) {
                                        if (tmpl_num(node->attr) == NUM_COUNT) {
-                                               RDEBUG3("[#]");
+                                               INFO("[#]");
                                        } else if (tmpl_num(node->attr) == NUM_ALL) {
-                                               RDEBUG3("[*]");
+                                               INFO("[*]");
                                        } else {
-                                               RDEBUG3("[%d]", tmpl_num(node->attr));
+                                               INFO("[%d]", tmpl_num(node->attr));
                                        }
                                }
-                               REXDENT();
-                               RDEBUG3("}");
+                               INFO("}");
                        }
                        break;
 
                case XLAT_VIRTUAL:
                        fr_assert(node->fmt != NULL);
-                       RDEBUG3("virtual --> %s", node->fmt);
+                       INFO("virtual --> %s", node->fmt);
+                       break;
+
+               case XLAT_VIRTUAL_UNRESOLVED:
+                       fr_assert(node->fmt != NULL);
+                       INFO("virtual-unresolved --> %s", node->fmt);
                        break;
 
                case XLAT_FUNC:
-                       fr_assert(node->xlat != NULL);
-                       RDEBUG3("xlat --> %s", node->xlat->name);
+                       fr_assert(node->call.func != NULL);
+                       INFO("xlat --> %s", node->call.func->name);
+                       if (node->child) {
+                               INFO("{");
+                               xlat_debug(node->child);
+                               INFO("}");
+                       }
+                       break;
+
+               case XLAT_FUNC_UNRESOLVED:
+                       INFO("xlat-unresolved --> %s", node->fmt);
                        if (node->child) {
-                               RDEBUG3("{");
-                               xlat_tokenize_debug(request, node->child);
-                               RDEBUG3("}");
+                               INFO("{");
+                               xlat_debug(node->child);
+                               INFO("}");
                        }
                        break;
 
 #ifdef HAVE_REGEX
                case XLAT_REGEX:
-                       RDEBUG3("regex-var --> %d", node->regex_index);
+                       INFO("regex-var --> %d", node->regex_index);
                        break;
 #endif
 
                case XLAT_ALTERNATE:
                        DEBUG("XLAT-IF {");
-                       xlat_tokenize_debug(request, node->child);
+                       xlat_debug(node->child);
                        DEBUG("}");
                        DEBUG("XLAT-ELSE {");
-                       xlat_tokenize_debug(request, node->alternate);
+                       xlat_debug(node->alternate);
                        DEBUG("}");
                        break;
+
+               case XLAT_INVALID:
+                       DEBUG("XLAT-INVALID");
+                       break;
                }
                node = node->next;
        }
-       REXDENT();
 }
 
-size_t xlat_snprint(char *out, size_t outlen, xlat_exp_t const *node)
+/** Reconstitute an xlat expression from its constituent nodes
+ *
+ * @param[in] out      Where to write the output string.
+ * @param[in] head     First node to print.
+ * @param[in] e_rules  Specifying how to escape literal values.
+ */
+ssize_t xlat_print(fr_sbuff_t *out, xlat_exp_t const *head, fr_sbuff_escape_rules_t const *e_rules)
 {
-       char *p, *end;
-
-       if (!node || (outlen <= 2)) {
-               *out = '\0';
-               return 0;
-       }
-
-       p = out;
-       end = out + outlen;
+       ssize_t                 slen;
+       size_t                  at_in = fr_sbuff_used_total(out);
+       xlat_exp_t const        *node = head;
 
-#define CHECK_SPACE(_p, _end) if (_p >= _end) goto oob
+       if (!node) return 0;
 
        while (node) {
-               if ((node->type == XLAT_LITERAL) || (node->type == XLAT_CHILD)) {
-                       p += strlcpy(p, node->fmt, end - p);
-                       CHECK_SPACE(p, end);
+               switch (node->type) {
+               case XLAT_GROUP:
+                       if (node->quote != T_BARE_WORD) FR_SBUFF_IN_CHAR_RETURN(out, fr_token_quote[node->quote]);
+                       xlat_print(out, node->child, fr_value_escape_by_quote[node->quote]);
+                       if (node->quote != T_BARE_WORD) FR_SBUFF_IN_CHAR_RETURN(out, fr_token_quote[node->quote]);
+                       if (node->next) FR_SBUFF_IN_CHAR_RETURN(out, ' ');      /* Add ' ' between args */
                        goto next;
-               }
 
-               if (node->type == XLAT_ONE_LETTER) {
-                       *(p++) = '%';
-                       if (p >= end) {
-                       oob:
-                               out[outlen - 1] = '\0';
-                               return outlen + 1;
-                       }
+               case XLAT_LITERAL:
+                       FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(out, node->fmt, e_rules);
+                       goto next;
 
-                       *(p++) = node->fmt[0];
-                       CHECK_SPACE(p, end);
+               case XLAT_ONE_LETTER:
+                       FR_SBUFF_IN_CHAR_RETURN(out, '%', node->fmt[0]);
                        goto next;
-               }
 
-               *(p++) = '%';
-               CHECK_SPACE(p, end);
-               *(p++) = '{';
-               CHECK_SPACE(p, end);
+               default:
+                       break;
+               }
 
+               FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, "%{");
                switch (node->type) {
                case XLAT_ATTRIBUTE:
-               {
-                       size_t need;
-
-                       p += tmpl_snprint_attr_str(&need, p, end - p, node->attr);
-                       if (need > 0) goto oob;
-               }
+                       slen = tmpl_attr_print(out, node->attr, TMPL_ATTR_REF_PREFIX_NO);
+                       if (slen < 0) {
+                       error:
+                               return slen;
+                       }
                        break;
 #ifdef HAVE_REGEX
                case XLAT_REGEX:
-                       p += snprintf(p, end - p, "%i", node->regex_index);
-                       CHECK_SPACE(p, end);
+                       FR_SBUFF_IN_SPRINTF_RETURN(out, "%i", node->regex_index);
                        break;
 #endif
                case XLAT_VIRTUAL:
-                       p += strlcpy(p, node->fmt, end - p);
-                       CHECK_SPACE(p, end);
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
+                       break;
+
+               case XLAT_VIRTUAL_UNRESOLVED:
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->fmt);
                        break;
 
                case XLAT_FUNC:
-                       p += strlcpy(p, node->xlat->name, end - p);
-                       CHECK_SPACE(p, end);
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
+                       FR_SBUFF_IN_CHAR_RETURN(out, ':');
+
+                       if (node->child) {
+                               slen = xlat_print(out, node->child, &xlat_escape);
+                               if (slen < 0) goto error;
+                       }
+                       break;
 
-                       *(p++) = ':';
-                       CHECK_SPACE(p, end);
+               case XLAT_FUNC_UNRESOLVED:
+                       FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->fmt);
+                       FR_SBUFF_IN_CHAR_RETURN(out, ':');
 
                        if (node->child) {
-                               p += xlat_snprint(p, end - p, node->child);
-                               CHECK_SPACE(p, end);
+                               slen = xlat_print(out, node->child, &xlat_escape);
+                               if (slen < 0) goto error;
                        }
                        break;
 
                case XLAT_ALTERNATE:
-                       p += xlat_snprint(p, end - p, node->child);
-                       CHECK_SPACE(p, end);
+                       slen = xlat_print(out, node->child, &xlat_escape);
+                       if (slen < 0) goto error;
 
-                       *(p++) = ':';
-                       CHECK_SPACE(p, end);
-                       *(p++) = '-';
-                       CHECK_SPACE(p, end);
+                       FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, ":-");
+                       slen = xlat_print(out, node->alternate, &xlat_escape);
+                       if (slen < 0) goto error;
+                       break;
 
-                       p += xlat_snprint(p, end - p, node->alternate);
-                       CHECK_SPACE(p, end);
+                       fr_assert_fail(NULL);
                        break;
 
-               default:
+               case XLAT_INVALID:
+               case XLAT_LITERAL:
+               case XLAT_ONE_LETTER:
+               case XLAT_GROUP:
                        fr_assert_fail(NULL);
                        break;
                }
-
-               *(p++) = '}';
-               CHECK_SPACE(p, end);
-
+               FR_SBUFF_IN_CHAR_RETURN(out, '}');
        next:
                node = node->next;
        }
 
-       *p = '\0';
-
-       return p - out;
+       return fr_sbuff_used_total(out) - at_in;
 }
 
 /** Tokenize an xlat expansion at runtime
@@ -1117,83 +1088,252 @@ size_t xlat_snprint(char *out, size_t outlen, xlat_exp_t const *node)
  *
  * @param[in] ctx      to allocate dynamic buffers in.
  * @param[out] head    the head of the xlat list / tree structure.
+ * @param[in,out] flags        that control evaluation and parsing.
  * @param[in] request  the input request.  Memory will be attached here.
- * @param[in] fmt      the format string to expand.
- * @param[in] rules    controlling how attribute references are parsed.
+ * @param[in] in       the format string to expand.
+ * @param[in] p_rules  from the encompassing grammar.
+ * @param[in] ar_rules controlling how attribute references are parsed.
  * @return
- *     - <= -1 on error.  Return value is negative offset of where parsing
- *       error occured.
- *     - >= 0 on success.  The number of bytes parsed.
+ *     - >0 on success.
+ *     - 0 and *head == NULL - Parse failure on first char.
+ *     - 0 and *head != NULL - Zero length expansion
+ *     - <0 the negative offset of the parse failure.
  */
-ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, REQUEST *request,
-                               char const *fmt, tmpl_rules_t const *rules)
+ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, REQUEST *request,
+                               fr_sbuff_t *in,
+                               fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
 {
-       ssize_t         slen;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       xlat_flags_t    tmp_flags = {};
+
+       if (!flags) flags = &tmp_flags;
 
        *head = NULL;
 
        fr_strerror();  /* Clear error buffer */
-       slen = xlat_tokenize_literal(request, head, fmt, strlen(fmt), false, rules);
+       if (xlat_tokenize_literal(request, head, flags,
+                                 &our_in, false, p_rules, ar_rules) < 0) return -fr_sbuff_used(&our_in);
 
        /*
         *      Zero length expansion, return a zero length node.
         */
-       if (slen == 0) {
-               MEM(*head = talloc_zero(ctx, xlat_exp_t));
-               (*head)->async_safe = true;
+       if (fr_sbuff_used(&our_in) == 0) *head = xlat_exp_alloc(ctx, XLAT_LITERAL, "", 0);
+
+       if (RDEBUG_ENABLED3) {
+               INFO("%pV", fr_box_strvalue_len(fr_sbuff_start(&our_in), fr_sbuff_used(&our_in)));
+               INFO("Parsed xlat tree:");
+               xlat_debug(*head);
        }
 
        /*
-        *      Output something like:
-        *
-        *      "format string"
-        *      "       ^ error was here"
+        *      Create ephemeral instance data for the xlat
         */
-       if (slen < 0) {
-               return slen;
+       if (xlat_instantiate_ephemeral(*head) < 0) {
+               fr_strerror_printf("Failed performing ephemeral instantiation for xlat");
+               TALLOC_FREE(*head);
+               return 0;
        }
 
-       if (*head && RDEBUG_ENABLED3) {
-               RDEBUG3("%s", fmt);
-               RDEBUG3("Parsed xlat tree:");
-               xlat_tokenize_debug(request, *head);
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
+ *
+ * @param[in] ctx              to allocate nodes in.  Note: All nodes will be
+ *                             allocated in the same ctx.  This is to allow
+ *                             manipulation by xlat instantiation functions
+ *                             later.
+ * @param[out] head            the head of the xlat list / tree structure.
+ * @param[out] flags           Populated with parameters that control xlat
+ *                             evaluation and multi-pass parsing.
+ * @param[in] in               the format string to expand.
+ * @param[in] ar_rules         controlling how attribute references are parsed.
+ * @return
+ *     - <=0 on error.
+ *     - >0  on success which is the number of characters parsed.
+ */
+ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                          fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
+{
+       fr_sbuff_t                      our_in = FR_SBUFF_NO_ADVANCE(in);
+       ssize_t                         slen;
+       fr_cursor_t                     cursor;
+       fr_sbuff_marker_t               m;
+       fr_sbuff_parse_rules_t const    *our_p_rules;   /* Bareword parse rules */
+       xlat_flags_t                    tmp_flags = {};
+
+       if (!flags) flags = &tmp_flags;
+
+       *head = NULL;
+
+       if (p_rules && p_rules->terminals) {
+               our_p_rules = &(fr_sbuff_parse_rules_t){
+                       .terminals = fr_sbuff_terminals_amerge(NULL, p_rules->terminals,
+                                                              tmpl_parse_rules_bareword_quoted.terminals),
+                       .escapes = tmpl_parse_rules_bareword_quoted.escapes
+               };
+       } else {
+               our_p_rules = &tmpl_parse_rules_bareword_quoted;
        }
 
        /*
-        *      Create ephemeral instance data for the xlat
+        *      skip spaces at the beginning as we
+        *      don't want them to become a whitespace
+        *      literal.
         */
-       if (xlat_instantiate_ephemeral(*head) < 0) {
-               talloc_free(*head);
+       fr_sbuff_adv_past_whitespace(in, SIZE_MAX);
+       fr_sbuff_marker(&m, &our_in);
 
-               fr_strerror_printf("Failed performing ephemeral instantiation for xlat");
-               return -1;
+       fr_cursor_init(&cursor, head);
+       while (fr_sbuff_extend(&our_in)) {
+               xlat_exp_t      *node = NULL;
+               fr_token_t      quote;
+               char            *fmt;
+               size_t          len;
+
+               fr_sbuff_set(&m, &our_in);      /* Record start of argument */
+
+               fr_sbuff_out_by_longest_prefix(&slen, &quote, xlat_quote_table, &our_in, T_BARE_WORD);
+
+               /*
+                *      Alloc a new node to hold the child nodes
+                *      that make up the argument.
+                */
+               node = xlat_exp_alloc_null(ctx);
+               xlat_exp_set_type(node, XLAT_GROUP);
+               node->quote = quote;
+
+               switch (quote) {
+               /*
+                *      Barewords --may-contain=%{expansions}
+                */
+               case T_BARE_WORD:
+                       if (xlat_tokenize_literal(node, &node->child, &node->flags, &our_in,
+                                                 false, our_p_rules, ar_rules) < 0) {
+                       error:
+                               if (our_p_rules != &tmpl_parse_rules_bareword_quoted) {
+                                       talloc_const_free(our_p_rules->terminals);
+                               }
+                               talloc_free(node);
+                               fr_cursor_head(&cursor);
+                               fr_cursor_free_list(&cursor);
+
+                               return -fr_sbuff_used(&our_in); /* error */
+                       }
+                       xlat_flags_merge(flags, &node->flags);
+                       break;
+
+               /*
+                *      "Double quoted strings may contain %{expansions}"
+                */
+               case T_DOUBLE_QUOTED_STRING:
+                       if (xlat_tokenize_literal(node, &node->child, &node->flags, &our_in,
+                                                 false, &tmpl_parse_rules_double_quoted, ar_rules) < 0) goto error;
+                       xlat_flags_merge(flags, &node->flags);
+                       break;
+
+               /*
+                *      'Single quoted strings get parsed as literals'
+                */
+               case T_SINGLE_QUOTED_STRING:
+               {
+                       char            *str;
+
+                       node->child = xlat_exp_alloc_null(node);
+                       xlat_exp_set_type(node->child, XLAT_LITERAL);
+                       node->flags.needs_async = false;        /* Literals are always needs_async */
+
+                       slen = fr_sbuff_out_aunescape_until(node->child, &str, &our_in, SIZE_MAX,
+                                                           tmpl_parse_rules_single_quoted.terminals,
+                                                           tmpl_parse_rules_single_quoted.escapes);
+                       if (slen < 0) goto error;
+                       xlat_exp_set_name_buffer_shallow(node->child, str);
+                       xlat_flags_merge(flags, &node->flags);
+               }
+                       break;
+
+               /*
+                *      `back quoted strings aren't supported`
+                */
+               case T_BACK_QUOTED_STRING:
+                       fr_strerror_printf("Unexpected `...` string");
+                       goto error;
+
+               default:
+                       fr_assert(0);
+                       break;
+               }
+
+               if ((quote != T_BARE_WORD) && !fr_sbuff_next_if_char(&our_in, fr_token_quote[quote])) { /* Quoting */
+                       fr_strerror_printf("Unterminated string");
+                       fr_sbuff_set(&our_in, &m);
+                       goto error;
+               }
+
+               fmt = talloc_bstrndup(node, fr_sbuff_current(&m), fr_sbuff_behind(&m));
+               xlat_exp_set_name_buffer_shallow(node, fmt);
+
+               xlat_flags_merge(flags, &node->flags);
+               fr_cursor_insert(&cursor, node);
+               node = NULL;
+
+               /*
+                *      If we're not and the end of the string
+                *      and there's no whitespace between tokens
+                *      then error.
+                */
+               fr_sbuff_set(&m, &our_in);
+               len = fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+
+               /*
+                *      Check to see if we have a terminal char
+                */
+               if ((p_rules && p_rules->terminals) && fr_sbuff_is_terminal(&our_in, p_rules->terminals)) break;
+
+               /*
+                *      Otherwise, if we can extend, and found
+                *      no additional whitespace, it means two
+                *      arguments were smushed together.
+                */
+               if (fr_sbuff_extend(&our_in) && (len == 0)) {
+                       fr_strerror_printf("Unexpected text after argument");
+                       goto error;
+               }
        }
 
-       return slen;
+       if (our_p_rules != &tmpl_parse_rules_bareword_quoted) talloc_const_free(our_p_rules->terminals);
+
+       return fr_sbuff_set(in, &our_in);
 }
 
 /** Tokenize an xlat expansion
  *
  * @param[in] ctx      to allocate dynamic buffers in.
  * @param[out] head    the head of the xlat list / tree structure.
+ * @param[in,out] flags        that control evaluation and parsing.
  * @param[in] in       the format string to expand.
- * @param[in] inlen    the length of the input string.  If the string is \0 terminated, inlen may be -1.
- * @param[in] rules    controlling how attribute references are parsed.
+ * @param[in] p_rules  controlling how the string containing the xlat
+ *                     expansions should be parsed.
+ * @param[in] ar_rules controlling how attribute references are parsed.
  * @return
- *     - <0 on error.
- *     - 0 on success.
+ *     - >0 on success.
+ *     - 0 and *head == NULL - Parse failure on first char.
+ *     - 0 and *head != NULL - Zero length expansion
+ *     - < 0 the negative offset of the parse failure.
  */
-ssize_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, ssize_t inlen, tmpl_rules_t const *rules)
+ssize_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+                     fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
 {
-       int ret;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
+       xlat_flags_t    tmp_flags = {};
 
+       if (!flags) flags = &tmp_flags;
        *head = NULL;
 
-       if (inlen < 0) inlen = strlen(in);
-
        fr_strerror();  /* Clear error buffer */
-       ret = xlat_tokenize_literal(ctx, head, in, inlen, false, rules);
-       if (ret < 0) return ret;
+
+       if (xlat_tokenize_literal(ctx, head, flags,
+                                 &our_in, false, p_rules, ar_rules) < 0) return -fr_sbuff_used(&our_in);
 
        /*
         *      Add nodes that need to be bootstrapped to
@@ -1201,9 +1341,326 @@ ssize_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, ssize_
         */
        if (xlat_bootstrap(*head) < 0) {
                TALLOC_FREE(*head);
-               return -1;
+               return 0;
+       }
+
+       return fr_sbuff_set(in, &our_in);
+}
+
+/** Check to see if the expansion consists entirely of literal elements
+ *
+ * @param[in] head     to check.
+ * @return
+ *     - true if expansion contains only literal elements.
+ *     - false if expansion contains expandable elements.
+ */
+bool xlat_is_literal(xlat_exp_t const *head)
+{
+       xlat_exp_t const *node;
+
+       for (node = head;
+            node;
+            node = node->next) {
+               if (node->type != XLAT_LITERAL) return false;
+       }
+
+       return true;
+}
+
+/** Convert an xlat node to an unescaped literal string and free the original node
+ *
+ * @param[in] ctx      to allocate the new string in.
+ * @param[out] str     a duplicate of the node's fmt string.
+ * @param[in,out] head to convert.
+ * @return
+ *     - true  the tree consists of a single literal node which was converted.
+ *      - false the tree was more complex than a single literal, op was a noop.
+ */
+bool xlat_to_literal(TALLOC_CTX *ctx, char **str, xlat_exp_t **head)
+{
+       xlat_exp_t              *node;
+       fr_sbuff_t              out;
+       fr_sbuff_uctx_talloc_t  tctx;
+       size_t                  len = 0;
+
+       if (!*head) return false;
+
+       /*
+        *      Instantiation functions may chop
+        *      up the node list into multiple
+        *      literals, so we need to walk the
+        *      list until we find a non-literal.
+        */
+       for (node = *head; node; node = node->next) {
+               if (!xlat_is_literal(node)) return false;
+               len += talloc_array_length(node->fmt) - 1;
+       }
+
+       fr_sbuff_init_talloc(ctx, &out, &tctx, len, SIZE_MAX);
+       for (node = *head; node; node = node->next) fr_sbuff_in_bstrcpy_buffer(&out, node->fmt);
+
+       *str = fr_sbuff_buff(&out);     /* No need to trim, should be the correct length */
+
+       return true;
+}
+
+/** Walk over an xlat tree recursively, resolving any unresolved functions or references
+ *
+ * @param[in,out] head         of xlat tree to resolve.
+ * @param[in,out] flags                that control evaluation and parsing.
+ * @param[in] allow_unresolved Don't error out if we can't resolve a function or attribute.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int xlat_resolve(xlat_exp_t **head, xlat_flags_t *flags, bool allow_unresolved)
+{
+       xlat_exp_t      *node;
+       xlat_flags_t    our_flags;
+
+       if (!flags->needs_resolving) return 0;                  /* Already done */
+
+       our_flags = *flags;
+       our_flags.needs_resolving = false;                      /* We flip this if not all resolutions are successful */
+
+       for (node = *head; node; node = node->next) {
+               if (!node->flags.needs_resolving) continue;     /* This node and non of its children need resolving */
+
+               switch (node->type) {
+               case XLAT_GROUP:
+                       return xlat_resolve(&node->child, &node->flags, allow_unresolved);
+
+               /*
+                *      Alternate expansion a || b
+                *
+                *      Do resolution for a OR b
+                */
+               case XLAT_ALTERNATE:
+               {
+                       xlat_flags_t    child_flags = node->flags, alt_flags = node->flags;
+
+                       if ((xlat_resolve(&node->child, &child_flags, allow_unresolved) < 0) ||
+                           (xlat_resolve(&node->alternate, &alt_flags, allow_unresolved) < 0)) return -1;
+
+                       xlat_flags_merge(&child_flags, &alt_flags);
+                       node->flags = child_flags;
+               }
+                       break;
+
+               /*
+                *      A resolved function with unresolved args
+                */
+               case XLAT_FUNC:
+                       if (xlat_resolve(&node->child, &node->flags, allow_unresolved) < 0) return -1;
+                       xlat_flags_merge(&our_flags, &node->flags);
+                       break;
+
+               /*
+                *      An unresolved function.
+                */
+               case XLAT_FUNC_UNRESOLVED:
+               {
+                       xlat_t          *func;
+                       xlat_flags_t    child_flags = node->flags;
+
+
+                       /*
+                        *      We can't tell if it's just the function
+                        *      that needs resolving or its children too.
+                        */
+                       if (xlat_resolve(&node->child, &child_flags, allow_unresolved) < 0) return -1;
+
+                       /*
+                        *      Try and find the function
+                        */
+                       func = xlat_func_find(node->fmt, talloc_array_length(node->fmt) - 1);
+                       if (!func) {
+                               /*
+                                *      FIXME - Produce proper error with marker
+                                */
+                               if (!allow_unresolved) {
+                                       fr_strerror_printf("Failed resolving function \"%pV\"",
+                                                          fr_box_strvalue_buffer(node->fmt));
+                                       return -1;
+                               }
+                               our_flags.needs_resolving = true;       /* Still unresolved nodes */
+                               break;
+                       }
+
+                       xlat_exp_set_type(node, XLAT_FUNC);
+                       node->call.func = func;
+
+                       /*
+                        *      Reset node flags
+                        */
+                       node->flags = (xlat_flags_t){ .needs_async = func->needs_async };
+
+                       /*
+                        *      Merge the result of trying to resolve
+                        *      the child nodes.
+                        */
+                       xlat_flags_merge(&node->flags, &child_flags);
+
+                       /*
+                        *      Add the freshly resolved function
+                        *      to the bootstrap tree.
+                        */
+                       if (xlat_bootstrap_func(node) < 0) return -1;
+               }
+                       break;
+               /*
+                *      This covers unresolved attributes as well as
+                *      unresolved functions.
+                */
+               case XLAT_VIRTUAL_UNRESOLVED:
+               {
+                       xlat_t *func;
+
+                       fr_assert(node->attr->type == TMPL_TYPE_ATTR_UNRESOLVED);
+
+                       func = xlat_func_find(tmpl_attr_unresolved(node->attr), -1);
+                       if (func) {
+                               xlat_exp_set_type(node, XLAT_VIRTUAL);
+                               node->attr = node->attr;        /* Shift to the right location */
+                               node->call.func = func;
+
+                               /*
+                                *      Reset node flags
+                                */
+                               node->flags = (xlat_flags_t){ .needs_async = func->needs_async };
+                               break;
+                       }
+
+                       /*
+                        *      Try and resolve (in-place) as an attribute
+                        */
+                       if ((tmpl_resolve(node->attr) < 0) || (node->attr->type != TMPL_TYPE_ATTR)) {
+                               /*
+                                *      FIXME - Produce proper error with marker
+                                */
+                               if (!allow_unresolved) {
+                                       fr_strerror_printf_push("Failed resolving attribute in expansion %%{%s}",
+                                                               node->fmt);
+                                       return -1;
+                               }
+                               our_flags.needs_resolving = true;       /* Still unresolved nodes */
+                               break;
+                       }
+
+                       /*
+                        *      Just need to flip the type as the tmpl
+                        *      should already have been fixed up
+                        */
+                       xlat_exp_set_type(node, XLAT_ATTRIBUTE);
+
+                       /*
+                        *      Reset node flags
+                        */
+                       node->flags = (xlat_flags_t){ };
+               }
+                       break;
+
+               default:
+                       fr_assert(0);   /* Should not have been marked as unresolved */
+                       return -1;
+               }
+
+               xlat_flags_merge(&our_flags, &node->flags);
        }
 
-       return ret;
+       *flags = our_flags;     /* Update parent flags - not merge, replacement */
+
+       return 0;
+}
+
+
+/** Try to convert an xlat to a tmpl for efficiency
+ *
+ * @param ctx to allocate new tmpl_t in.
+ * @param node to convert.
+ * @return
+ *     - NULL if unable to convert (not necessarily error).
+ *     - A new #tmpl_t.
+ */
+tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
+{
+       tmpl_t *vpt;
+
+       if (node->next || (node->type != XLAT_ATTRIBUTE) || !tmpl_is_attr(node->attr)) return NULL;
+
+       /*
+        *   Concat means something completely different as an attribute reference
+        *   Count isn't implemented.
+        */
+       if ((tmpl_num(node->attr) == NUM_COUNT) || (tmpl_num(node->attr) == NUM_ALL)) return NULL;
+
+       vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, node->fmt, talloc_array_length(node->fmt) - 1);
+       if (!vpt) return NULL;
+
+       tmpl_attr_copy(vpt, node->attr);
+
+       TMPL_VERIFY(vpt);
+
+       return vpt;
 }
 
+/** Convert attr tmpl to an xlat for &attr[*]
+ *
+ * @param[in] ctx      to allocate new expansion in.
+ * @param[out] head    Where to write new xlat node.
+ * @param[out] flags   Where to write xlat resolution flags.
+ * @param[in,out] vpt_p        to convert to xlat expansion.
+ *                     Will be set to NULL on completion
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, tmpl_t **vpt_p)
+{
+       xlat_exp_t      *node;
+       xlat_t          *func;
+       tmpl_t          *vpt = *vpt_p;
+
+       if (!tmpl_is_attr(vpt) && !tmpl_is_attr_unresolved(vpt)) return 0;
+
+       /*
+        *      If it's a single attribute reference
+        *      see if it's actually a virtual attribute.
+        */
+       if (tmpl_attr_ref_count(vpt) == 1) {
+               if (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual) {
+                       func = xlat_func_find(tmpl_da(vpt)->name, -1);
+                       if (!func) {
+                       unresolved:
+                               node = xlat_exp_alloc(ctx, XLAT_VIRTUAL_UNRESOLVED, vpt->name, vpt->len);
+
+                               /*
+                                *      FIXME - Need a tmpl_copy function to
+                                *      the assignment of the tmpl to the new
+                                *      xlat expression
+                                */
+                               node->attr = talloc_move(node, vpt_p);
+                               node->flags = (xlat_flags_t) { .needs_resolving = true };
+                               *head = node;
+                               xlat_flags_merge(flags, &node->flags);
+                               return 0;
+                       }
+
+               virtual:
+                       node = xlat_exp_alloc(ctx, XLAT_VIRTUAL, vpt->name, vpt->len);
+                       node->attr = talloc_move(node, vpt_p);
+                       node->call.func = func;
+                       *head = node;
+                       node->flags = (xlat_flags_t) { .needs_async = func->needs_async };
+               } else if (tmpl_is_attr_unresolved(vpt)) {
+                       func = xlat_func_find(tmpl_attr_unresolved(vpt), -1);
+                       if (!func) goto unresolved;
+                       goto virtual;
+               }
+       }
+
+       node = xlat_exp_alloc(ctx, XLAT_ATTRIBUTE, vpt->name, vpt->len);
+       node->attr = talloc_move(node, vpt_p);
+
+       return 0;
+}
index 2ac952745775801ebaba7b8432089f369b89c155..96d26168953f00e974c92c7b0809caa119612cdb 100644 (file)
@@ -67,9 +67,16 @@ typedef struct fr_dict fr_dict_t;
  */
 typedef struct {
        unsigned int            is_root : 1;                    //!< Is root of a dictionary.
-       unsigned int            is_unknown : 1;                 //!< &Attr-1.2.3.4 taken from a packet
-       unsigned int            is_raw : 1;                     //!< &Attr-1.2.3.4 taken from the config,
-                                                               //!< and added (along with parents) to the dictionionaries
+
+       unsigned int            is_unknown : 1;                 //!< This dictionary attribute is ephemeral
+                                                               ///< and not part of the main dictionary.
+
+       unsigned int            is_raw : 1;                     //!< This dictionary attribute was constructed
+                                                               ///< from a known attribute to allow the user
+                                                               ///< to assign octets values directly.
+                                                               ///< See .is_unknown to determine if it is
+                                                               ///< ephemeral.
+
        unsigned int            internal : 1;                   //!< Internal attribute, should not be received
                                                                ///< in protocol packets, should not be encoded.
        unsigned int            has_tag : 1;                    //!< Tagged attribute.
index 330a509cc2a015ec56b3edb4d88d69f0507dd783..c7d87370c0a9beb966652ffc5c91b6040af920b0 100644 (file)
@@ -81,7 +81,7 @@ fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
        default:
                type = FR_TYPE_OCTETS;
                break;
-       }       
+       }
 
        /*
         *      Initialize the rest of the fields.
@@ -135,8 +135,7 @@ fr_dict_attr_t const *fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const
        }
 
        memcpy(&flags, &old->flags, sizeof(flags));
-       flags.is_unknown = false;
-       flags.is_raw = true;
+       flags.is_unknown = 0;
 
        /*
         *      If this is a vendor, we skip most of the sanity
@@ -246,10 +245,9 @@ int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
                                     fr_dict_attr_t const *parent, unsigned int vendor)
 {
        fr_dict_attr_flags_t    flags = {
-                                       .is_unknown = true,
-                                       .is_raw = true,
-                                       .type_size = true,
-                                       .length = true
+                                       .is_unknown = 1,
+                                       .type_size = 1,
+                                       .length = 1
                                };
 
        if (!fr_cond_assert(parent)) {
@@ -301,8 +299,7 @@ fr_dict_attr_t const *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr
        fr_dict_attr_t          *n;
        fr_dict_attr_t          *new_parent = NULL;
        fr_dict_attr_flags_t    flags = {
-               .is_unknown     = true,
-               .is_raw         = true,
+               .is_unknown     = 1
        };
 
        if (!fr_cond_assert(parent)) {
@@ -388,7 +385,6 @@ int fr_dict_unknown_attr_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
        fr_dict_attr_t          *da;
        fr_dict_attr_flags_t    flags = {
                                        .is_unknown = true,
-                                       .is_raw = true,
                                };
 
        if (!fr_cond_assert(parent)) {
@@ -435,9 +431,8 @@ ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
        fr_dict_attr_t const    *our_parent = parent;
        fr_dict_attr_t          *n = NULL, *our_da;
        fr_dict_attr_flags_t    flags = {
-               .is_unknown = true,
-               .is_raw = true,
-       };
+                                       .is_unknown = true
+                               };
 
        if (!fr_cond_assert(parent)) {
                fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
index 33036518034356fd1ef130051746d736e18ae02a..da919d22c4dbe0d48289949c3f9a6a1f73030eab 100644 (file)
@@ -1331,7 +1331,7 @@ ssize_t dict_by_protocol_substr(fr_dict_attr_err_t *err,
        char                    buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1 + 1];      /* +1 \0 +1 for "too long" */
        fr_sbuff_t              our_name = FR_SBUFF_NO_ADVANCE(name);
 
-       if (!dict_gctx || !name || !fr_sbuff_remaining(name) || !out) return 0;
+       if (!dict_gctx || !name || !out) return 0;
 
        memset(&root, 0, sizeof(root));
 
@@ -1762,7 +1762,7 @@ ssize_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t cons
        *out = da;
        if (err) *err = FR_DICT_ATTR_OK;
 
-       return (size_t)fr_sbuff_set(name, &our_name);
+       return fr_sbuff_set(name, &our_name);
 }
 
 /* Internal version of fr_dict_attr_by_name
@@ -2386,7 +2386,6 @@ fr_dict_t *dict_alloc(TALLOC_CTX *ctx)
        return dict;
 }
 
-
 /** Decrement the reference count on a previously loaded dictionary
  *
  * @param[in] dict     to free.
@@ -2404,6 +2403,26 @@ int fr_dict_free(fr_dict_t **dict)
        return ret;
 }
 
+/** Decrement the reference count on a previously loaded dictionary
+ *
+ * @param[in] dict     to free.
+ * @return how many references to the dictionary remain.
+ */
+int fr_dict_const_free(fr_dict_t const **dict)
+{
+       int ret;
+       fr_dict_t *our_dict;
+
+       if (!*dict) return 0;
+
+       memcpy(&our_dict, dict, sizeof(our_dict));
+
+       ret = talloc_decrease_ref_count(our_dict);
+       *dict = NULL;
+
+       return ret;
+}
+
 /** Process a dict_attr_autoload element to load/verify a dictionary attribute
  *
  * @param[in] to_load  attribute definition
index 3fb7e4746d66f459536938cb711e765a33a66ded..c557b6d23924c21bd65e9813d4dbce32be5d7429 100644 (file)
@@ -39,10 +39,11 @@ SOURCES             := \
                   missing.c \
                   net.c \
                   packet.c \
+                  pair.c \
                   pair_cursor.c \
                   pair_legacy.c \
+                  pair_print.c \
                   pair_tokenize.c \
-                  pair.c \
                   paths.c \
                   pcap.c \
                   print.c \
index 5d5aa8d1bde1cb00348d787a7894b48d55023eef..71dc37cb75c79eeec82cfed7278d00ee73727585 100644 (file)
@@ -75,7 +75,6 @@ VALUE_PAIR *fr_pair_alloc(TALLOC_CTX *ctx)
        }
 
        vp->op = T_OP_EQ;
-       vp->tag = TAG_ANY;
        vp->type = VT_NONE;
 
        talloc_set_destructor(vp, _fr_pair_free);
@@ -201,7 +200,6 @@ VALUE_PAIR *fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
        if (!n) return NULL;
 
        n->op = vp->op;
-       n->tag = vp->tag;
        n->next = NULL;
        n->type = vp->type;
 
@@ -380,7 +378,7 @@ void *fr_pair_iter_next_by_ancestor(void **prev, void *to_eval, void *uctx)
 /** Find the pair with the matching DAs
  *
  */
-VALUE_PAIR *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da, int8_t tag)
+VALUE_PAIR *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da)
 {
        VALUE_PAIR      *vp;
 
@@ -391,10 +389,7 @@ VALUE_PAIR *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da, int8_
 
        if (!da) return NULL;
 
-       for (vp = head; vp != NULL; vp = vp->next) {
-               if ((da == vp->da) && TAG_EQ(tag, vp->tag)) return vp;
-       }
-
+       for (vp = head; vp != NULL; vp = vp->next) if (da == vp->da) return vp;
        return NULL;
 }
 
@@ -403,7 +398,7 @@ VALUE_PAIR *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da, int8_
  *
  * @todo should take DAs and do a pointer comparison.
  */
-VALUE_PAIR *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
+VALUE_PAIR *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr)
 {
        VALUE_PAIR      *vp;
 
@@ -424,7 +419,7 @@ VALUE_PAIR *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned
                        if (dv->pen != vendor) continue;
                }
 
-               if ((attr == vp->da->attr) && TAG_EQ(tag, vp->tag)) return vp;
+               if (attr == vp->da->attr) return vp;
        }
 
        return NULL;
@@ -433,7 +428,7 @@ VALUE_PAIR *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned
 /** Find the pair with the matching attribute
  *
  */
-VALUE_PAIR *fr_pair_find_by_child_num(VALUE_PAIR *head, fr_dict_attr_t const *parent, unsigned int attr, int8_t tag)
+VALUE_PAIR *fr_pair_find_by_child_num(VALUE_PAIR *head, fr_dict_attr_t const *parent, unsigned int attr)
 {
        fr_dict_attr_t const    *da;
        VALUE_PAIR              *vp;
@@ -446,9 +441,7 @@ VALUE_PAIR *fr_pair_find_by_child_num(VALUE_PAIR *head, fr_dict_attr_t const *pa
        da = fr_dict_attr_child_by_num(parent, attr);
        if (!da) return NULL;
 
-       for (vp = head; vp != NULL; vp = vp->next) {
-               if ((da == vp->da) && TAG_EQ(tag, vp->tag)) return vp;
-       }
+       for (vp = head; vp != NULL; vp = vp->next) if (da == vp->da) return vp;
 
        return NULL;
 }
@@ -471,11 +464,10 @@ fr_pair_list_t *fr_pair_group_get_sublist(VALUE_PAIR *head)
  *
  * @param[in] head VP which MUST be of FR_TYPE_GROUP
  * @param[in] da to search for
- * @param[in] tag to search for
  */
-VALUE_PAIR *fr_pair_group_find_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da, int8_t tag)
+VALUE_PAIR *fr_pair_group_find_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da)
 {
-       return fr_pair_find_by_da((VALUE_PAIR *) head, da, tag);
+       return fr_pair_find_by_da((VALUE_PAIR *) head, da);
 }
 
 
@@ -484,11 +476,10 @@ VALUE_PAIR *fr_pair_group_find_by_da(fr_pair_list_t *head, fr_dict_attr_t const
  * @param[in] head VP which MUST be of FR_TYPE_GROUP
  * @param[in] vendor to search for
  * @param[in] attr to search for
- * @param[in] tag to search for
  */
-VALUE_PAIR *fr_pair_group_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr, int8_t tag)
+VALUE_PAIR *fr_pair_group_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr)
 {
-       return fr_pair_find_by_num((VALUE_PAIR *) head, vendor, attr, tag);
+       return fr_pair_find_by_num((VALUE_PAIR *) head, vendor, attr);
 }
 
 /** Add a VP to the end of the FR_TYPE_GROUP.
@@ -617,7 +608,7 @@ void fr_pair_replace(VALUE_PAIR **head, VALUE_PAIR *replace)
                 *      Found the head attribute, replace it,
                 *      and return.
                 */
-               if ((i->da == replace->da) && ATTR_TAG_MATCH(i, replace->tag)) {
+               if (i->da == replace->da) {
                        *prev = replace;
 
                        /*
@@ -648,9 +639,8 @@ void fr_pair_replace(VALUE_PAIR **head, VALUE_PAIR *replace)
  * @param[in,out] head VP in list.
  * @param[in] attr     to match.
  * @param[in] parent   to match.
- * @param[in] tag      to match. TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
  */
-void fr_pair_delete_by_child_num(VALUE_PAIR **head, fr_dict_attr_t const *parent, unsigned int attr, int8_t tag)
+void fr_pair_delete_by_child_num(VALUE_PAIR **head, fr_dict_attr_t const *parent, unsigned int attr)
 {
        VALUE_PAIR              *i, *next;
        VALUE_PAIR              **last = head;
@@ -662,7 +652,7 @@ void fr_pair_delete_by_child_num(VALUE_PAIR **head, fr_dict_attr_t const *parent
        for (i = *head; i; i = next) {
                VP_VERIFY(i);
                next = i->next;
-               if ((i->da == da) && (!i->da->flags.has_tag || TAG_EQ(tag, i->tag))) {
+               if (i->da == da) {
                        *last = next;
                        talloc_free(i);
                } else {
@@ -789,20 +779,15 @@ void fr_pair_delete(VALUE_PAIR **list, VALUE_PAIR const *vp)
  *     - 0 if a == b
  *     - -1 if a < b
  */
-int8_t fr_pair_cmp_by_da_tag(void const *a, void const *b)
+int8_t fr_pair_cmp_by_da(void const *a, void const *b)
 {
        VALUE_PAIR const *my_a = a;
        VALUE_PAIR const *my_b = b;
 
-       uint8_t cmp;
-
        VP_VERIFY(my_a);
        VP_VERIFY(my_b);
 
-       cmp = fr_pointer_cmp(my_a->da, my_b->da);
-       if (cmp != 0) return cmp;
-
-       return (my_a->tag > my_b->tag) - (my_a->tag < my_b->tag);
+       return fr_pointer_cmp(my_a->da, my_b->da);
 }
 
 /** Order attributes by their attribute number, and tag
@@ -814,7 +799,7 @@ int8_t fr_pair_cmp_by_da_tag(void const *a, void const *b)
  *     - 0 if a == b
  *     - -1 if a < b
  */
-static inline int8_t pair_cmp_by_num_tag(void const *a, void const *b)
+static inline int8_t pair_cmp_by_num(void const *a, void const *b)
 {
        VALUE_PAIR const *my_a = a;
        VALUE_PAIR const *my_b = b;
@@ -822,13 +807,7 @@ static inline int8_t pair_cmp_by_num_tag(void const *a, void const *b)
        VP_VERIFY(my_a);
        VP_VERIFY(my_b);
 
-       if (my_a->da->attr < my_b->da->attr) return -1;
-       if (my_a->da->attr > my_b->da->attr) return +1;
-
-       if (my_a->tag < my_b->tag) return -1;
-       if (my_a->tag > my_b->tag) return +1;
-
-       return 0;
+       return (my_a->da->attr < my_b->da->attr) - (my_a->da->attr > my_b->da->attr);
 }
 
 /** Order attributes by their parent(s), attribute number, and tag
@@ -843,7 +822,7 @@ static inline int8_t pair_cmp_by_num_tag(void const *a, void const *b)
  *     - 0 if a == b
  *     - -1 if a < b
  */
-int8_t fr_pair_cmp_by_parent_num_tag(void const *a, void const *b)
+int8_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
 {
        VALUE_PAIR const        *vp_a = a;
        VALUE_PAIR const        *vp_b = b;
@@ -851,20 +830,21 @@ int8_t fr_pair_cmp_by_parent_num_tag(void const *a, void const *b)
        fr_dict_attr_t const    *da_b = vp_b->da;
        fr_da_stack_t           da_stack_a;
        fr_da_stack_t           da_stack_b;
+       int8_t                  cmp;
        int i;
 
        /*
         *      Fast path (assuming attributes
         *      are in the same dictionary).
         */
-       if ((da_a->parent->flags.is_root) && (da_b->parent->flags.is_root)) return pair_cmp_by_num_tag(vp_a, vp_b);
+       if ((da_a->parent->flags.is_root) && (da_b->parent->flags.is_root)) return pair_cmp_by_num(vp_a, vp_b);
 
        fr_proto_da_stack_build(&da_stack_a, da_a);
        fr_proto_da_stack_build(&da_stack_b, da_b);
 
        for (i = 0; (da_a = da_stack_a.da[i]) && (da_b = da_stack_b.da[i]); i++) {
-               if (da_a->attr > da_b->attr) return +1;
-               if (da_a->attr < da_b->attr) return -1;
+               cmp = (da_a->attr > da_b->attr) - (da_a->attr < da_b->attr);
+               if (cmp != 0) return cmp;
        }
 
        /*
@@ -872,13 +852,7 @@ int8_t fr_pair_cmp_by_parent_num_tag(void const *a, void const *b)
         *      hierarchy than b, it should come
         *      before b.
         */
-       if (da_a && !da_b) return +1;
-       if (!da_a && da_b) return -1;
-
-       if (vp_a->tag > vp_b->tag) return +1;
-       if (vp_a->tag < vp_b->tag) return -1;
-
-       return 0;
+       return (da_a && !da_b) - (!da_a && da_b);
 }
 
 /** Compare two pairs, using the operator from "a"
@@ -935,7 +909,7 @@ int fr_pair_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
                                                        a->da->name);
                                return -1;
                        }
-                       value = fr_pair_asprint(NULL, b, '\0');
+                       fr_pair_aprint(NULL, &value, b);
                        if (!value) {
                                talloc_free(preg);
                                return -1;
@@ -990,9 +964,6 @@ int fr_pair_list_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
                ret = (a_p->da < b_p->da) - (a_p->da > b_p->da);
                if (ret != 0) return ret;
 
-               ret = (a_p->tag < b_p->tag) - (a_p->tag > b_p->tag);
-               if (ret != 0) return ret;
-
                if (a_p->da->type == FR_TYPE_GROUP) {
                        ret = fr_pair_list_cmp(a_p->vp_group, b_p->vp_group);
                        if (ret != 0) return ret;
@@ -1132,14 +1103,8 @@ void fr_pair_validate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
                return;
        }
 
-       if (!ATTR_TAG_MATCH(list, filter->tag)) {
-               fr_strerror_printf("Attribute \"%s\" tag \"%i\" didn't match filter tag \"%i\"",
-                                  list->da->name, list->tag, filter->tag);
-               return;
-       }
-
-       value = fr_pair_asprint(ctx, list, '"');
-       str = fr_pair_asprint(ctx, filter, '"');
+       fr_pair_aprint(ctx, &value, list);
+       fr_pair_aprint(ctx, &str, filter);
 
        fr_strerror_printf("Attribute value \"%s\" didn't match filter: %s", value, str);
 
@@ -1175,8 +1140,8 @@ bool fr_pair_validate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAI
         *
         *      @todo this should be removed one we have sets and lists
         */
-       fr_pair_list_sort(&filter, fr_pair_cmp_by_da_tag);
-       fr_pair_list_sort(&list, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(&filter, fr_pair_cmp_by_da);
+       fr_pair_list_sort(&list, fr_pair_cmp_by_da);
 
        check = fr_cursor_init(&filter_cursor, &filter);
        match = fr_cursor_init(&list_cursor, &list);
@@ -1242,8 +1207,8 @@ bool fr_pair_validate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, V
         *
         *      @todo this should be removed one we have sets and lists
         */
-       fr_pair_list_sort(&filter, fr_pair_cmp_by_da_tag);
-       fr_pair_list_sort(&list, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(&filter, fr_pair_cmp_by_da);
+       fr_pair_list_sort(&list, fr_pair_cmp_by_da);
 
        fr_pair_cursor_init(&list_cursor, &list);
        for (check = fr_pair_cursor_init(&filter_cursor, &filter);
@@ -1257,7 +1222,7 @@ bool fr_pair_validate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, V
                         *      Record the start of the matching attributes in the pair list
                         *      For every other operator we require the match to be present
                         */
-                       match = fr_pair_cursor_next_by_da(&list_cursor, check->da, check->tag);
+                       match = fr_pair_cursor_next_by_da(&list_cursor, check->da);
                        if (!match) {
                                if (check->op == T_OP_CMP_FALSE) continue;
                                goto mismatch;
@@ -1651,7 +1616,7 @@ int fr_pair_value_strtrim(VALUE_PAIR *vp)
  * @param[in,out] vp to update
  * @param[in] fmt the format string
  */
-int fr_pair_value_asprintf(VALUE_PAIR *vp, char const *fmt, ...)
+int fr_pair_value_aprintf(VALUE_PAIR *vp, char const *fmt, ...)
 {
        int     ret;
        va_list ap;
@@ -2058,102 +2023,6 @@ int fr_pair_value_mem_append_buffer(VALUE_PAIR *vp, uint8_t *src, bool tainted)
        return ret;
 }
 
-/** Print the value of an attribute to a string
- *
- * @param[out] out Where to write the string.
- * @param[in] outlen Size of outlen (must be at least 3 bytes).
- * @param[in] vp to print.
- * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0
- *     raw string will be added.
- * @return
- *     - Length of data written to out.
- *     - Value >= outlen on truncation.
- */
-size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
-{
-       VP_VERIFY(vp);
-
-       if (vp->type == VT_XLAT) return snprintf(out, outlen, "%c%s%c", quote, vp->xlat, quote);
-
-       if (vp->da->type == FR_TYPE_GROUP) {
-               VALUE_PAIR *child, *head = vp->vp_ptr;
-               fr_cursor_t cursor;
-               char *p, *end;
-               size_t len;
-
-               if (!fr_cond_assert(head != NULL)) return 0;
-
-               /*
-                *      "{  }"
-                */
-               if (outlen < 4) return 0;
-
-               p = out;
-               end = out + outlen - 2; /* need room for the last " }" */
-
-               *(p++) = '{';
-               *(p++) = ' ';
-
-               for (child = fr_cursor_init(&cursor, &head);
-                    child != NULL;
-                    child = fr_cursor_next(&cursor)) {
-                       VP_VERIFY(child);
-
-                       len = fr_pair_snprint(p, end - p, child);
-                       if (len == 0) goto done;
-
-                       p += len;
-                       *(p++) = ',';
-                       *(p++) = ' ';
-
-               }
-
-               p -= 2;         /* over-write the last ", " */
-
-       done:
-               *(p++) = ' ';
-               *(p++) = '}';
-               return p - out;
-       }
-
-       return fr_value_box_snprint(out, outlen, &vp->data, quote);
-}
-
-/** Print one attribute value to a string
- *
- * @param ctx to allocate string in.
- * @param vp to print.
- * @param[in] quote the quotation character
- * @return a talloced buffer with the attribute operator and value.
- */
-char *fr_pair_value_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
-{
-       VP_VERIFY(vp);
-
-       if (vp->type == VT_XLAT) return fr_asprint(ctx, vp->xlat, talloc_array_length(vp->xlat) - 1, quote);
-
-       /*
-        *      Groups are magical.
-        */
-       if (vp->da->type == FR_TYPE_GROUP) {
-               char *tmp = talloc_array(ctx, char, 1024);
-               char *out;
-               size_t len;
-
-               len = fr_pair_value_snprint(tmp, 1024, vp, quote);
-               if (len >= 1024) {
-                       talloc_free(tmp);
-                       return NULL;
-               }
-
-               out = talloc_memdup(ctx, tmp, len + 1);
-               talloc_free(tmp);
-               return out;
-       }
-
-       return fr_value_box_asprint(ctx, &vp->data, quote);
-}
-
 /** Return a const buffer for an enum type attribute
  *
  * Where the vp type is numeric but does not have any enumv, or its value
@@ -2188,7 +2057,7 @@ char const *fr_pair_value_enum(VALUE_PAIR const *vp, char buff[20])
        }
 
        if (!enumv) {
-               fr_pair_value_snprint(buff, 20, vp, '\0');
+               fr_pair_print_value_quoted(&FR_SBUFF_OUT(buff, 20), vp, T_BARE_WORD);
                str = buff;
        } else {
                str = enumv->name;
@@ -2223,208 +2092,6 @@ int fr_pair_value_enum_box(fr_value_box_t const **out, VALUE_PAIR *vp)
        return 0;
 }
 
-char *fr_pair_type_asprint(TALLOC_CTX *ctx, fr_type_t type)
-{
-       switch (type) {
-       case FR_TYPE_STRING :
-               return talloc_typed_strdup(ctx, "_");
-
-       case FR_TYPE_UINT64:
-       case FR_TYPE_SIZE:
-       case FR_TYPE_INT32:
-       case FR_TYPE_UINT8:
-       case FR_TYPE_UINT16:
-       case FR_TYPE_UINT32:
-       case FR_TYPE_DATE:
-               return talloc_typed_strdup(ctx, "0");
-
-       case FR_TYPE_IPV4_ADDR:
-               return talloc_typed_strdup(ctx, "?.?.?.?");
-
-       case FR_TYPE_IPV4_PREFIX:
-               return talloc_typed_strdup(ctx, "?.?.?.?/?");
-
-       case FR_TYPE_IPV6_ADDR:
-               return talloc_typed_strdup(ctx, "[:?:]");
-
-       case FR_TYPE_IPV6_PREFIX:
-               return talloc_typed_strdup(ctx, "[:?:]/?");
-
-       case FR_TYPE_OCTETS:
-               return talloc_typed_strdup(ctx, "??");
-
-       case FR_TYPE_ETHERNET:
-               return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
-
-       case FR_TYPE_ABINARY:
-               return talloc_typed_strdup(ctx, "??");
-
-       case FR_TYPE_GROUP:
-               return talloc_typed_strdup(ctx, "{ ? }");
-
-       default :
-               break;
-       }
-
-       return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
-}
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
-       <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param out Where to write the string.
- * @param outlen Length of output buffer.
- * @param vp to print.
- * @return
- *     - Length of data written to out.
- *     - value >= outlen on truncation.
- */
-size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
-{
-       char const      *token = NULL;
-       size_t          len, freespace = outlen;
-
-       if (!out) return 0;
-
-       *out = '\0';
-       if (!vp || !vp->da) return 0;
-
-       VP_VERIFY(vp);
-
-       if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
-               token = fr_tokens[vp->op];
-       } else {
-               token = "<INVALID-TOKEN>";
-       }
-
-       if (vp->da->flags.has_tag && (vp->tag != 0) && (vp->tag != TAG_ANY)) {
-               len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
-       } else {
-               len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
-       }
-
-       if (is_truncated(len, freespace)) return len;
-       out += len;
-       freespace -= len;
-
-       len = fr_pair_value_snprint(out, freespace, vp, '"');
-       if (is_truncated(len, freespace)) return (outlen - freespace) + len;
-       freespace -= len;
-
-       return (outlen - freespace);
-}
-
-/** Print one attribute and value to FP
- *
- * Complete string with '\\t' and '\\n' is written to buffer before printing to
- * avoid issues when running with multiple threads.
- *
- * @param fp to output to.
- * @param vp to print.
- */
-void fr_pair_fprint(FILE *fp, VALUE_PAIR const *vp)
-{
-       char    buf[1024];
-       char    *p = buf;
-       size_t  len;
-
-       if (!fp) return;
-       VP_VERIFY(vp);
-
-       *p++ = '\t';
-       len = fr_pair_snprint(p, sizeof(buf) - 1, vp);
-       if (!len) {
-               return;
-       }
-       p += len;
-
-       /*
-        *      Deal with truncation gracefully
-        */
-       if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
-               p = buf + (sizeof(buf) - 2);
-       }
-
-       *p++ = '\n';
-       *p = '\0';
-
-       fputs(buf, fp);
-}
-
-
-/** Print a list of attributes and enumv
- *
- * @param[in] log to output to.
- * @param[in] vp to print.
- * @param[in] file where the message originated
- * @param[in] line where the message originated
- */
-void _fr_pair_list_log(fr_log_t const *log, VALUE_PAIR const *vp, char const *file, int line)
-{
-       VALUE_PAIR *our_vp;
-       fr_cursor_t cursor;
-
-       memcpy(&our_vp, &vp, sizeof(vp)); /* const work-arounds */
-
-       for (vp = fr_cursor_init(&cursor, &our_vp); vp; vp = fr_cursor_next(&cursor)) {
-               fr_log(log, L_DBG, file, line, "\t%pP", vp);
-       }
-}
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
-       <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param ctx to allocate string in.
- * @param vp to print.
- * @param[in] quote the quotation character
- * @return a talloced buffer with the attribute operator and value.
- */
-char *fr_pair_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
-{
-       char const      *token = NULL;
-       char            *str, *value;
-
-       if (!vp || !vp->da) return 0;
-
-       VP_VERIFY(vp);
-
-       if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
-               token = fr_tokens[vp->op];
-       } else {
-               token = "<INVALID-TOKEN>";
-       }
-
-       value = fr_pair_value_asprint(ctx, vp, quote);
-
-       if (vp->da->flags.has_tag) {
-               if (quote && (vp->vp_type == FR_TYPE_STRING)) {
-                       str = talloc_typed_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
-               } else {
-                       str = talloc_typed_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
-               }
-       } else {
-               if (quote && (vp->vp_type == FR_TYPE_STRING)) {
-                       str = talloc_typed_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
-               } else {
-                       str = talloc_typed_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
-               }
-       }
-
-       talloc_free(value);
-
-       return str;
-}
-
 #ifdef WITH_VERIFY_PTR
 /*
  *     Verify a VALUE_PAIR
index 7592f2d0026113cd5d28bdaf2d90709d63af67fa..87206e14c2f626aaf96002a4b8ef924e99d2a119 100644 (file)
@@ -102,9 +102,6 @@ struct value_pair_s {
        struct {
                fr_token_t              op;                     //!< Operator to use when moving or inserting
                                                                //!< valuepair into a list.
-
-               int8_t                  tag;                    //!< Tag value used to group valuepairs.
-
                char const              *xlat;                  //!< Source string for xlat expansion.
        };
 
@@ -183,18 +180,7 @@ typedef struct {
 #define vp_type                        data.type
 #define vp_tainted             data.tainted
 
-#define TAG_VALID(x)           ((x) > 0 && (x) < 0x20)
-#define TAG_VALID_ZERO(x)              ((x) >= 0 && (x) < 0x20)
-#define TAG_ANY                        INT8_MIN
-#define TAG_NONE               0
-/** Check if tags are equal
- *
- * @param _x tag were matching on.
- * @param _y tag belonging to the attribute were checking.
- */
-#define TAG_EQ(_x, _y) ((_x == _y) || (_x == TAG_ANY) || ((_x == TAG_NONE) && (_y == TAG_ANY)))
-#define ATTR_TAG_MATCH(_a, _t) (!_a->da->flags.has_tag || TAG_EQ(_t, _a->tag))
-#define ATTRIBUTE_EQ(_x, _y) ((_x && _y) && (_x->da == _y->da) && (!_x->da->flags.has_tag || TAG_EQ(_x->tag, _y->tag)))
+#define ATTRIBUTE_EQ(_x, _y) ((_x && _y) && (_x->da == _y->da))
 
 #  ifdef WITH_VERIFY_PTR
 void           fr_pair_verify(char const *file, int line, VALUE_PAIR const *vp);
@@ -257,19 +243,17 @@ static inline VALUE_PAIR *fr_cursor_iter_by_ancestor_init(fr_cursor_t *cursor,
        return fr_cursor_talloc_iter_init(cursor, list, fr_pair_iter_next_by_ancestor, da, VALUE_PAIR);
 }
 
-VALUE_PAIR     *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da, int8_t tag);
+VALUE_PAIR     *fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da);
 
-VALUE_PAIR     *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag);
+VALUE_PAIR     *fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr);
 
-VALUE_PAIR     *fr_pair_find_by_child_num(VALUE_PAIR *head, fr_dict_attr_t const *parent,
-                                          unsigned int attr, int8_t tag);
+VALUE_PAIR     *fr_pair_find_by_child_num(VALUE_PAIR *head, fr_dict_attr_t const *parent, unsigned int attr);
 
 void           fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp);
 
 void           fr_pair_replace(VALUE_PAIR **head, VALUE_PAIR *add);
 
-void           fr_pair_delete_by_child_num(VALUE_PAIR **head, fr_dict_attr_t const *parent,
-                                           unsigned int attr, int8_t tag);
+void           fr_pair_delete_by_child_num(VALUE_PAIR **head, fr_dict_attr_t const *parent, unsigned int attr);
 
 int            fr_pair_add_by_da(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR **list, fr_dict_attr_t const *da);
 
@@ -282,9 +266,9 @@ void                fr_pair_delete(VALUE_PAIR **list, VALUE_PAIR const *vp);
 /* functions for FR_TYPE_GROUP */
 fr_pair_list_t *fr_pair_group_get_sublist(VALUE_PAIR *head);
 
-VALUE_PAIR     *fr_pair_group_find_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da, int8_t tag);
+VALUE_PAIR     *fr_pair_group_find_by_da(fr_pair_list_t *head, fr_dict_attr_t const *da);
 
-VALUE_PAIR     *fr_pair_group_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr, int8_t tag);
+VALUE_PAIR     *fr_pair_group_find_by_num(fr_pair_list_t *head, unsigned int vendor, unsigned int attr);
 
 void           fr_pair_group_add(fr_pair_list_t *head, VALUE_PAIR *vp);
 
@@ -312,8 +296,8 @@ typedef             int8_t (*fr_cmp_t)(void const *a, void const *b);
  *     - -1 on failure.
  */
 #define                fr_pair_cmp_op(_op, _a, _b)     fr_value_box_cmp_op(_op, &_a->data, &_b->data)
-int8_t         fr_pair_cmp_by_da_tag(void const *a, void const *b);
-int8_t         fr_pair_cmp_by_parent_num_tag(void const *a, void const *b);
+int8_t         fr_pair_cmp_by_da(void const *a, void const *b);
+int8_t         fr_pair_cmp_by_parent_num(void const *a, void const *b);
 int            fr_pair_cmp(VALUE_PAIR *a, VALUE_PAIR *b);
 int            fr_pair_list_cmp(VALUE_PAIR *a, VALUE_PAIR *b);
 void           fr_pair_list_sort(VALUE_PAIR **vps, fr_cmp_t cmp);
@@ -351,7 +335,7 @@ int         fr_pair_value_strdup_shallow(VALUE_PAIR *vp, char const *src, bool tainted)
 
 int            fr_pair_value_strtrim(VALUE_PAIR *vp);
 
-int            fr_pair_value_asprintf(VALUE_PAIR *vp, char const *fmt, ...) CC_HINT(format (printf, 2, 3));
+int            fr_pair_value_aprintf(VALUE_PAIR *vp, char const *fmt, ...) CC_HINT(format (printf, 2, 3));
 /** @} */
 
 /** @name Assign and manipulate binary-safe strings
@@ -375,7 +359,7 @@ int         fr_pair_value_bstrn_append(VALUE_PAIR *vp, char const *src, size_t len, boo
 int            fr_pair_value_bstr_append_buffer(VALUE_PAIR *vp, char const *src, bool tainted);
  /** @} */
 
- /** @name Assign and manipulate octets strings
+/** @name Assign and manipulate octets strings
  *
  * @{
  */
@@ -396,22 +380,35 @@ int               fr_pair_value_mem_append(VALUE_PAIR *vp, uint8_t *src, size_t len, bool tai
 int            fr_pair_value_mem_append_buffer(VALUE_PAIR *vp, uint8_t *src, bool tainted);
  /** @} */
 
-/* Printing functions */
-size_t         fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote);
-char           *fr_pair_value_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote);
-char const     *fr_pair_value_enum(VALUE_PAIR const *vp, char buff[static 20]);
-int            fr_pair_value_enum_box(fr_value_box_t const **out, VALUE_PAIR *vp);
+/** @name Enum functions
+ *
+ * @{
+ */
+char const             *fr_pair_value_enum(VALUE_PAIR const *vp, char buff[static 20]);
 
-size_t         fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp);
-void           fr_pair_fprint(FILE *, VALUE_PAIR const *vp);
+int                    fr_pair_value_enum_box(fr_value_box_t const **out, VALUE_PAIR *vp);
+/** @} */
 
-#define                fr_pair_list_log(_log, _vp) _fr_pair_list_log(_log, _vp, __FILE__, __LINE__);
-void           _fr_pair_list_log(fr_log_t const *log, VALUE_PAIR const *vp, char const *file, int line);
-char           *fr_pair_type_asprint(TALLOC_CTX *ctx, fr_type_t type);
-char           *fr_pair_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote);
+/** @name Printing functions
+ *
+ * @{
+ */
+ssize_t                fr_pair_print_value_quoted(fr_sbuff_t *out, VALUE_PAIR const *vp, fr_token_t quote);
+
+static inline size_t   fr_pair_aprint_value_quoted(TALLOC_CTX *ctx, char **out, VALUE_PAIR const *vp, fr_token_t quote) SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_pair_print_value_quoted, vp, quote)
+
+ssize_t                        fr_pair_print(fr_sbuff_t *out, VALUE_PAIR const *vp);
+
+static inline size_t   fr_pair_aprint(TALLOC_CTX *ctx, char **out, VALUE_PAIR const *vp) SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_pair_print, vp)
+
+void                   fr_pair_fprint(FILE *, VALUE_PAIR const *vp);
+
+#define                        fr_pair_list_log(_log, _vp) _fr_pair_list_log(_log, _vp, __FILE__, __LINE__);
+void                   _fr_pair_list_log(fr_log_t const *log, VALUE_PAIR const *vp, char const *file, int line);
+/** @} */
 
-void           fr_pair_list_tainted(VALUE_PAIR *vp);
-VALUE_PAIR     *fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_value_box_t *box);
+void                   fr_pair_list_tainted(VALUE_PAIR *vp);
+VALUE_PAIR             *fr_pair_list_afrom_box(TALLOC_CTX *ctx, fr_dict_t const *dict, fr_value_box_t *box);
 
 /* Tokenization */
 typedef struct {
index 1304dcc5310592673feff849a64a289d7ccbb439..0f0d066745f70707e0b66868c27f7a0d28897b3a 100644 (file)
@@ -189,13 +189,11 @@ void fr_pair_cursor_end(vp_cursor_t *cursor)
  * @param cursor to operate on.
  * @param attr number to match.
  * @param vendor number to match (0 for none vendor attribute).
- * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
- *       untagged attribute, TAG_NONE to match attributes without tags.
  * @return
  *     - The next matching #VALUE_PAIR.
  *     - NULL if no #VALUE_PAIR (s) match.
  */
-VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag)
+VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr)
 {
        VALUE_PAIR *i;
 
@@ -209,9 +207,7 @@ VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor,
                     i != NULL;
                     i = i->next) {
                        VP_VERIFY(i);
-                       if (fr_dict_attr_is_top_level(i->da) && (i->da->attr == attr) && ATTR_TAG_MATCH(i, tag)) {
-                               break;
-                       }
+                       if (fr_dict_attr_is_top_level(i->da) && (i->da->attr == attr)) break;
                }
        } else {
                for (i = cursor->found ? cursor->found->next : cursor->current;
@@ -219,10 +215,7 @@ VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor,
                     i = i->next) {
                        VP_VERIFY(i);
                        if ((i->da->parent->type == FR_TYPE_VENDOR) &&
-                           (i->da->attr == attr) && (fr_dict_vendor_num_by_da(i->da) == vendor) &&
-                           ATTR_TAG_MATCH(i, tag)) {
-                               break;
-                       }
+                           (i->da->attr == attr) && (fr_dict_vendor_num_by_da(i->da) == vendor)) break;
                }
        }
 
@@ -242,14 +235,12 @@ VALUE_PAIR *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor,
  * @param cursor       to operate on.
  * @param parent       to search for attr in.
  * @param attr         number to match.
- * @param tag          to match. Either a tag number or TAG_ANY to match any tagged or
- *                     untagged attribute, TAG_NONE to match attributes without tags.
  * @return
  *     - The next matching #VALUE_PAIR.
        - NULL if no #VALUE_PAIR (s) match (or attr doesn't exist).
  */
 VALUE_PAIR *fr_pair_cursor_next_by_child_num(vp_cursor_t *cursor,
-                                       fr_dict_attr_t const *parent, unsigned int attr, int8_t tag)
+                                       fr_dict_attr_t const *parent, unsigned int attr)
 {
        fr_dict_attr_t const *da;
        VALUE_PAIR *i;
@@ -263,9 +254,7 @@ VALUE_PAIR *fr_pair_cursor_next_by_child_num(vp_cursor_t *cursor,
             i != NULL;
             i = i->next) {
                VP_VERIFY(i);
-               if ((i->da == da) && ATTR_TAG_MATCH(i, tag)) {
-                       break;
-               }
+               if (i->da == da) break;
        }
 
        return fr_pair_cursor_update(cursor, i);
@@ -283,13 +272,11 @@ VALUE_PAIR *fr_pair_cursor_next_by_child_num(vp_cursor_t *cursor,
  *
  * @param cursor to operate on.
  * @param da to match.
- * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
- *       untagged attribute, TAG_NONE to match attributes without tags.
  * @return
  *     - Next matching #VALUE_PAIR.
  *     - NULL if no #VALUE_PAIR (s) match.
  */
-VALUE_PAIR *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da, int8_t tag)
+VALUE_PAIR *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da)
 {
        VALUE_PAIR *i;
 
@@ -299,9 +286,7 @@ VALUE_PAIR *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const
             i != NULL;
             i = i->next) {
                VP_VERIFY(i);
-               if ((i->da == da) && ATTR_TAG_MATCH(i, tag)) {
-                       break;
-               }
+               if (i->da == da) break;
        }
 
        return fr_pair_cursor_update(cursor, i);
@@ -317,13 +302,11 @@ VALUE_PAIR *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const
  *
  * @param cursor to operate on.
  * @param ancestor attribute to match on.
- * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
- *       untagged attribute, TAG_NONE to match attributes without tags.
  * @return
  *     - Next matching #VALUE_PAIR.
  *     - NULL if no #VALUE_PAIR (s) match.
  */
-VALUE_PAIR *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t const *ancestor, int8_t tag)
+VALUE_PAIR *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t const *ancestor)
 {
        VALUE_PAIR *i;
 
@@ -333,10 +316,7 @@ VALUE_PAIR *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t
             i != NULL;
             i = i->next) {
                VP_VERIFY(i);
-               if (fr_dict_attr_common_parent(ancestor, i->da, true) &&
-                   ATTR_TAG_MATCH(i, tag)) {
-                       break;
-               }
+               if (fr_dict_attr_common_parent(ancestor, i->da, true)) break;
        }
 
        return fr_pair_cursor_update(cursor, i);
index 133495d9307b300653c43e17512ad6685c32d2bd..1f928ca4ca83e5588d483e1b1815d518f51e2d0c 100644 (file)
@@ -35,16 +35,15 @@ void                fr_pair_cursor_copy(vp_cursor_t *out, vp_cursor_t *in);
 VALUE_PAIR     *fr_pair_cursor_head(vp_cursor_t *cursor);
 VALUE_PAIR     *fr_pair_cursor_tail(vp_cursor_t *cursor);
 void           fr_pair_cursor_end(vp_cursor_t *cursor);
-VALUE_PAIR     *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag);
+VALUE_PAIR     *fr_pair_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr);
 
-VALUE_PAIR     *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da, int8_t tag)
+VALUE_PAIR     *fr_pair_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da)
                CC_HINT(nonnull);
 
 VALUE_PAIR     *fr_pair_cursor_next_by_child_num(vp_cursor_t *cursor,
-                                            fr_dict_attr_t const *parent, unsigned int attr,
-                                            int8_t tag);
+                                            fr_dict_attr_t const *parent, unsigned int attr);
 
-VALUE_PAIR     *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t const *ancestor, int8_t tag)
+VALUE_PAIR     *fr_pair_cursor_next_by_ancestor(vp_cursor_t *cursor, fr_dict_attr_t const *ancestor)
                CC_HINT(nonnull);
 
 VALUE_PAIR     *fr_pair_cursor_next(vp_cursor_t *cursor);
index aa73737c2ca23b2d7ab6f70e75c2879b4abc294d..f3c3e83d4cb0e3a7386583bdaceaedceebf4f806 100644 (file)
@@ -204,66 +204,15 @@ static VALUE_PAIR *fr_pair_make_unknown(TALLOC_CTX *ctx, fr_dict_t const *dict,
 VALUE_PAIR *fr_pair_make(TALLOC_CTX *ctx, fr_dict_t const *dict, VALUE_PAIR **vps,
                         char const *attribute, char const *value, fr_token_t op)
 {
-       fr_dict_attr_t const *da;
-       VALUE_PAIR      *vp;
-       char            *p;
-       int8_t          tag;
-       char const      *attrname = attribute;
-       char            buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1 + 32];
-
-       /*
-        *    Check for tags in 'Attribute:Tag' format.
-        */
-       tag = TAG_NONE;
-
-       p = strchr(attribute, ':');
-       if (p) {
-               char *end;
-
-               if (!p[1]) {
-                       fr_strerror_printf("Invalid tag for attribute %s", attribute);
-                       return NULL;
-               }
-
-               strlcpy(buffer, attribute, sizeof(buffer));
-
-               p = buffer + (p - attrname);
-               attrname = buffer;
-
-               /* Colon found with something behind it */
-               if ((p[1] == '*') && !p[2]) {
-                       /* Wildcard tag for check items */
-                       tag = TAG_ANY;
-               } else {
-                       /* It's not a wild card tag */
-                       tag = strtol(p + 1, &end, 10);
-                       if (*end) {
-                               fr_strerror_printf("Unexpected text after tag for attribute %s", attribute);
-                               return NULL;
-                       }
-
-                       if (!TAG_VALID_ZERO(tag)) {
-                               fr_strerror_printf("Invalid tag for attribute %s", attribute);
-                               return NULL;
-                       }
-               }
-
-               /*
-                *      Leave only the attribute name in the buffer.
-                */
-               *p = '\0';
-       }
+       fr_dict_attr_t const    *da;
+       VALUE_PAIR              *vp;
+       char const              *attrname = attribute;
 
        /*
         *      It's not found in the dictionary, so we use
         *      another method to create the attribute.
         */
        if (fr_dict_attr_by_qualified_name(&da, dict, attrname, true) != FR_DICT_ATTR_OK) {
-               if (tag != TAG_NONE) {
-                       fr_strerror_printf("Invalid tag for attribute %s", attribute);
-                       return NULL;
-               }
-
                vp = fr_pair_make_unknown(ctx, dict, attrname, value, op);
                if (!vp) return NULL;
 
@@ -275,14 +224,6 @@ VALUE_PAIR *fr_pair_make(TALLOC_CTX *ctx, fr_dict_t const *dict, VALUE_PAIR **vp
        if (!da) return NULL;
 #endif
 
-       /*
-        *      Untagged attributes can't have a tag.
-        */
-       if (!da->flags.has_tag && (tag != TAG_NONE)) {
-               fr_strerror_printf("Invalid tag for attribute %s", attribute);
-               return NULL;
-       }
-
        if (da->type == FR_TYPE_GROUP) {
                fr_strerror_printf("Attributes of type 'group' are not supported");
                return NULL;
@@ -291,7 +232,6 @@ VALUE_PAIR *fr_pair_make(TALLOC_CTX *ctx, fr_dict_t const *dict, VALUE_PAIR **vp
        vp = fr_pair_afrom_da(ctx, da);
        if (!vp) return NULL;
        vp->op = (op == 0) ? T_OP_EQ : op;
-       vp->tag = tag;
 
        switch (vp->op) {
        case T_OP_CMP_TRUE:
@@ -435,14 +375,6 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_t const *dict,
 
                next = p + slen;
 
-               /*
-                *      Allow tags if the attribute supports them.
-                */
-               if (da->flags.has_tag && (*next == ':') && isdigit((int) next[1])) {
-                       next += 2;
-                       while (isdigit((int) *next)) next++;
-               }
-
                if ((size_t) (next - p) >= sizeof(raw.l_opand)) {
                        fr_dict_unknown_free(&da);
                        fr_strerror_printf("Attribute name too long");
@@ -563,8 +495,7 @@ static ssize_t fr_pair_list_afrom_substr(TALLOC_CTX *ctx, fr_dict_t const *dict,
                        } else {
                                /*
                                 *      All other attributes get the name
-                                *      parsed, which also includes parsing
-                                *      the tag.
+                                *      parsed.
                                 */
                                vp = fr_pair_make(ctx, dict, NULL, raw.l_opand, NULL, raw.op);
                                if (!vp) goto error;
@@ -733,8 +664,6 @@ error:
  * @note fr_pair_list_free should be called on the head of the source list to free
  *      unmoved attributes (if they're no longer needed).
  *
- * @note Does not respect tags when matching.
- *
  * @param[in,out] to destination list.
  * @param[in,out] from source list.
  *
@@ -796,7 +725,7 @@ void fr_pair_list_move(VALUE_PAIR **to, VALUE_PAIR **from)
                 *      it doesn't already exist.
                 */
                case T_OP_EQ:
-                       found = fr_pair_find_by_da(*to, i->da, TAG_ANY);
+                       found = fr_pair_find_by_da(*to, i->da);
                        if (!found) goto do_add;
 
                        tail_from = &(i->next);
@@ -807,7 +736,7 @@ void fr_pair_list_move(VALUE_PAIR **to, VALUE_PAIR **from)
                 *      of the same vendor/attr which already exists.
                 */
                case T_OP_SET:
-                       found = fr_pair_find_by_da(*to, i->da, TAG_ANY);
+                       found = fr_pair_find_by_da(*to, i->da);
                        if (!found) goto do_add;
 
                        switch (found->vp_type) {
@@ -825,7 +754,6 @@ void fr_pair_list_move(VALUE_PAIR **to, VALUE_PAIR **from)
                        case FR_TYPE_STRING:
                                fr_pair_value_bstrdup_buffer(found, i->vp_strvalue, i->vp_tainted);
                                fr_pair_value_clear(i);
-                               found->tag = i->tag;
                                break;
                        }
 
index 88fb5702e2af326567cb8d1b3902b2231040717b..4538ea743d26f1668e68826be64f66e47d339d4d 100644 (file)
  *
  * @copyright 2020 The FreeRADIUS server project
  */
+#include <freeradius-devel/util/pair.h>
+#include <freeradius-devel/util/talloc.h>
 
-char *fr_pair_type_asprint(TALLOC_CTX *ctx, fr_type_t type)
+/** Print the value of an attribute to a string
+ *
+ * @param[in] out      Where to write the string.
+ * @param[in] vp       to print.
+ * @param[in] quote    Char to add before and after printed value,
+ *                     if 0 no char will be added, if < 0 raw string
+ *                     will be added.
+ * @return
+ *     - >= 0 length of data written to out.
+ *     - <0 the number of bytes we would have needed to write
+ *       the complete string to out.
+ */
+ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, VALUE_PAIR const *vp, fr_token_t quote)
 {
-       switch (type) {
-       case FR_TYPE_STRING :
-               return talloc_typed_strdup(ctx, "_");
-
-       case FR_TYPE_UINT64:
-       case FR_TYPE_SIZE:
-       case FR_TYPE_INT32:
-       case FR_TYPE_UINT8:
-       case FR_TYPE_UINT16:
-       case FR_TYPE_UINT32:
-       case FR_TYPE_DATE:
-               return talloc_typed_strdup(ctx, "0");
+       fr_sbuff_t      our_out;
+       VALUE_PAIR      *child, *head;
+       fr_cursor_t     cursor;
 
-       case FR_TYPE_IPV4_ADDR:
-               return talloc_typed_strdup(ctx, "?.?.?.?");
+       VP_VERIFY(vp);
 
-       case FR_TYPE_IPV4_PREFIX:
-               return talloc_typed_strdup(ctx, "?.?.?.?/?");
+       /*
+        *      Legacy crap that needs to be removed
+        */
+       if (vp->type == VT_XLAT) {
+               char const *quote_str = fr_table_str_by_value(fr_token_quotes_table, quote, "");
 
-       case FR_TYPE_IPV6_ADDR:
-               return talloc_typed_strdup(ctx, "[:?:]");
+               return fr_sbuff_in_sprintf(out, "%s%s%s", quote_str, vp->xlat, quote_str);
+       }
 
-       case FR_TYPE_IPV6_PREFIX:
-               return talloc_typed_strdup(ctx, "[:?:]/?");
+       /*
+        *      For simple types just print the box
+        */
+       if (vp->da->type != FR_TYPE_GROUP) {
+               return fr_value_box_print_quoted(out, &vp->data, quote);
+       }
 
-       case FR_TYPE_OCTETS:
-               return talloc_typed_strdup(ctx, "??");
+       /*
+        *      Serialize all child VPs as full quoted
+        *      <pair> = ["]<child>["]
+        */
+       our_out = FR_SBUFF_NO_ADVANCE(out);
 
-       case FR_TYPE_ETHERNET:
-               return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
+       head = vp->vp_ptr;
+       if (!fr_cond_assert(head != NULL)) return 0;
 
-       case FR_TYPE_ABINARY:
-               return talloc_typed_strdup(ctx, "??");
+       FR_SBUFF_IN_CHAR_RETURN(&our_out, '{', ' ');
+       for (child = fr_cursor_init(&cursor, &head);
+            child != NULL;
+            child = fr_cursor_next(&cursor)) {
+               VP_VERIFY(child);
 
-       case FR_TYPE_GROUP:
-               return talloc_typed_strdup(ctx, "{ ? }");
+               FR_SBUFF_RETURN(fr_pair_print, &our_out, child);
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, ',', ' ');
+       }
 
-       default :
-               break;
+       if (fr_sbuff_used(&our_out)) {
+               fr_sbuff_set(&our_out, fr_sbuff_current(&our_out) - 2);
+               *fr_sbuff_current(&our_out) = '\0';
        }
 
-       return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
+       FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ', '}');
+
+       return fr_sbuff_set(out, &our_out);
 }
 
 /** Print one attribute and value to a string
  *
  * Print a VALUE_PAIR in the format:
 @verbatim
-       <attribute_name>[:tag] <op> <value>
+       <attribute_name>[:tag] <op> [q]<value>[q]
 @endverbatim
  * to a string.
  *
- * @param out Where to write the string.
- * @param outlen Length of output buffer.
- * @param vp to print.
+ * @param[in] out      Where to write the string.
+ * @param[in] vp       to print.
  * @return
  *     - Length of data written to out.
  *     - value >= outlen on truncation.
  */
-size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
+ssize_t fr_pair_print(fr_sbuff_t *out, VALUE_PAIR const *vp)
 {
        char const      *token = NULL;
-       size_t          len, freespace = outlen;
+       fr_sbuff_t      our_out = FR_SBUFF_NO_ADVANCE(out);
 
        if (!out) return 0;
 
-       *out = '\0';
        if (!vp || !vp->da) return 0;
 
        VP_VERIFY(vp);
@@ -100,21 +119,10 @@ size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
                token = "<INVALID-TOKEN>";
        }
 
-       if (vp->da->flags.has_tag && (vp->tag != 0) && (vp->tag != TAG_ANY)) {
-               len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
-       } else {
-               len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
-       }
-
-       if (is_truncated(len, freespace)) return len;
-       out += len;
-       freespace -= len;
-
-       len = fr_pair_value_snprint(out, freespace, vp, '"');
-       if (is_truncated(len, freespace)) return (outlen - freespace) + len;
-       freespace -= len;
+       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%s %s ", vp->da->name, token);
+       FR_SBUFF_RETURN(fr_pair_print_value_quoted, &our_out, vp, T_DOUBLE_QUOTED_STRING);
 
-       return (outlen - freespace);
+       return fr_sbuff_set(out, &our_out);
 }
 
 /** Print one attribute and value to FP
@@ -127,31 +135,17 @@ size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
  */
 void fr_pair_fprint(FILE *fp, VALUE_PAIR const *vp)
 {
-       char    buf[1024];
-       char    *p = buf;
-       size_t  len;
+       char            buff[1024];
+       fr_sbuff_t      sbuff = FR_SBUFF_OUT(buff, sizeof(buff));
 
        if (!fp) return;
        VP_VERIFY(vp);
 
-       *p++ = '\t';
-       len = fr_pair_snprint(p, sizeof(buf) - 1, vp);
-       if (!len) {
-               return;
-       }
-       p += len;
-
-       /*
-        *      Deal with truncation gracefully
-        */
-       if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
-               p = buf + (sizeof(buf) - 2);
-       }
-
-       *p++ = '\n';
-       *p = '\0';
+       fr_sbuff_in_char(&sbuff, '\t');
+       fr_pair_print(&sbuff, vp);
+       fr_sbuff_in_char(&sbuff, '\n');
 
-       fputs(buf, fp);
+       fputs(buff, fp);
 }
 
 
@@ -173,108 +167,3 @@ void _fr_pair_list_log(fr_log_t const *log, VALUE_PAIR const *vp, char const *fi
                fr_log(log, L_DBG, file, line, "\t%pP", vp);
        }
 }
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
-       <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param ctx to allocate string in.
- * @param vp to print.
- * @param[in] quote the quotation character
- * @return a talloced buffer with the attribute operator and value.
- */
-char *fr_pair_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
-{
-       char const      *token = NULL;
-       char            *str, *value;
-
-       if (!vp || !vp->da) return 0;
-
-       VP_VERIFY(vp);
-
-       if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
-               token = fr_tokens[vp->op];
-       } else {
-               token = "<INVALID-TOKEN>";
-       }
-
-       value = fr_pair_value_asprint(ctx, vp, quote);
-
-       if (vp->da->flags.has_tag) {
-               if (quote && (vp->vp_type == FR_TYPE_STRING)) {
-                       str = talloc_typed_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
-               } else {
-                       str = talloc_typed_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
-               }
-       } else {
-               if (quote && (vp->vp_type == FR_TYPE_STRING)) {
-                       str = talloc_typed_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
-               } else {
-                       str = talloc_typed_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
-               }
-       }
-
-       talloc_free(value);
-
-       return str;
-}
-
-/** Print the attribute portion of a pair (only)
- *
- * @param[out] out     Where to write the output attribute.
- * @param[in] end      of the output buffer.
- * @param[in] vp       to print the attribute name for.
- * @return
- *     - >= 0 number of bytes written to out on success.
- *     - < 0 number of bytes we need to write out the value.
- */
-static inline ssize_t fr_pair_snprint_attr(char * const out, char const * const end, VALUE_PAIR const *vp)
-{
-       char    *p = out;
-       size_t  len = strlen(vp->da->name);
-
-       RETURN_SLEN_IF_NO_SPACE(len, p, end);
-       memcpy(p, vp->da->name, len);
-       p += len;
-
-       if (vp->tag) {
-               size_t ret;
-
-               RETURN_SLEN_IF_NO_SPACE(1, p, end);
-               *p++ = ':';
-
-               ret = snprintf(p, end - p, "%u", vp->tag);
-               RETURN_SLEN_IF_TRUNCATED(ret, p, end);
-
-               p += ret;
-       }
-
-       return p - out;
-}
-
-/** Print a leaf attribute pair i.e. <attr> <op> <value>
- *
- * @param[out] out     Where to write the output attribute.
- * @param[in] end      of the output buffer.
- * @param[in] vp       to print.
- * @param[in] str_quote        to use when printing quoted string values.
- */
-static inline ssize_t fr_pair_snprintf_leaf(char * const out, char const * const end,
-                                           VALUE_PAIR const *vp, char str_quote)
-{
-       char *p = out;
-
-       if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
-               token = fr_tokens[vp->op];
-       } else {
-               token = "<INVALID-TOKEN>";
-       }
-
-       fr_value_box_snprint(out, )
-}
-
-
index 2e7eb251ec9ec92603daa72f57bf26560fee740c..93f7ce17d5ce07ac5e4a359cca03bebe96ce085e 100644 (file)
@@ -224,10 +224,10 @@ char const *fr_utf8_strchr(int *out_chr_len, char const *str, ssize_t inlen, cha
  * @note Return value should be checked with is_truncated
  * @note Will always \0 terminate unless outlen == 0.
  *
- * @param[in] in       string to escape.
- * @param[in] inlen    length of string to escape (lets us deal with embedded NULs)
  * @param[out] out     where to write the escaped string.
  * @param[out] outlen  the length of the buffer pointed to by out.
+ * @param[in] in       string to escape.
+ * @param[in] inlen    length of string to escape (lets us deal with embedded NULs)
  * @param[in] quote    the quotation character
  * @return
  *     - The number of bytes written to the out buffer.
@@ -665,8 +665,12 @@ char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
                         */
                        switch (*(p + 1)) {
                        case 'V':
+                       case 'R':
                        {
                                fr_value_box_t const *in = va_arg(ap_q, fr_value_box_t const *);
+                               fr_sbuff_escape_rules_t const *e_rules = NULL;
+
+                               if (*(p + 1) == 'V') e_rules = &fr_value_escape_double;
 
                                /*
                                 *      Allocations that are not part of the output
@@ -674,7 +678,7 @@ char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
                                 *      any pool associated with it.
                                 */
                                if (in) {
-                                       subst = fr_value_box_asprint(NULL, in, '"');
+                                       fr_value_box_aprint(NULL, &subst, in, e_rules);
                                        if (!subst) {
                                                talloc_free(out);
                                                va_end(ap_p);
@@ -780,7 +784,7 @@ char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
                                        goto do_splice;
                                }
 
-                               subst = fr_value_box_list_asprint(NULL, in, NULL, '"');
+                               subst = fr_value_box_list_aprint(NULL, in, NULL, &fr_value_escape_double);
                        }
                                goto do_splice;
 
@@ -794,7 +798,7 @@ char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap)
                                }
 
                                VP_VERIFY(in);
-                               subst = fr_pair_asprint(NULL, in, '"');
+                               fr_pair_aprint(NULL, &subst, in);
                        }
                                goto do_splice;
 
index 910f33f8655277ffdd2990203bc614f376b99880..dd9f024519baff98a836fc3961fc925f64a897e7 100644 (file)
@@ -1229,26 +1229,29 @@ fr_regmatch_t *regex_match_data_alloc(TALLOC_CTX *ctx, uint32_t count)
  * @param[out] out             Flag structure to populate.  Must be initialised to zero
  *                             if this is the first call to regex_flags_parse.
  * @param[in] in               Flag string to parse.
- * @param[in] len              Length of input string.
+ * @param[in] terminals                Terminal characters. If parsing ends before the buffer
+ *                             is exhausted, and is pointing to one of these chars
+ *                             it's not considered an error.
  * @param[in] err_on_dup       Error if the flag is already set.
  * @return
  *      - > 0 on success.  The number of flag bytes parsed.
  *     - <= 0 on failure.  Negative offset of first unrecognised flag.
  */
-ssize_t regex_flags_parse(int *err, fr_regex_flags_t *out, char const *in, size_t len, bool err_on_dup)
+ssize_t regex_flags_parse(int *err, fr_regex_flags_t *out, fr_sbuff_t *in,
+                         fr_sbuff_term_t const *terminals, bool err_on_dup)
 {
-       char const *p = in, *end = p + len;
+       fr_sbuff_t      our_in = FR_SBUFF_NO_ADVANCE(in);
 
        if (err) *err = 0;
 
-       while (p < end) {
-               switch (*p) {
+       while (fr_sbuff_extend(&our_in)) {
+               switch (*our_in.p) {
 #define DO_REGEX_FLAG(_f, _c) \
                case _c: \
                        if (err_on_dup && out->_f) { \
-                               fr_strerror_printf("Duplicate regex flag '%c'", *p); \
+                               fr_strerror_printf("Duplicate regex flag '%c'", *our_in.p); \
                                if (err) *err = -2; \
-                               return -(p - in); \
+                               return -fr_sbuff_used(&our_in); \
                        } \
                        out->_f = 1; \
                        break
@@ -1262,38 +1265,31 @@ ssize_t regex_flags_parse(int *err, fr_regex_flags_t *out, char const *in, size_
 #undef DO_REGEX_FLAG
 
                default:
-                       fr_strerror_printf("Unsupported regex flag '%c'", *p);
+                       if (fr_sbuff_is_terminal(&our_in, terminals)) return fr_sbuff_set(in, &our_in);
+
+                       fr_strerror_printf("Unsupported regex flag '%c'", *our_in.p);
                        if (err) *err = -1;
-                       return -(p - in);
+                       return -(fr_sbuff_used_total(&our_in));
                }
-               p++;
+               fr_sbuff_advance(&our_in, 1);
        }
-       return len;
+       return fr_sbuff_set(in, &our_in);
 }
 
 /** Print the flags
  *
- * @param[out] out     where to write flags.
- * @param[in] outlen   Space in output buffer.
+ * @param[out] sbuff   where to write flags.
  * @param[in] flags    to print.
  * @return
  *     - The number of bytes written to the out buffer.
  *     - A number >= outlen if truncation has occurred.
  */
-size_t regex_flags_snprint(char *out, size_t outlen, fr_regex_flags_t const flags[static REGEX_FLAG_BUFF_SIZE])
+ssize_t regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const flags[static REGEX_FLAG_BUFF_SIZE])
 {
-       char *p = out, *end = p + outlen;
+       fr_sbuff_t our_sbuff = FR_SBUFF_NO_ADVANCE(sbuff);
 
 #define DO_REGEX_FLAG(_f, _c) \
-       do { \
-               if (flags->_f) { \
-                       if ((end - p) <= 1) { \
-                               *end = '\0'; \
-                               return outlen + 1; \
-                       } \
-                       *p++ = _c; \
-               } \
-       } while(0)
+       if (flags->_f) FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, _c)
 
        DO_REGEX_FLAG(global, 'g');
        DO_REGEX_FLAG(ignore_case, 'i');
@@ -1303,6 +1299,6 @@ size_t regex_flags_snprint(char *out, size_t outlen, fr_regex_flags_t const flag
        DO_REGEX_FLAG(extended, 'x');
 #undef DO_REGEX_FLAG
 
-       return p - out;
+       return fr_sbuff_set(sbuff, &our_sbuff);
 }
 #endif
index ca3f9dbdbe5e4ef52565fc98a5594ed5e1608d8d..73e3b40ed81fc30107639e5e3d6cb29b75e0ee69 100644 (file)
@@ -29,6 +29,7 @@ extern "C" {
 
 #include <freeradius-devel/build.h>
 #include <freeradius-devel/missing.h>
+#include <freeradius-devel/util/sbuff.h>
 
 #include <stdbool.h>
 #include <stdint.h>
@@ -167,9 +168,11 @@ typedef struct {
 
 #define REGEX_FLAG_BUFF_SIZE   7
 
-ssize_t                regex_flags_parse(int *err, fr_regex_flags_t *out, char const *in, size_t len, bool err_on_dup);
-size_t         regex_flags_snprint(char *out, size_t outlen,
-                                   fr_regex_flags_t const flags[static REGEX_FLAG_BUFF_SIZE]);
+ssize_t                regex_flags_parse(int *err, fr_regex_flags_t *out, fr_sbuff_t *in,
+                                 fr_sbuff_term_t const *terminals, bool err_on_dup);
+
+ssize_t                regex_flags_print(fr_sbuff_t *sbuff, fr_regex_flags_t const flags[static REGEX_FLAG_BUFF_SIZE]);
+
 ssize_t                regex_compile(TALLOC_CTX *ctx, regex_t **out, char const *pattern, size_t len,
                              fr_regex_flags_t const *flags, bool subcaptures, bool runtime);
 int            regex_exec(regex_t *preg, char const *subject, size_t len, fr_regmatch_t *regmatch);
index cf01fd73dea9b40e0c75ab5d7fa07b372f1bd6eb..4a92231a0c7cad037ed79ed393aa8ae1dc58974b 100644 (file)
@@ -107,41 +107,6 @@ static inline CC_HINT(always_inline) ssize_t safecpy(char *o_start, char *o_end,
        return (i_len);
 }
 
-/** Copy function that only allows non-overlapping memory ranges to be copied
- *
- * This should be used in printing functions which may write more data
- * than was originally present in the buffer.
- *
- * @param[out] o_start         start of output buffer.
- * @param[in] o_end            end of the output buffer.
- * @param[in] i_start          start of the input buffer.
- * @param[in] i_end            end of data to copy.
- * @return
- *     - >0 the number of bytes copied.
- *      - 0 invalid args.
- *      - <0 the number of bytes we'd need to complete the copy.
- */
-static inline CC_HINT(always_inline) ssize_t safecpy_no_overlap(char *o_start, char *o_end,
-                                                               char const *i_start, char const *i_end)
-{
-       ssize_t diff;
-       size_t  i_len = i_end - i_start;
-
-       if (unlikely((o_end < o_start) || (i_end < i_start))) return 0; /* sanity check */
-
-       diff = (o_end - o_start) - (i_len);
-       if (diff < 0) return diff;
-
-       if (likely((i_start > o_end) || (i_end < o_start))) {   /* no-overlap */
-               memcpy(o_start,  i_start, i_len);
-       } else {
-               fr_strerror_printf("Address overlap %p-%p with %p-%p", o_start, o_end, i_start, i_end); /* overlap */
-               return 0;
-       }
-
-       return (i_len);
-}
-
 /** Update all markers and pointers in the set of sbuffs to point to new_buff
  *
  * This function should be used if the underlying buffer is realloced.
@@ -486,7 +451,7 @@ static inline bool fr_sbuff_terminal_search(fr_sbuff_t *in, char const *p,
                elem = term->elem[mid].str;
                tlen = strlen(elem);
 
-               ret = strncasecmp(p, elem, tlen < (size_t)remaining ? tlen : (size_t)remaining);
+               ret = strncmp(p, elem, tlen < (size_t)remaining ? tlen : (size_t)remaining);
                if (ret == 0) {
                        /*
                         *      If we have more text than the table element, that's fine
@@ -1293,7 +1258,7 @@ ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
        len = strlen(str);
        FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
 
-       safecpy_no_overlap(sbuff->p, sbuff->end, str, str + len);
+       safecpy(sbuff->p, sbuff->end, str, str + len);
        sbuff->p[len] = '\0';
 
        return fr_sbuff_advance(sbuff, len);
@@ -1316,7 +1281,7 @@ ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
 
        FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
 
-       safecpy_no_overlap(sbuff->p, sbuff->end, str, str + len);
+       safecpy(sbuff->p, sbuff->end, str, str + len);
        sbuff->p[len] = '\0';
 
        return fr_sbuff_advance(sbuff, len);
@@ -1342,7 +1307,7 @@ ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
 
        FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len);
 
-       safecpy_no_overlap(sbuff->p, sbuff->end, str, str + len);
+       safecpy(sbuff->p, sbuff->end, str, str + len);
        sbuff->p[len] = '\0';
 
        return fr_sbuff_advance(sbuff, len);
index f18453db530080338cb57cc93a89c09fd775659d..340ade1b90831b7fb3f4cbcb3a483c39dfb8073c 100644 (file)
@@ -157,6 +157,8 @@ typedef struct {
  *
  */
 typedef struct {
+       char const      *name;                          //!< Name for rule set to aid we debugging.
+
        char            chr;                            //!< Character at the start of an escape sequence.
        char const      subs[UINT8_MAX + 1];            //!< Special characters and their substitutions.
                                                        ///< Indexed by the printable representation i.e.
@@ -176,6 +178,8 @@ typedef struct {
  *
  */
 typedef struct {
+       char const      *name;                          //!< Name for rule set to aid we debugging.
+
        char            chr;                            //!< Character at the start of an escape sequence.
 
        char const      subs[UINT8_MAX + 1];            //!< Special characters and their substitutions.
@@ -304,19 +308,8 @@ extern bool const sbuff_char_alpha_num[UINT8_MAX + 1];
        [0x01] = true, [0x02] = true, [0x03] = true, [0x04] = true, [0x05] = true, [0x06] = true, [0x07] = true, [0x08] = true, \
        [0x09] = true, [0x0a] = true, [0x0b] = true, [0x0c] = true, [0x0d] = true, [0x0e] = true, [0x0f] = true, [0x10] = true, \
        [0x11] = true, [0x12] = true, [0x13] = true, [0x14] = true, [0x15] = true, [0x16] = true, [0x17] = true, [0x18] = true, \
-       [0x19] = true, [0x1a] = true, [0x1b] = true, [0x1c] = true, [0x1d] = true, [0x1e] = true, [0x1f] = true, [0x20] = true, \
-       [0x21] = true, [0x22] = true, [0x23] = true, [0x24] = true, [0x25] = true, [0x26] = true, [0x27] = true, [0x28] = true, \
-       [0x29] = true, [0x2a] = true, [0x2b] = true, [0x2c] = true, [0x2d] = true, [0x2e] = true, [0x2f] = true, [0x30] = true, \
-       [0x31] = true, [0x32] = true, [0x33] = true, [0x34] = true, [0x35] = true, [0x36] = true, [0x37] = true, [0x38] = true, \
-       [0x39] = true, [0x3a] = true, [0x3b] = true, [0x3c] = true, [0x3d] = true, [0x3e] = true, [0x3f] = true, [0x40] = true, \
-       [0x41] = true, [0x42] = true, [0x43] = true, [0x44] = true, [0x45] = true, [0x46] = true, [0x47] = true, [0x48] = true, \
-       [0x49] = true, [0x4a] = true, [0x4b] = true, [0x4c] = true, [0x4d] = true, [0x4e] = true, [0x4f] = true, [0x50] = true, \
-       [0x51] = true, [0x52] = true, [0x53] = true, [0x54] = true, [0x55] = true, [0x56] = true, [0x57] = true, [0x58] = true, \
-       [0x59] = true, [0x5a] = true, [0x5b] = true, [0x5c] = true, [0x5d] = true, [0x5e] = true, [0x5f] = true, [0x60] = true, \
-       [0x61] = true, [0x62] = true, [0x63] = true, [0x64] = true, [0x65] = true, [0x66] = true, [0x67] = true, [0x68] = true, \
-       [0x69] = true, [0x6a] = true, [0x6b] = true, [0x6c] = true, [0x6d] = true, [0x6e] = true, [0x6f] = true, [0x70] = true, \
-       [0x71] = true, [0x72] = true, [0x73] = true, [0x74] = true, [0x75] = true, [0x76] = true, [0x77] = true, [0x78] = true, \
-       [0x79] = true, [0x7a] = true, [0x7b] = true, [0x7c] = true, [0x7d] = true, [0x7e] = true, [0x7f] = true,
+       [0x19] = true, [0x1a] = true, [0x1b] = true, [0x1c] = true, [0x1d] = true, [0x1e] = true, [0x1f] = true, \
+       [0x7f] = true
 
 /** Unprintables (extended range)
  *
@@ -340,7 +333,7 @@ extern bool const sbuff_char_alpha_num[UINT8_MAX + 1];
        [0xe1] = true, [0xe2] = true, [0xe3] = true, [0xe4] = true, [0xe5] = true, [0xe6] = true, [0xe7] = true, [0xe8] = true, \
        [0xe9] = true, [0xea] = true, [0xeb] = true, [0xec] = true, [0xed] = true, [0xee] = true, [0xef] = true, [0xf0] = true, \
        [0xf1] = true, [0xf2] = true, [0xf3] = true, [0xf4] = true, [0xf5] = true, [0xf6] = true, [0xf7] = true, [0xf8] = true, \
-       [0xf9] = true, [0xfa] = true, [0xfb] = true, [0xfc] = true, [0xfd] = true, [0xfe] = true, [0xff] = true,
+       [0xf9] = true, [0xfa] = true, [0xfb] = true, [0xfc] = true, [0xfd] = true, [0xfe] = true, [0xff] = true
 
 /** Generic wrapper macro to return if there's insufficient memory to satisfy the request on the sbuff
  *
@@ -525,7 +518,7 @@ static inline fr_sbuff_t *fr_sbuff_init_file(fr_sbuff_t *sbuff, fr_sbuff_uctx_fi
  * @param[in] ctx      to allocate buffer in.
  * @param[out] sbuff   to initialise.
  * @param[out] tctx    to initialise.  Must have a lifetime >= to the sbuff.
- * @param[in] init     The length of the initial buffer.
+ * @param[in] init     The length of the initial buffer, excluding \0 byte.
  * @param[in] max      The maximum length of the buffer.
  * @return
  *     - The passed sbuff on success.
@@ -1250,8 +1243,6 @@ do { \
 /** Build a talloc wrapper function for a fr_sbuff_out_* function
  *
  * @param[in] _func    to call.
- * @param[in] _in      input sbuff arg.
- * @param[in] _len     expected output len.
  * @param[in] ...      additional arguments to pass to _func.
  */
 #define SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(_func, ...) \
index 2b0a7a9fab4c26f2959e109ad167b8bc03c88af9..1687c174edb3255c494256a87e2db42e06030aa6 100644 (file)
@@ -61,6 +61,7 @@ size_t fr_tokens_table_len = NUM_ELEMENTS(fr_tokens_table);
 fr_table_num_sorted_t const fr_token_quotes_table[] = {
        { L(""),        T_BARE_WORD             },
        { L("'"),       T_SINGLE_QUOTED_STRING  },
+       { L("/"),       T_SOLIDUS_QUOTED_STRING },
        { L("\""),      T_DOUBLE_QUOTED_STRING  },
        { L("`"),       T_BACK_QUOTED_STRING    }
 };
index 8acb43e7c3fe5514f1be64245fe8741a1dcda86a..d12fd46bc877bc29fcdeb209aaa1179d7e85a18d 100644 (file)
@@ -36,7 +36,7 @@
  *
  * - PRESENTATION format is what we print to the screen, and what we get from the user, databases
  *   and configuration files.
- *   - #fr_value_box_asprint is used to convert from INTERNAL to PRESENTATION format.
+ *   - #fr_value_box_aprint is used to convert from INTERNAL to PRESENTATION format.
  *   - #fr_value_box_from_str is used to convert from PRESENTATION to INTERNAL format.
  *
  * @copyright 2014-2017 The FreeRADIUS server project
@@ -290,6 +290,7 @@ size_t const fr_value_box_offsets[] = {
 };
 
 fr_sbuff_unescape_rules_t fr_value_unescape_double = {
+       .name = "double",
        .chr = '\\',
        .subs = {
                ['"'] = '"',    /* Quoting char */
@@ -308,6 +309,7 @@ fr_sbuff_unescape_rules_t fr_value_unescape_double = {
 };
 
 fr_sbuff_unescape_rules_t fr_value_unescape_single = {
+       .name = "single",
        .chr = '\\',
        .subs = {
                ['\''] = '\'',  /* Quoting char */
@@ -318,11 +320,11 @@ fr_sbuff_unescape_rules_t fr_value_unescape_single = {
 };
 
 fr_sbuff_unescape_rules_t fr_value_unescape_solidus = {
+       .name = "solidus",
        .chr = '\\',
        .subs = {
                ['%'] = '%',    /* xlat expansions */
                ['/'] = '/',    /* Quoting char */
-               ['\\'] = '\\',
                ['a'] = '\a',
                ['b'] = '\b',
                ['e'] = '\\',
@@ -331,14 +333,15 @@ fr_sbuff_unescape_rules_t fr_value_unescape_solidus = {
                ['t'] = '\t',
                ['v'] = '\v'
        },
-//     .skip = {
-//             ['\\'] = '\\'   /* Leave this for the regex library */
-//     },
+       .skip = {
+               ['\\'] = '\\'   /* Leave this for the regex library */
+       },
        .do_hex = true,
        .do_oct = true
 };
 
 fr_sbuff_unescape_rules_t fr_value_unescape_backtick = {
+       .name = "backtick",
        .chr = '\\',
        .subs = {
                ['%'] = '%',    /* xlat expansions */
@@ -364,6 +367,7 @@ fr_sbuff_unescape_rules_t *fr_value_unescape_by_quote[T_TOKEN_LAST] = {
 };
 
 fr_sbuff_escape_rules_t fr_value_escape_double = {
+       .name = "double",
        .chr = '\\',
        .subs = {
                ['"'] = '"',    /* Quoting char */
@@ -377,13 +381,15 @@ fr_sbuff_escape_rules_t fr_value_escape_double = {
                ['\v'] = 'v'
        },
        .esc = {
-               SBUFF_CHAR_UNPRINTABLES_LOW
+               SBUFF_CHAR_UNPRINTABLES_LOW,
+               SBUFF_CHAR_UNPRINTABLES_EXTENDED
        },
        .do_utf8 = true,
        .do_oct = true
 };
 
 fr_sbuff_escape_rules_t fr_value_escape_single = {
+       .name = "single",
        .chr = '\\',
        .subs = {
                ['\''] = '\'',  /* Quoting char */
@@ -393,6 +399,7 @@ fr_sbuff_escape_rules_t fr_value_escape_single = {
 };
 
 fr_sbuff_escape_rules_t fr_value_escape_solidus = {
+       .name = "solidus",
        .chr = '\\',
        .subs = {
                ['%'] = '%',    /* xlat expansions */
@@ -405,13 +412,15 @@ fr_sbuff_escape_rules_t fr_value_escape_solidus = {
                ['\v'] = 'v'
        },
        .esc = {
-               SBUFF_CHAR_UNPRINTABLES_LOW
+               SBUFF_CHAR_UNPRINTABLES_LOW,
+               SBUFF_CHAR_UNPRINTABLES_EXTENDED
        },
        .do_utf8 = true,
        .do_oct = true
 };
 
 fr_sbuff_escape_rules_t fr_value_escape_backtick = {
+       .name = "backtick",
        .chr = '\\',
        .subs = {
                ['%'] = '%',    /* xlat expansions */
@@ -425,7 +434,8 @@ fr_sbuff_escape_rules_t fr_value_escape_backtick = {
                ['\v'] = 'v'
        },
        .esc = {
-               SBUFF_CHAR_UNPRINTABLES_LOW
+               SBUFF_CHAR_UNPRINTABLES_LOW,
+               SBUFF_CHAR_UNPRINTABLES_EXTENDED
        },
        .do_utf8 = true,
        .do_oct = true
@@ -1813,7 +1823,7 @@ static inline int fr_value_box_cast_to_strvalue(TALLOC_CTX *ctx, fr_value_box_t
        {
                char *str;
 
-               str = fr_value_box_asprint(ctx, src, '\0');
+               fr_value_box_aprint(ctx, &str, src, NULL);
                if (unlikely(!str)) return -1;
 
                return fr_value_box_bstrdup_buffer_shallow(NULL, dst, dst_enumv, str, src->tainted);
@@ -3110,7 +3120,7 @@ static inline int fr_value_box_cast_to_date(TALLOC_CTX *ctx, fr_value_box_t *dst
  * @param dst          Where to write result of casting.
  * @param dst_type     to cast to.
  * @param dst_enumv    Aliases for values contained within this fr_value_box_t.
- *                     If #fr_value_box_t is passed to #fr_value_box_asprint
+ *                     If #fr_value_box_t is passed to #fr_value_box_aprint
  *                     names will be printed instead of actual value.
  * @param src          Input data.
  * @return
@@ -3408,7 +3418,7 @@ int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst,
  * @param vb           to cast.
  * @param dst_type     to cast to.
  * @param dst_enumv    Aliases for values contained within this fr_value_box_t.
- *                     If #fr_value_box_t is passed to #fr_value_box_asprint
+ *                     If #fr_value_box_t is passed to #fr_value_box_aprint
  *                     names will be printed instead of actual value.
  * @return
  *     - 0 on success.
@@ -4941,7 +4951,7 @@ finish:
        dst->tainted = tainted;
 
        /*
-        *      Fixup enumv
+        *      Fixup enumvs
         */
        dst->enumv = dst_enumv;
        dst->next = NULL;
@@ -4949,64 +4959,44 @@ finish:
        return 0;
 }
 
-/** Print one attribute value to a string
+/** Print one boxed value to a string
+ *
+ * This function should primarily when the #fr_value_box_t being serialized
+ * in some non-standard way, i.e. as a value for a field in a database.
  *
+ * @param[in] out      Where to write the printed string.
+ * @param[in] data     Value box to print.
+ * @param[in] e_rules  To apply to FR_TYPE_STRING types.
+ *                     Is not currently applied to any other box type.
  */
-char *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quote)
+ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
 {
-       char *p = NULL;
+       fr_sbuff_t      our_out = FR_SBUFF_NO_ADVANCE(out);
 
-       if (!fr_cond_assert(data->type != FR_TYPE_INVALID)) return NULL;
+       char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
+
+       if (!fr_cond_assert(data->type != FR_TYPE_INVALID)) return 0;
 
-       /*
-        *      _fr_box_with_da() uses in-line enumv's, and we don't
-        *      want to do dictionary lookups based on that.
-        */
        if (data->enumv && data->enumv->name) {
-               fr_dict_enum_t const    *dv;
+               char const *name;
 
-               dv = fr_dict_enum_by_value(data->enumv, data);
-               if (dv) return talloc_typed_strdup(ctx, dv->name);
+               name = fr_dict_enum_name_by_value(data->enumv, data);
+               if (name) {
+                       FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, name, NULL);
+                       goto done;
+               }
        }
 
        switch (data->type) {
        case FR_TYPE_STRING:
-       {
-               size_t len, ret;
-
-               if (!quote) {
-                       p = talloc_bstrndup(ctx, data->vb_strvalue, data->datum.length);
-                       if (!p) return NULL;
-                       talloc_set_type(p, char);
-                       return p;
-               }
-
-               /* Gets us the size of the buffer we need to alloc */
-               len = fr_snprint_len(data->vb_strvalue, data->datum.length, quote);
-               p = talloc_array(ctx, char, len);
-               if (!p) return NULL;
-
-               ret = fr_snprint(p, len, data->vb_strvalue, data->datum.length, quote);
-               if (!fr_cond_assert(ret == (len - 1))) {
-                       talloc_free(p);
-                       return NULL;
-               }
+               if (data->datum.length) FR_SBUFF_IN_ESCAPE_RETURN(&our_out,
+                                                                 data->vb_strvalue, data->datum.length, e_rules);
                break;
-       }
 
        case FR_TYPE_OCTETS:
-               p = talloc_array(ctx, char, 2 + 1 + data->datum.length * 2);
-               if (!p) return NULL;
-               p[0] = '0';
-               p[1] = 'x';
-
-               if (data->vb_octets && data->datum.length) {
-                       fr_bin2hex(&FR_SBUFF_OUT(p + 2, (data->datum.length * 2) + 1), &FR_DBUFF_TMP(data->vb_octets, data->datum.length),
-                                  SIZE_MAX);
-                       p[2 + (data->datum.length * 2)] = '\0';
-               } else {
-                       p[2] = '\0';
-               }
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, '0', 'x');
+               if (data->datum.length) FR_SBUFF_RETURN(fr_bin2hex, &our_out,
+                                                       &FR_DBUFF_TMP(data->vb_octets, data->datum.length), SIZE_MAX);
                break;
 
        /*
@@ -5017,110 +5007,143 @@ char *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quo
         *      An example is tunneled ipv4 in ipv6 addresses.
         */
        case FR_TYPE_IPV4_ADDR:
-       case FR_TYPE_IPV4_PREFIX:
-       {
-               char buff[INET_ADDRSTRLEN  + 4]; // + /prefix
-
-               buff[0] = '\0';
-               fr_value_box_snprint(buff, sizeof(buff), data, '\0');
-
-               p = talloc_typed_strdup(ctx, buff);
-       }
-       break;
-
        case FR_TYPE_IPV6_ADDR:
-       case FR_TYPE_IPV6_PREFIX:
-       {
-               char buff[INET6_ADDRSTRLEN + 4]; // + /prefix
-
-               buff[0] = '\0';
-               fr_value_box_snprint(buff, sizeof(buff), data, '\0');
+               if (!fr_inet_ntop(buf, sizeof(buf), &data->vb_ip)) return 0;
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, buf);
+               break;
 
-               p = talloc_typed_strdup(ctx, buff);
-       }
-       break;
+       case FR_TYPE_IPV4_PREFIX:
+       case FR_TYPE_IPV6_PREFIX:
+               if (!fr_inet_ntop_prefix(buf, sizeof(buf), &data->vb_ip)) return 0;
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, buf);
+               break;
 
        case FR_TYPE_IFID:
-               p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
-                                         (data->vb_ifid[0] << 8) | data->vb_ifid[1],
-                                         (data->vb_ifid[2] << 8) | data->vb_ifid[3],
-                                         (data->vb_ifid[4] << 8) | data->vb_ifid[5],
-                                         (data->vb_ifid[6] << 8) | data->vb_ifid[7]);
+               if (!fr_inet_ifid_ntop(buf, sizeof(buf), data->vb_ifid)) return 0;
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, buf);
                break;
 
        case FR_TYPE_ETHERNET:
-               p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
-                                         data->vb_ether[0], data->vb_ether[1],
-                                         data->vb_ether[2], data->vb_ether[3],
-                                         data->vb_ether[4], data->vb_ether[5]);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                          data->vb_ether[0], data->vb_ether[1],
+                                          data->vb_ether[2], data->vb_ether[3],
+                                          data->vb_ether[4], data->vb_ether[5]);
                break;
 
        case FR_TYPE_BOOL:
-               p = talloc_typed_strdup(ctx, data->vb_uint8 ? "yes" : "no");
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, data->vb_uint8 ? "yes" : "no");
                break;
 
        case FR_TYPE_UINT8:
-               p = talloc_typed_asprintf(ctx, "%u", data->vb_uint8);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", data->vb_uint8);
                break;
 
        case FR_TYPE_UINT16:
-               p = talloc_typed_asprintf(ctx, "%u", data->vb_uint16);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", data->vb_uint16);
                break;
 
        case FR_TYPE_UINT32:
-               p = talloc_typed_asprintf(ctx, "%u", data->vb_uint32);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", data->vb_uint32);
                break;
 
        case FR_TYPE_UINT64:
-               p = talloc_typed_asprintf(ctx, "%" PRIu64, data->vb_uint64);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIu64, data->vb_uint64);
                break;
 
        case FR_TYPE_INT8:
-               p = talloc_typed_asprintf(ctx, "%d", data->vb_int8);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%d", data->vb_int8);
                break;
 
        case FR_TYPE_INT16:
-               p = talloc_typed_asprintf(ctx, "%d", data->vb_int16);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%d", data->vb_int16);
                break;
 
        case FR_TYPE_INT32:
-               p = talloc_typed_asprintf(ctx, "%d", data->vb_int32);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%d", data->vb_int32);
                break;
 
        case FR_TYPE_INT64:
-               p = talloc_typed_asprintf(ctx, "%" PRId64, data->vb_int64);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRId64, data->vb_int64);
                break;
 
        case FR_TYPE_FLOAT32:
-               p = talloc_typed_asprintf(ctx, "%f", (double) data->vb_float32);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%f", (double) data->vb_float32);
                break;
 
        case FR_TYPE_FLOAT64:
-               p = talloc_typed_asprintf(ctx, "%g", data->vb_float64);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%g", data->vb_float64);
                break;
 
        case FR_TYPE_DATE:
        {
-               char buff[128];
+               int64_t         subseconds;
+               time_t          t;
+               struct tm       s_tm;
+               size_t          len;
+
+               t = fr_unix_time_to_sec(data->vb_date);
+               (void) gmtime_r(&t, &s_tm);
+
+               if (!data->enumv || (data->enumv->flags.type_size == FR_TIME_RES_SEC)) {
+                       len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S UTC", &s_tm);
+                       FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, buf, len);
+                       goto done;
+               }
+
+               len = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &s_tm);
+               FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, buf, len);
+               subseconds = data->vb_date % NSEC;
 
                /*
-                *      This is complex, so call another function to
-                *      do the work.
+                *      Use RFC 3339 format, which is a
+                *      profile of ISO8601.  The ISO standard
+                *      allows a much more complex set of date
+                *      formats.  The RFC is much stricter.
                 */
-               (void) fr_value_box_snprint(buff, sizeof(buff), data, quote);
-               p = talloc_typed_strdup(ctx, buff);
+               switch (data->enumv->flags.type_size) {
+               default:
+                       break;
+
+               case FR_TIME_RES_MSEC:
+                       subseconds /= 1000000;
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%03" PRIi64, subseconds);
+                       break;
+
+               case FR_TIME_RES_USEC:
+                       subseconds /= 1000;
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%06" PRIi64, subseconds);
+                       break;
+
+               case FR_TIME_RES_NSEC:
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, ".%09" PRIi64, subseconds);
+                       break;
+               }
+
+               /*
+                *      And time zone.
+                */
+               if (s_tm.tm_gmtoff != 0) {
+                       int hours, minutes;
+
+                       hours = s_tm.tm_gmtoff / 3600;
+                       minutes = (s_tm.tm_gmtoff / 60) % 60;
+
+                       FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%+03d:%02u", hours, minutes);
+               } else {
+                       FR_SBUFF_IN_CHAR_RETURN(&our_out, 'Z');
+               }
                break;
        }
 
        case FR_TYPE_SIZE:
-               p = talloc_typed_asprintf(ctx, "%zu", data->datum.size);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zu", data->datum.size);
                break;
 
        case FR_TYPE_TIME_DELTA:
        {
-               char *q;
-               uint64_t lhs, rhs;
-               fr_time_res_t res = FR_TIME_RES_SEC;
+               char            *q;
+               uint64_t        lhs, rhs;
+               fr_time_res_t   res = FR_TIME_RES_SEC;
 
                if (data->enumv) res = data->enumv->flags.type_size;
 
@@ -5147,15 +5170,13 @@ char *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quo
                        break;
                }
 
-               p = talloc_typed_asprintf(ctx, "%" PRIu64 ".%09" PRIu64, lhs, rhs);
+               FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIu64 ".%09" PRIu64, lhs, rhs);
+               q = fr_sbuff_current(&our_out) - 1;
 
                /*
                 *      Truncate trailing zeros.
                 */
-               q = p + strlen(p) - 1;
-               while (*q == '0') {
-                       *(q--) = '\0';
-               }
+               while (*q == '0') *(q--) = '\0';
 
                /*
                 *      If there's nothing after the decimal point,
@@ -5167,29 +5188,30 @@ char *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quo
                } else {
                        q++;    /* to account for q-- above */
                }
-
-               p = talloc_bstr_realloc(ctx, p, q - p);
+               fr_sbuff_set(&our_out, q);
        }
                break;
 
        case FR_TYPE_ABINARY:
-               p = talloc_array(ctx, char, 128);
-               if (!p) return NULL;
-               print_abinary(NULL, p, 128, (uint8_t const *) &data->datum.filter, data->datum.length, 0);
+               print_abinary(NULL, buf, sizeof(buf), (uint8_t const *) &data->datum.filter, data->datum.length, 0);
+               FR_SBUFF_IN_STRCPY_RETURN(&our_out, buf);
                break;
 
        case FR_TYPE_GROUP:
        {
-               fr_value_box_t vb;
+               fr_value_box_t  vb;
+               ssize_t         slen;
 
                memset(&vb, 0, sizeof(vb));
 
                /*
                 *      Be lazy by just converting it to a string, and then printing the string.
                 */
-               if (fr_value_box_cast_to_strvalue(NULL, &vb, FR_TYPE_STRING, NULL, data->vb_group) < 0) return NULL;
-               p = fr_value_box_asprint(ctx, &vb, quote);
+               if (fr_value_box_cast_to_strvalue(NULL, &vb, FR_TYPE_STRING, NULL, data->vb_group) < 0) return 0;
+
+               slen = fr_value_box_print(&our_out, &vb, e_rules);
                fr_value_box_clear(&vb);
+               if (slen < 0) return slen;
        }
                break;
 
@@ -5206,10 +5228,39 @@ char *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quo
        case FR_TYPE_VALUE_BOX:
        case FR_TYPE_BAD:
                (void)fr_cond_assert(0);
-               return NULL;
+               return 0;
+       }
+
+done:
+       return fr_sbuff_set(out, &our_out);
+}
+
+/** Print one boxed value to a string with quotes (where needed)
+ *
+ * @param[in] out      Where to write the printed string.
+ * @param[in] data     Value box to print.
+ * @param[in] quote    To apply to FR_TYPE_STRING types.
+ *                     Is not currently applied to any
+ *                     other box type.
+ */
+ssize_t fr_value_box_print_quoted(fr_sbuff_t *out, fr_value_box_t const *data, fr_token_t quote)
+{
+       fr_sbuff_t      our_out = FR_SBUFF_NO_ADVANCE(out);
+
+       if (quote == T_BARE_WORD) return fr_value_box_print(out, data, NULL);
+
+       switch (data->type) {
+       case FR_TYPE_QUOTED:
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, fr_token_quote[quote]);
+               FR_SBUFF_RETURN(fr_value_box_print, &our_out, data, fr_value_escape_by_quote[quote]);
+               FR_SBUFF_IN_CHAR_RETURN(&our_out, fr_token_quote[quote]);
+               break;
+
+       default:
+               return fr_value_box_print(out, data, NULL);
        }
 
-       return p;
+       return fr_sbuff_set(out, &our_out);
 }
 
 /** Concatenate a list of value boxes
@@ -5333,12 +5384,13 @@ int fr_value_box_list_concat(TALLOC_CTX *ctx,
  * @param[in] ctx      to allocate the buffer in.
  * @param[in] head     of the list of value boxes.
  * @param[in] delim    to insert between value box values.
- * @param[in] quote    character used set unescape mode.  @see fr_value_str_unescape.
+ * @param[in] e_rules  to control escaping of the concatenated elements.
  * @return
  *     - NULL on error.
  *     - The concatenation of the string values of the value box list on success.
  */
-char *fr_value_box_list_asprint(TALLOC_CTX *ctx, fr_value_box_t const *head, char const *delim, char quote)
+char *fr_value_box_list_aprint(TALLOC_CTX *ctx, fr_value_box_t const *head, char const *delim,
+                              fr_sbuff_escape_rules_t const *e_rules)
 {
        fr_value_box_t const    *vb = head;
        char                    *aggr, *td = NULL;
@@ -5346,7 +5398,7 @@ char *fr_value_box_list_asprint(TALLOC_CTX *ctx, fr_value_box_t const *head, cha
 
        if (!head) return NULL;
 
-       aggr = fr_value_box_asprint(ctx, vb, quote);
+       fr_value_box_aprint(ctx, &aggr, vb, e_rules);
        if (!aggr) return NULL;
        if (!vb->next) return aggr;
 
@@ -5360,7 +5412,7 @@ char *fr_value_box_list_asprint(TALLOC_CTX *ctx, fr_value_box_t const *head, cha
        while ((vb = vb->next)) {
                char *str, *new_aggr;
 
-               str = fr_value_box_asprint(pool, vb, quote);
+               fr_value_box_aprint(pool, &str, vb, e_rules);
                if (!str) continue;
 
                new_aggr = talloc_buffer_append_variadic_buffer(ctx, aggr, 2, td, str);
@@ -5424,8 +5476,7 @@ int fr_value_box_list_flatten_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box
        if (!argv) return -1;
 
        if (in->type != FR_TYPE_GROUP) {
-               argv[0] = fr_value_box_asprint(argv, in, 0);
-
+               fr_value_box_aprint(argv, &argv[0], in, NULL);
        } else {
                fr_value_box_t const *in_p;
 
@@ -5435,7 +5486,7 @@ int fr_value_box_list_flatten_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box
                for (in_p = in, i = 0;
                     in_p;
                     in_p = in_p->next) {
-                       argv[i] = fr_value_box_asprint(argv, in_p->vb_group, '\0');
+                       fr_value_box_aprint(argv, &argv[i], in_p->vb_group, NULL);
                        if (!argv[i]) {
                                talloc_free(argv);
                                return -1;
@@ -5525,7 +5576,7 @@ bool fr_value_box_list_tainted(fr_value_box_t const *head)
  *     - NULL if there is no member at given index
  *     - member if it exists
  */
-fr_value_box_t * fr_value_box_list_get(fr_value_box_t *head, int index)
+fr_value_box_t *fr_value_box_list_get(fr_value_box_t *head, int index)
 {
        int i = 0;
 
@@ -5536,342 +5587,3 @@ fr_value_box_t * fr_value_box_list_get(fr_value_box_t *head, int index)
 
        return head;
 }
-
-/** Print the value of an attribute to a string
- *
- * @note return value should be checked with is_truncated.
- * @note Will always \0 terminate unless outlen == 0.
- *
- * @param out Where to write the printed version of the attribute value.
- * @param outlen Length of the output buffer.
- * @param data to print.
- * @param quote char to escape in string output.
- * @return
- *     - The number of uint8s written to the out buffer.
- *     - A number >= outlen if truncation has occurred.
- */
-size_t fr_value_box_snprint(char *out, size_t outlen, fr_value_box_t const *data, char quote)
-{
-       char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
-       char const      *a = NULL;
-       char            *p = out, *end = p + outlen;
-       time_t          t;
-       struct tm       s_tm;
-
-       size_t          len = 0;
-
-       if (!data) return 0;
-
-       if (!fr_cond_assert(data->type != FR_TYPE_INVALID)) return -1;
-
-       if (outlen == 0) return data->datum.length;
-
-       *out = '\0';
-
-       p = out;
-
-       if (data->enumv && data->enumv->name) {
-               fr_dict_enum_t const    *dv;
-
-               dv = fr_dict_enum_by_value(data->enumv, data);
-               if (dv) return strlcpy(out, dv->name, outlen);
-       }
-
-       switch (data->type) {
-       case FR_TYPE_STRING:
-
-               /*
-                *      Ensure that WE add the quotation marks around the string.
-                */
-               if (quote) {
-                       if ((end - p) < 3) return data->datum.length + 2;
-
-                       *p++ = quote;
-
-                       len = fr_snprint(p, (end - p), data->vb_strvalue, data->datum.length, quote);
-                       /* always terminate the quoted string with another quote */
-                       if ((len + 1) >= (size_t)(end - p)) {
-                               /* Use out not p as we're operating on the entire buffer */
-                               out[outlen - 2] = (char) quote;
-                               out[outlen - 1] = '\0';
-                               return len + 2;
-                       }
-                       p += len;
-
-                       *p++ = (char) quote;
-                       *p = '\0';
-
-                       return len + 2;
-               }
-
-               return fr_snprint(out, outlen, data->vb_strvalue, data->datum.length, quote);
-
-       case FR_TYPE_IPV4_ADDR:
-       case FR_TYPE_IPV6_ADDR:
-               a = fr_inet_ntop(buf, sizeof(buf), &data->vb_ip);
-               len = strlen(buf);
-               break;
-
-       case FR_TYPE_IPV4_PREFIX:
-       case FR_TYPE_IPV6_PREFIX:
-               a = fr_inet_ntop_prefix(buf, sizeof(buf), &data->vb_ip);
-               len = strlen(buf);
-               break;
-
-       case FR_TYPE_IFID:
-               a = fr_inet_ifid_ntop(buf, sizeof(buf), data->vb_ifid);
-               len = strlen(buf);
-               break;
-
-       case FR_TYPE_ETHERNET:
-               return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
-                               data->vb_ether[0], data->vb_ether[1],
-                               data->vb_ether[2], data->vb_ether[3],
-                               data->vb_ether[4], data->vb_ether[5]);
-
-       case FR_TYPE_BOOL:
-               return snprintf(out, outlen, "%s", data->vb_bool ? "yes" : "no");
-
-       case FR_TYPE_UINT8:
-               return snprintf(out, outlen, "%u", data->vb_uint8);
-
-       case FR_TYPE_UINT16:
-               return snprintf(out, outlen, "%u", data->vb_uint16);
-
-       case FR_TYPE_UINT32:
-               return snprintf(out, outlen, "%u", data->vb_uint32);
-
-       case FR_TYPE_UINT64:
-               return snprintf(out, outlen, "%" PRIu64, data->vb_uint64);
-
-       case FR_TYPE_INT8:
-               return snprintf(out, outlen, "%d", data->vb_int8);
-
-       case FR_TYPE_INT16:
-               return snprintf(out, outlen, "%d", data->vb_int16);
-
-       case FR_TYPE_INT32:
-               return snprintf(out, outlen, "%d", data->vb_int32);
-
-       case FR_TYPE_INT64:
-               return snprintf(out, outlen, "%" PRId64, data->vb_int64);
-
-       case FR_TYPE_FLOAT32:
-               return snprintf(out, outlen, "%f", (double) data->vb_float32);
-
-       case FR_TYPE_FLOAT64:
-               return snprintf(out, outlen, "%g", data->vb_float64);
-
-       case FR_TYPE_DATE:
-               t = fr_unix_time_to_sec(data->vb_date);
-               (void) gmtime_r(&t, &s_tm);
-
-               if (!data->enumv || (data->enumv->flags.type_size == FR_TIME_RES_SEC)) {
-                       if (quote > 0) {
-                               len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S UTC", &s_tm);
-                       } else {
-                               len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S UTC", &s_tm);
-                       }
-
-               } else {
-                       int64_t subseconds;
-
-                       /*
-                        *      Print this out first, as it's always the same
-                        */
-                       if (quote > 0) {
-                               len = strftime(buf, sizeof(buf) - 1, "%%%Y-%m-%dT%H:%M:%S", &s_tm);
-                       } else {
-                               len = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &s_tm);
-                       }
-
-                       subseconds = data->vb_date % NSEC;
-
-                       /*
-                        *      Use RFC 3339 format, which is a
-                        *      profile of ISO8601.  The ISO standard
-                        *      allows a much more complex set of date
-                        *      formats.  The RFC is much stricter.
-                        */
-                       switch (data->enumv->flags.type_size) {
-                       default:
-                               break;
-
-                       case FR_TIME_RES_MSEC:
-                               subseconds /= 1000000;
-                               len += snprintf(buf + len, sizeof(buf) - len - 1, ".%03" PRIi64, subseconds);
-                               break;
-
-                       case FR_TIME_RES_USEC:
-                               subseconds /= 1000;
-                               len += snprintf(buf + len, sizeof(buf) - len - 1, ".%06" PRIi64, subseconds);
-                               break;
-
-                       case FR_TIME_RES_NSEC:
-                               len += snprintf(buf + len, sizeof(buf) - len - 1, ".%09" PRIi64, subseconds);
-                               break;
-                       }
-
-                       /*
-                        *      And time zone.
-                        */
-                       if (s_tm.tm_gmtoff != 0) {
-                               int hours, minutes;
-
-                               hours = s_tm.tm_gmtoff / 3600;
-                               minutes = (s_tm.tm_gmtoff / 60) % 60;
-
-                               len += snprintf(buf + len, sizeof(buf) - len - 1, "%+03d:%02u", hours, minutes);
-                       } else {
-                               buf[len] = 'Z';
-                               len++;
-                       }
-               }
-
-               if (quote > 0) {
-                       buf[0] = (char) quote;
-                       buf[len] = (char) quote;
-                       buf[len + 1] = '\0';
-                       len++;  /* Account for trailing quote */
-               }
-               a = buf;
-               break;
-
-       case FR_TYPE_ABINARY:
-               len = print_abinary(NULL, buf, sizeof(buf),
-                                   (uint8_t const *) data->datum.filter, data->datum.length, quote);
-               a = buf;
-               break;
-
-       case FR_TYPE_OCTETS:
-       case FR_TYPE_TLV:
-       {
-               size_t max;
-
-               /* Return the number of uint8s we would have written */
-               len = (data->datum.length * 2) + 2;
-               if ((end - p) <= 1) return len;
-
-
-               *p++ = '0';
-               if ((end - p) <= 1) {
-                       *p = '\0';
-                       return len;
-               }
-
-               *p++ = 'x';
-               if ((end - p) <= 2) {
-                       *p = '\0';
-                       return len;
-               }
-
-               /* Get maximum number of uint8s we can encode given (end - p) */
-               if (data->vb_octets && data->datum.length) {
-                       max = (((end - p) % 2) ? (end - p) - 1 : (end - p) - 2) / 2;
-                       fr_bin2hex(&FR_SBUFF_OUT(p, end),
-                                  &FR_DBUFF_TMP(data->vb_octets,
-                                                (size_t)data->datum.length > max ? max : (size_t)data->datum.length),
-                                  SIZE_MAX);
-               } else {
-                       *p = '\0';
-               }
-       }
-               return len;
-
-
-
-       case FR_TYPE_SIZE:
-               return snprintf(out, outlen, "%zu", data->datum.size);
-
-       case FR_TYPE_TIME_DELTA:
-       {
-               char            *q;
-               uint64_t        lhs, rhs;
-               fr_time_res_t   res = FR_TIME_RES_SEC;
-
-               if (data->enumv) res = data->enumv->flags.type_size;
-
-               switch (res) {
-               default:
-               case FR_TIME_RES_SEC:
-                       lhs = data->datum.time_delta / NSEC;
-                       rhs = data->datum.time_delta % NSEC;
-                       break;
-
-               case FR_TIME_RES_MSEC:
-                       lhs = data->datum.time_delta / 1000000;
-                       rhs = data->datum.time_delta % 1000000;
-                       break;
-
-               case FR_TIME_RES_USEC:
-                       lhs = data->datum.time_delta / 1000;
-                       rhs = data->datum.time_delta % 1000;
-                       break;
-
-               case FR_TIME_RES_NSEC:
-                       lhs = data->datum.time_delta;
-                       rhs = 0;
-                       break;
-               }
-
-               len = snprintf(buf, sizeof(buf), "%" PRIu64 ".%09" PRIu64, lhs, rhs);
-               a = buf;
-
-               /*
-                *      Truncate trailing zeros.
-                */
-               q = buf + len - 1;
-               while (*q == '0') {
-                       *(q--) = '\0';
-                       len--;
-               }
-
-               /*
-                *      If there's nothing after the decimal point,
-                *      trunctate the decimal point.  i.e. Don't print
-                *      "5."
-                */
-               if (*q == '.') {
-                       *q = '\0';
-                       len--;
-               }
-       }
-               break;
-
-       case FR_TYPE_GROUP:
-       {
-               fr_cursor_t cursor;
-               fr_value_box_t *vb;
-
-               fr_cursor_init(&cursor, &data->vb_group);
-               while ((vb = fr_cursor_current(&cursor)) != NULL) {
-                       len = fr_value_box_snprint(p, end - p, vb, quote);
-                       p += len;
-                       if (p >= end) break;
-                       fr_cursor_next(&cursor);
-               }
-       }
-               break;
-
-       /*
-        *      Don't add default here
-        */
-       case FR_TYPE_INVALID:
-       case FR_TYPE_COMBO_IP_ADDR:
-       case FR_TYPE_COMBO_IP_PREFIX:
-       case FR_TYPE_EXTENDED:
-       case FR_TYPE_VSA:
-       case FR_TYPE_VENDOR:
-       case FR_TYPE_STRUCT:
-       case FR_TYPE_VALUE_BOX:
-       case FR_TYPE_MAX:
-               (void)fr_cond_assert_msg(0, "%s: Can't print box of type \"%s\"", __FUNCTION__,
-                                        fr_table_str_by_value(fr_value_box_type_table, data->type, "?Unknown?"));
-               *out = '\0';
-               return 0;
-       }
-
-       if (a) strlcpy(out, a, outlen);
-
-       return len;     /* Return the number of uint8s we would of written (for truncation detection) */
-}
index fb40b5ade17cb0a3cee61fd2685d0f6c8bc929bd..5152138896be873c34c14d04de6146c7b6b36981 100644 (file)
@@ -726,7 +726,8 @@ int         fr_value_box_list_concat(TALLOC_CTX *ctx,
                                         fr_value_box_t *out, fr_value_box_t **list,
                                         fr_type_t type, bool free_input);
 
-char           *fr_value_box_list_asprint(TALLOC_CTX *ctx, fr_value_box_t const *head, char const *delim, char quote);
+char           *fr_value_box_list_aprint(TALLOC_CTX *ctx, fr_value_box_t const *head, char const *delim,
+                                        fr_sbuff_escape_rules_t const *e_rules);
 
 int            fr_value_box_list_acopy(TALLOC_CTX *ctx, fr_value_box_t **out, fr_value_box_t const *in);
 
@@ -740,9 +741,17 @@ int                fr_value_box_list_flatten_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_bo
 /*
  *     Printing
  */
-char           *fr_value_box_asprint(TALLOC_CTX *ctx, fr_value_box_t const *data, char quote);
+ssize_t                fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules);
 
-size_t         fr_value_box_snprint(char *out, size_t outlen, fr_value_box_t const *data, char quote);
+ssize_t                fr_value_box_print_quoted(fr_sbuff_t *out, fr_value_box_t const *data, fr_token_t quote);
+
+static inline size_t fr_value_box_aprint(TALLOC_CTX *ctx, char **out,
+                                        fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
+SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_value_box_print, data, e_rules)
+
+static inline size_t fr_value_box_aprint_quoted(TALLOC_CTX *ctx, char **out,
+                                               fr_value_box_t const *data, fr_token_t quote)
+SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_value_box_print_quoted, data, quote)
 
 /** @name Hashing
  *
index 9eafaab08fdd04a0ba6187bcadcd7eb91ad4f953..d1f331bc0cc15a31d99377d861946161c611e12a 100644 (file)
@@ -109,7 +109,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                 *      Allow the admin to explicitly set the reply
                 *      type.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_arp_operation, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_arp_operation);
                if (vp) {
                        request->reply->code = vp->vp_uint8;
                } else switch (rcode) {
index 8bfd33b88ee449e1b914ab60f821fcb5c54123c1..39d9b4ad837ec58022e035eb3974e1f4ac3ee2c3 100644 (file)
@@ -134,7 +134,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                /*
                 *      Allow for over-ride of reply code.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, inst->attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, inst->attr_packet_type);
                if (vp) request->reply->code = vp->vp_uint32;
 
                if (request->reply->code == FR_CODE_DO_NOT_RESPOND) {
index f12f603a1454b52e660792af194d928be4976cd5..104d4b77516f06578879f0072da72fe534a75307 100644 (file)
@@ -363,11 +363,9 @@ static RADIUS_PACKET *fr_dhcpv4_recv_raw_loop(int lsockfd,
 
                        if (reply->code == FR_DHCP_OFFER) {
                                VALUE_PAIR *vp1 = fr_pair_find_by_da(reply->vps,
-                                                                    attr_dhcp_dhcp_server_identifier,
-                                                                    TAG_ANY);
+                                                                    attr_dhcp_dhcp_server_identifier);
                                VALUE_PAIR *vp2 = fr_pair_find_by_da(reply->vps,
-                                                                    attr_dhcp_your_ip_address,
-                                                                    TAG_ANY);
+                                                                    attr_dhcp_your_ip_address);
 
                                if (vp1 && vp2) {
                                        nb_offer++;
@@ -589,7 +587,7 @@ static void dhcp_packet_debug(RADIUS_PACKET *packet, bool received)
             vp = fr_cursor_next(&cursor)) {
                VP_VERIFY(vp);
 
-               fr_pair_snprint(buffer, sizeof(buffer), vp);
+               fr_pair_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vp);
                printf("\t%s\n", buffer);
        }
 }
index d216efcaf86b47b433ec46f1c168ffde584d6a67..cb13b7241a48e0d4dbe8fa7794616c7e50ee60a0 100644 (file)
@@ -123,7 +123,7 @@ static void dhcpv4_packet_debug(REQUEST *request, RADIUS_PACKET *packet, bool re
 
                if (!*dhcp_header_attrs[i]) continue;
 
-               vp = fr_pair_find_by_da(packet->vps, *dhcp_header_attrs[i], TAG_ANY);
+               vp = fr_pair_find_by_da(packet->vps, *dhcp_header_attrs[i]);
                if (!vp) continue;
                RDEBUGX(L_DBG_LVL_1, "%pP", vp);
        }
@@ -185,7 +185,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                 *      Allow the admin to explicitly set the reply
                 *      type.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_message_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_message_type);
                if (vp) {
                        request->reply->code = vp->vp_uint8;
                } else switch (rcode) {
@@ -217,7 +217,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                 *      Offer and ACK MUST have YIADDR.
                 */
                if ((request->reply->code == FR_DHCP_OFFER) || (request->reply->code == FR_DHCP_ACK)) {
-                       vp = fr_pair_find_by_da(request->reply->vps, attr_yiaddr, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->reply->vps, attr_yiaddr);
                        if (!vp) {
                                REDEBUG("%s packet does not have YIADDR.  The client will not receive an IP address.",
                                        dhcp_message_types[request->reply->code]);
index f354b22cf931bbdc115823ca7bea70bd092c7b3d..93f6897b2c3a6700279aa84758c6a83ade7c9378 100644 (file)
@@ -156,7 +156,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                 *      Allow the admin to explicitly set the reply
                 *      type.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                if (vp) {
                        request->reply->code = vp->vp_uint32;
                } else switch (rcode) {
index bbdaa3dd248bc1d9650910911b99e191cdd621cc..12c6900afb49dc698486551922c4cbeb57549e06 100644 (file)
@@ -843,7 +843,7 @@ static int proto_ldap_cookie_load(TALLOC_CTX *ctx, uint8_t **cookie, rad_listen_
        {
                VALUE_PAIR *vp;
 
-               vp = fr_pair_find_by_da(request->reply->vps, attr_ldap_sync_cookie, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_ldap_sync_cookie);
                if (!vp) {
                        if (config->allow_refresh) RDEBUG2("No &reply:Cookie attribute found.  All entries matching "
                                                           "sync configuration will be returned");
index 58eea42abd368ece9085a186344e6ca289fe7743..f1b5a24f151f56ac26a81e81b1cd0a844a760f7b 100644 (file)
@@ -119,7 +119,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                /*
                 *      Run accounting foo { ... }
                 */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type);
                if (!vp) goto setup_send;
 
                dv = fr_dict_enum_by_value(vp->da, &vp->data);
@@ -176,7 +176,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                /*
                 *      Allow for over-ride of reply code.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                if (vp) request->reply->code = vp->vp_uint32;
 
                dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
index 45d7e949078fc7ef16fc16f0366c986dbcd84b7b..34827486b57edffd8a403ecc4dd78f9118b7a32c 100644 (file)
@@ -161,9 +161,9 @@ static char *auth_name(char *buf, size_t buflen, REQUEST *request)
        uint32_t        port = 0;       /* RFC 2865 NAS-Port is 4 bytes */
        char const      *tls = "";
 
-       cli = fr_pair_find_by_da(request->packet->vps, attr_calling_station_id, TAG_ANY);
+       cli = fr_pair_find_by_da(request->packet->vps, attr_calling_station_id);
 
-       pair = fr_pair_find_by_da(request->packet->vps, attr_nas_port, TAG_ANY);
+       pair = fr_pair_find_by_da(request->packet->vps, attr_nas_port);
        if (pair != NULL) port = pair->vp_uint32;
 
        if (request->packet->dst_port == 0) tls = " via proxy to virtual server";
@@ -207,21 +207,21 @@ static void CC_HINT(format (printf, 4, 5)) auth_message(proto_radius_auth_t cons
         * Get the correct username based on the configured value
         */
        if (!inst->log_stripped_names) {
-               username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+               username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        } else {
-               username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name, TAG_ANY);
-               if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+               username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name);
+               if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        }
 
        /*
         *      Clean up the password
         */
        if (inst->log_auth_badpass || inst->log_auth_goodpass) {
-               password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+               password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
                if (!password) {
                        VALUE_PAIR *auth_type;
 
-                       auth_type = fr_pair_find_by_da(request->control, attr_auth_type, TAG_ANY);
+                       auth_type = fr_pair_find_by_da(request->control, attr_auth_type);
                        if (auth_type) {
                                snprintf(password_buff, sizeof(password_buff), "<via Auth-Type = %s>",
                                         fr_dict_enum_name_by_value(auth_type->da, &auth_type->data));
@@ -229,7 +229,7 @@ static void CC_HINT(format (printf, 4, 5)) auth_message(proto_radius_auth_t cons
                        } else {
                                password_str = "<no User-Password attribute>";
                        }
-               } else if (fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY)) {
+               } else if (fr_pair_find_by_da(request->packet->vps, attr_chap_password)) {
                        password_str = "<CHAP-Password>";
                }
        }
@@ -334,7 +334,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                case RLM_MODULE_DISALLOW:
                default:
                        if ((vp = fr_pair_find_by_da(request->packet->vps,
-                                                    attr_module_failure_message, TAG_ANY)) != NULL) {
+                                                    attr_module_failure_message)) != NULL) {
                                auth_message(inst, request, false, "Invalid user (%pV)", &vp->data);
                        } else {
                                auth_message(inst, request, false, "Invalid user");
@@ -370,7 +370,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                         *      the "recv Access-Request" section
                         *      should have returned reject.
                         */
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_service_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_service_type);
                        if (vp && (vp->vp_uint32 == FR_SERVICE_TYPE_VALUE_AUTHORIZE_ONLY)) {
                                RDEBUG("Skipping authenticate as we have found %pP", vp);
                                request->reply->code = FR_CODE_ACCESS_ACCEPT;
@@ -380,7 +380,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                        /*
                         *      Allow for over-ride of reply code.
                         */
-                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                        if (vp) {
                                request->reply->code = vp->vp_uint32;
                                goto setup_send;
@@ -460,7 +460,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                        /*
                         *      Maybe the shared secret is wrong?
                         */
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_user_password);
                        if (vp) {
                                if (RDEBUG_ENABLED2) {
                                        uint8_t const *p;
@@ -493,12 +493,12 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                /*
                 *      Allow for over-ride of reply code.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                if (vp) request->reply->code = vp->vp_uint32;
 
        setup_send:
                if (!request->reply->code) {
-                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                        if (vp) {
                                request->reply->code = vp->vp_uint32;
                        } else {
@@ -515,7 +515,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                 *      "send Access-Challenge" section.
                 */
                if ((request->reply->code == FR_CODE_ACCESS_CHALLENGE) &&
-                   !(vp = fr_pair_find_by_da(request->reply->vps, attr_state, TAG_ANY))) {
+                   !(vp = fr_pair_find_by_da(request->reply->vps, attr_state))) {
                        uint8_t buffer[16];
 
                        fr_rand_buffer(buffer, sizeof(buffer));
@@ -648,14 +648,14 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                 *      we're sending an accept.
                 */
                if (request->reply->code == FR_CODE_ACCESS_ACCEPT) {
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_module_success_message, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_module_success_message);
                        if (vp){
                                auth_message(inst, request, true, "Login OK (%pV)", &vp->data);
                        } else {
                                auth_message(inst, request, true, "Login OK");
                        }
                } else if (request->reply->code == FR_CODE_ACCESS_REJECT) {
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_module_failure_message, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_module_failure_message);
                        if (vp) {
                                auth_message(inst, request, false, "Login incorrect (%pV)", &vp->data);
                        } else {
index 32e4eb31ee12a85fd0d376fda33acec551e257c5..7b65c4b11ad8848dd0892c95d3672cdad4dd5640 100644 (file)
@@ -82,8 +82,8 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                 *      re-authorization requests.
                 */
                if (request->packet->code == FR_CODE_COA_REQUEST) {
-                       vp = fr_pair_find_by_da(request->reply->vps, attr_service_type, TAG_ANY);
-                       if (vp && !fr_pair_find_by_da(request->reply->vps, attr_state, TAG_ANY)) {
+                       vp = fr_pair_find_by_da(request->reply->vps, attr_service_type);
+                       if (vp && !fr_pair_find_by_da(request->reply->vps, attr_state)) {
                                REDEBUG("CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
                                request->reply->code = FR_CODE_COA_NAK;
                                goto nak;
@@ -135,7 +135,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                /*
                 *      Allow for over-ride of reply code.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                if (vp) request->reply->code = vp->vp_uint32;
 
        nak:
index 60a60e23a398736357e910d2edd93971ceebb206..51bcf1a4186abd735c65e6debcb1d87d9d55aa7e 100644 (file)
@@ -157,16 +157,16 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                if (request->reply->code == FR_CODE_ACCESS_ACCEPT) {
                        VALUE_PAIR *vp;
 
-                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix);
                        if (!vp) {
                                ERROR("The 'control' list MUST contain a FreeRADIUS-Client.. IP address attribute");
                                goto deny;
                        }
 
-                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_secret, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_secret);
                        if (!vp) {
                                ERROR("The 'control' list MUST contain a FreeRADIUS-Client-Secret attribute");
                                goto deny;
index 5fac929bde39987ef79b0c6c7da1130185643856..83723a568bc91de001d423023f62f92dc5f24e78 100644 (file)
@@ -408,7 +408,7 @@ static int mod_instantiate(void *instance, CONF_SECTION *cs)
 
        MEM(inst->packet = talloc_zero_array(inst, uint8_t, inst->max_packet_size));
 
-       vp = fr_pair_find_by_da(vps, attr_packet_type, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_packet_type);
        if (vp) code = vp->vp_uint32;
 
        /*
index c7d66fb25333949ae140c56de1d1dfb343a2599e..7396cfe38bdc642d060788b1a0c0a56360e8ce27 100644 (file)
@@ -106,7 +106,7 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                /*
                 *      Allow for over-ride of reply code.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                if (vp) request->reply->code = vp->vp_uint32;
 
                dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
index d4493f5fd2e3be6b678c155b01cfff2a5b1a28b9..f85a896d8a229bc4a696e894835d910ca2b896ff 100644 (file)
@@ -247,7 +247,7 @@ static int mod_decode(void const *instance, REQUEST *request, uint8_t *const dat
        case FR_TAC_PLUS_AUTHEN:
                if (packet_is_authen_start_request(pkt)) {
                        request->packet->code = FR_PACKET_TYPE_VALUE_AUTHENTICATION_START;
-               } else {        
+               } else {
                        request->packet->code = FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE;
                }
                break;
@@ -365,7 +365,7 @@ static int mod_decode(void const *instance, REQUEST *request, uint8_t *const dat
                if (client->active &&
                    ((pkt->hdr.flags & FR_TACACS_FLAGS_VALUE_UNENCRYPTED) == 0) &&
                    RDEBUG_ENABLED2 &&
-                   ((vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_user_name, TAG_ANY)) != NULL) &&
+                   ((vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_user_name)) != NULL) &&
                    (fr_utf8_str((uint8_t const *) vp->vp_strvalue, vp->vp_length) < 0)) {
                        RWDEBUG("Unprintable characters in the %s. "
                                "Double-check the shared secret on the server "
@@ -680,7 +680,7 @@ static void mod_unload(void)
 }
 
 fr_app_t proto_tacacs = {
-       .magic                  = RLM_MODULE_INIT,      
+       .magic                  = RLM_MODULE_INIT,
        .name                   = "tacacs",
        .config                 = proto_tacacs_config,
        .inst_size              = sizeof(proto_tacacs_t),
index 1b5a4c83fddc0733274c74e84751d7ae392a00d2..f95b45b41ae526b950111268e3b6eacb8a20d7f8 100644 (file)
@@ -100,7 +100,7 @@ static void accounting_failed(REQUEST *request, char const *msg)
        /*
         *      Set the server reply message.
         */
-       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message)) {
                MEM(pair_update_reply(&vp, attr_tacacs_server_message) >= 0);
                fr_pair_value_strdup(vp, "Accounting failed");
        }
@@ -194,7 +194,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                /*
                 *      Run accounting foo { ... }
                 */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_accounting_flags, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_accounting_flags);
                if (!vp) goto setup_send;
 
                dv = fr_dict_enum_by_value(vp->da, &vp->data);
index 0e14e2b534e06b935b1cfd9c254688022fdc6107..d5856c9af12cab9518d9dc71199ccd1da0290682 100644 (file)
@@ -130,7 +130,7 @@ static void authentication_failed(REQUEST *request, char const *msg)
        /*
         *      Set the server reply message.  Note that we do not tell the user *why* they failed authentication.
         */
-       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message)) {
                MEM(pair_update_reply(&vp, attr_tacacs_server_message) >= 0);
                fr_pair_value_strdup(vp, "Authentication failed");
        }
@@ -253,7 +253,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                 *      No Auth-Type, force it to reject.
                 */
                if (!auth_type) {
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_authentication_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_tacacs_authentication_type);
                        if (!vp) {
                                authentication_failed(request, "No Auth-Type or TACACS-Authentication-Type configured: rejecting authentication.");
                                goto setup_send;
@@ -272,7 +272,7 @@ static rlm_rcode_t mod_process(module_ctx_t const *mctx, REQUEST *request)
                         *      Use that name to search for a matching Auth-Type which has been defined.
                         */
                        dv = fr_dict_enum_by_name(attr_auth_type, dv->name, -1);
-                       if (!dv) {      
+                       if (!dv) {
                                authentication_failed(request, "No Auth-Type found to match TACACS-Authentication-Type: rejecting authentication.");
                                goto setup_send;
                        }
index 5c91dda2966f3e633a1e2e6ac823eacd4e039a06..db0ff5c429b0fcda4aa1a39c76365153ff1a4c0b 100644 (file)
@@ -84,7 +84,7 @@ static void authorization_failed(REQUEST *request, char const *msg)
        /*
         *      Set the server reply message.  Note that we do not tell the user *why* they failed authentication.
         */
-       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->reply->vps, attr_tacacs_server_message)) {
                MEM(pair_update_reply(&vp, attr_tacacs_server_message) >= 0);
                fr_pair_value_strdup(vp, "Authentication failed");
        }
index 84ee342873d79eb7fe6a7bdbbf9e0a102e7efa20..09d4c23805f0362a4dc968d223874584bf9f4f4b 100644 (file)
@@ -159,10 +159,10 @@ static rlm_rcode_t mod_process(UNUSED module_ctx_t const *mctx, REQUEST *request
                if (request->reply->code == CLIENT_ADD) {
                        VALUE_PAIR *vp;
 
-                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix, TAG_ANY);
-                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix);
+                       if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix);
                        if (!vp) {
                                ERROR("The 'control' list MUST contain a FreeRADIUS-Client.. IP address attribute");
                                goto deny;
index 4680a7dfc451c1ca5ac97ba0c89733f043851cf3..96bc8db6ddf3794ce97cafacf8947caaff5907b1 100644 (file)
@@ -300,7 +300,7 @@ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config
        /*
         *      Encode the entry created date
         */
-       tmpl_init(&created_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
+       tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
        fr_value_box_init(&created_value.data.literal, FR_TYPE_DATE, NULL, true);
        tmpl_value(&created_value)->vb_date = c->created;
 
@@ -310,7 +310,7 @@ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config
         *      Although Redis objects expire on their own, we still need this
         *      to ignore entries that were created before the last epoch.
         */
-       tmpl_init(&expires_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
+       tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
        fr_value_box_init(&expires_value.data.literal, FR_TYPE_DATE, NULL, true);
        tmpl_value(&expires_value)->vb_date = c->expires;
        expires.next = c->maps; /* Head of the list */
index 604223675e02866742ffb132da2b928c8c2d8210..39784f3019e22a7f3e09e74c7a2176570d0d5324 100644 (file)
@@ -171,7 +171,7 @@ static rlm_rcode_t cache_merge(rlm_cache_t const *inst, REQUEST *request, rlm_ca
                if (map_to_request(request, map, map_to_vp, NULL) < 0) {
                        char buffer[1024];
 
-                       map_snprint(NULL, buffer, sizeof(buffer), map);
+                       map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
                        REXDENT();
                        RDEBUG2("Skipping %s", buffer);
                        RINDENT();
@@ -391,7 +391,7 @@ static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_c
                                }
 
                                MEM(c_map->rhs = tmpl_alloc(c_map,
-                                                           TMPL_TYPE_DATA, map->rhs->name, map->rhs->len, quote));
+                                                           TMPL_TYPE_DATA, quote, map->rhs->name, map->rhs->len));
                                if (fr_value_box_copy(c_map->rhs, tmpl_value(c_map->rhs), &vp->data) < 0) {
                                        REDEBUG("Failed copying attribute value");
                                error:
@@ -407,7 +407,7 @@ static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_c
                         *      which is a combination of the LHS list and the attribute.
                         */
                        case TMPL_TYPE_LIST:
-                               if (tmpl_attr_afrom_list(c_map, &c_map->lhs, map->lhs, vp->da, vp->tag) < 0) {
+                               if (tmpl_attr_afrom_list(c_map, &c_map->lhs, map->lhs, vp->da) < 0) {
                                        RPERROR("Failed attribute -> list copy");
                                        goto error;
                                }
@@ -427,7 +427,7 @@ static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_c
        /*
         *      Check to see if we need to merge the entry into the request
         */
-       vp = fr_pair_find_by_da(request->control, attr_cache_merge_new, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_cache_merge_new);
        if (vp && vp->vp_bool) merge = true;
 
        if (merge) cache_merge(inst, request, c);
@@ -564,7 +564,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(module_ctx_t const *mctx, REQUE
         *      If Cache-Status-Only == yes, only return whether we found a
         *      valid cache entry
         */
-       vp = fr_pair_find_by_da(request->control, attr_cache_status_only, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_cache_status_only);
        if (vp && vp->vp_bool) {
                RINDENT();
                RDEBUG3("status-only: yes");
@@ -584,13 +584,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(module_ctx_t const *mctx, REQUE
        /*
         *      Figure out what operation we're doing
         */
-       vp = fr_pair_find_by_da(request->control, attr_cache_allow_merge, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_cache_allow_merge);
        if (vp) merge = vp->vp_bool;
 
-       vp = fr_pair_find_by_da(request->control, attr_cache_allow_insert, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_cache_allow_insert);
        if (vp) insert = vp->vp_bool;
 
-       vp = fr_pair_find_by_da(request->control, attr_cache_ttl, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_cache_ttl);
        if (vp) {
                if (vp->vp_int32 == 0) {
                        expire = true;
@@ -808,7 +808,9 @@ static ssize_t cache_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t freespace,
                              request, inst->config.key, NULL, NULL);
        if (key_len < 0) return -1;
 
-       slen = tmpl_afrom_attr_substr(ctx, NULL, &target, fmt, -1,
+       slen = tmpl_afrom_attr_substr(ctx, NULL, &target,
+                                     &FR_SBUFF_IN(fmt, strlen(fmt)),
+                                     NULL,
                                      &(tmpl_rules_t){
                                                .dict_def = request->dict,
                                                .prefix = TMPL_ATTR_REF_PREFIX_AUTO
@@ -837,10 +839,9 @@ static ssize_t cache_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t freespace,
 
        for (map = c->maps; map; map = map->next) {
                if ((tmpl_da(map->lhs) != tmpl_da(target)) ||
-                   (tmpl_tag(map->lhs) != tmpl_tag(target)) ||
                    (tmpl_list(map->lhs) != tmpl_list(target))) continue;
 
-               *out = fr_value_box_asprint(request, tmpl_value(map->rhs), '\0');
+               fr_value_box_aprint(request, out, tmpl_value(map->rhs), NULL);
                ret = talloc_array_length(*out) - 1;
                break;
        }
index 9d841d25d45b86eb15c53e50c2a52ef4b1946e18..5acefb54eaf2808293378986a9a304e5d7f1036b 100644 (file)
@@ -46,7 +46,7 @@ int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t const *c)
 
        char            *to_store = NULL;
 
-       to_store = talloc_typed_asprintf(ctx, "&Cache-Expires = %pV\n&Cache-Created = %pV\n",
+       to_store = talloc_typed_asprintf(ctx, "Cache-Expires = %pV\nCache-Created = %pV\n",
                                         fr_box_date(c->expires), fr_box_date(c->created));
        if (!to_store) return -1;
 
@@ -65,16 +65,16 @@ int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t const *c)
 
        for (map = c->maps; map; map = map->next) {
                char    *value;
-               size_t  len;
+               ssize_t slen;
 
-               len = tmpl_snprint(NULL, attr, sizeof(attr), map->lhs);
-               if (is_truncated(len, sizeof(attr))) {
+               slen = tmpl_print(&FR_SBUFF_OUT(attr, sizeof(attr)), map->lhs, TMPL_ATTR_REF_PREFIX_NO, NULL);
+               if (slen < 0) {
                        fr_strerror_printf("Serialized attribute too long.  Must be < " STRINGIFY(sizeof(attr)) " "
-                                          "bytes, got %zu bytes", len);
+                                          "bytes, needed %zu additional bytes", (size_t)(slen * -1));
                        goto error;
                }
 
-               value = fr_value_box_asprint(value_pool, tmpl_value(map->rhs), '\'');
+               fr_value_box_aprint(value_pool, &value, tmpl_value(map->rhs), &fr_value_escape_single);
                if (!value) goto error;
 
                to_store = talloc_asprintf_append_buffer(to_store, "%s %s %s\n", attr,
@@ -112,7 +112,8 @@ int cache_deserialize(rlm_cache_entry_t *c, fr_dict_t const *dict, char *in, ssi
        while (((size_t)(p - in)) < (size_t)inlen) {
                vp_map_t        *map = NULL;
                tmpl_rules_t parse_rules = {
-                                       .dict_def = dict
+                                       .dict_def = dict,
+                                       .prefix = TMPL_ATTR_REF_PREFIX_NO
                                };
 
                q = strchr(p, '\n');
index db69e5a8264104c2e35c1211845ec6a7ee42ac12..c0c2ea7e4e89bd76aa0f27d24a838c35dd55adbe 100644 (file)
@@ -105,7 +105,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        VALUE_PAIR              *vp;
        rlm_chap_t const        *inst = talloc_get_type_abort_const(mctx->instance, rlm_chap_t);
 
-       if (fr_pair_find_by_da(request->control, attr_auth_type, TAG_ANY) != NULL) {
+       if (fr_pair_find_by_da(request->control, attr_auth_type) != NULL) {
                RDEBUG3("Auth-Type is already set.  Not setting 'Auth-Type := %s'", inst->name);
                return RLM_MODULE_NOOP;
        }
@@ -114,7 +114,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
         *      This case means the warnings below won't be printed
         *      unless there's a CHAP-Password in the request.
         */
-       if (!fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY)) return RLM_MODULE_NOOP;
+       if (!fr_pair_find_by_da(request->packet->vps, attr_chap_password)) return RLM_MODULE_NOOP;
 
        /*
         *      Create the CHAP-Challenge if it wasn't already in the packet.
@@ -122,7 +122,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
         *      This is so that the rest of the code does not need to
         *      understand CHAP.
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_chap_challenge);
        if (!vp) {
                RDEBUG2("Creating &%s from request authenticator", attr_chap_challenge->name);
 
@@ -159,13 +159,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        fr_dict_attr_t const    *allowed_passwords[] = { attr_cleartext_password };
        bool                    ephemeral;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) {
                REDEBUG("&User-Name attribute is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
-       chap = fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY);
+       chap = fr_pair_find_by_da(request->packet->vps, attr_chap_password);
        if (!chap) {
                REDEBUG("You set '&control:Auth-Type = CHAP' for a request that "
                        "does not contain a CHAP-Password attribute!");
@@ -212,7 +212,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
                size_t          length;
                VALUE_PAIR      *vp;
 
-               vp = fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_chap_challenge);
                if (vp) {
                        RDEBUG2("Using challenge from &request:CHAP-Challenge");
                        p = vp->vp_octets;
index 4bcafa50d751aacbaf5b29b7652d0a9092e4efef..ecbb628e8e2c67ea8ee7dd01ead65546ac166577 100644 (file)
@@ -587,7 +587,7 @@ json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
                /* debug */
                RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
                /* get standard value */
-               fr_pair_value_snprint(value, sizeof(value), vp, 0);
+               fr_pair_print_value_quoted(&FR_SBUFF_OUT(value, sizeof(value)), vp, T_BARE_WORD);
                /* return string value from above */
                return json_object_new_string(value);
        }
@@ -627,7 +627,7 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
        }
 
        /* get current event timestamp */
-       if ((vp = fr_pair_find_by_da(vps, attr_event_timestamp, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_da(vps, attr_event_timestamp)) != NULL) {
                /* get seconds value from attribute */
                ts = fr_time_to_sec(vp->vp_date);
        } else {
@@ -641,7 +641,7 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
        memset(value, 0, sizeof(value));
 
        /* get elapsed session time */
-       if ((vp = fr_pair_find_by_da(vps, attr_acct_session_time, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_da(vps, attr_acct_session_time)) != NULL) {
                /* calculate diff */
                ts = (ts - vp->vp_uint32);
                /* calculate start time */
index 00e5d191cd7ba8bcff7a355d50015ac8ddac1d2c..e6c41d1cf39bf7cff2bac042118ea98cc81f45d8 100644 (file)
@@ -252,7 +252,7 @@ static rlm_rcode_t mod_accounting(module_ctx_t const *mctx, REQUEST *request)
        fr_assert(request->packet != NULL);
 
        /* sanity check */
-       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type)) == NULL) {
                /* log debug */
                RDEBUG2("could not find status type in packet");
                /* return */
@@ -332,7 +332,7 @@ static rlm_rcode_t mod_accounting(module_ctx_t const *mctx, REQUEST *request)
        switch (status) {
        case FR_STATUS_START:
                /* add start time */
-               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type)) != NULL) {
                        /* add to json object */
                        json_object_object_add(cookie->jobj, "startTimestamp",
                                               mod_value_pair_to_json_object(request, vp));
@@ -341,7 +341,7 @@ static rlm_rcode_t mod_accounting(module_ctx_t const *mctx, REQUEST *request)
 
        case FR_STATUS_STOP:
                /* add stop time */
-               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_event_timestamp, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_event_timestamp)) != NULL) {
                        /* add to json object */
                        json_object_object_add(cookie->jobj, "stopTimestamp",
                                               mod_value_pair_to_json_object(request, vp));
index dd334a03f99f3e69f51420e2292dd7726d593316..6e6592bd6b08c0c3f806cd35e774732f04233854 100644 (file)
@@ -62,7 +62,7 @@ typedef struct {
        rbtree_t        *tree;
        fr_trie_t       *trie;
 
-       tmpl_t  *key;
+       tmpl_t          *key;
        vp_map_t        *map;           //!< if there is an "update" section in the configuration.
 } rlm_csv_t;
 
index 1b078c46013e6b5ffcc2cfa5e7036c0db2de914a..c86fb42a4aee16875bd2fb0d9aefcfbd8ec7b311 100644 (file)
@@ -135,14 +135,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
         *      We require access to the plain-text password, or to the
         *      Digest-HA1 parameter.
         */
-       passwd = fr_pair_find_by_da(request->control, attr_digest_ha1, TAG_ANY);
+       passwd = fr_pair_find_by_da(request->control, attr_digest_ha1);
        if (passwd) {
                if (passwd->vp_length != 32) {
                        REDEBUG("Digest-HA1 has invalid length, authentication failed");
                        return RLM_MODULE_INVALID;
                }
        } else {
-               passwd = fr_pair_find_by_da(request->control, attr_cleartext_password, TAG_ANY);
+               passwd = fr_pair_find_by_da(request->control, attr_cleartext_password);
        }
        if (!passwd) {
                REDEBUG("Cleartext-Password or Digest-HA1 is required for authentication");
@@ -152,7 +152,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        /*
         *      We require access to the Digest-Nonce-Value
         */
-       nonce = fr_pair_find_by_da(request->packet->vps, attr_digest_nonce, TAG_ANY);
+       nonce = fr_pair_find_by_da(request->packet->vps, attr_digest_nonce);
        if (!nonce) {
                REDEBUG("No Digest-Nonce: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -161,7 +161,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        /*
         *      A1 = Digest-User-Name ":" Realm ":" Password
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_user_name, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_user_name);
        if (!vp) {
                REDEBUG("No Digest-User-Name: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -172,7 +172,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        a1[a1_len] = ':';
        a1_len++;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_realm, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_realm);
        if (!vp) {
                REDEBUG("No Digest-Realm: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -198,7 +198,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
         *      See which variant we calculate.
         *      Assume MD5 if no Digest-Algorithm attribute received
         */
-       algo = fr_pair_find_by_da(request->packet->vps, attr_digest_algorithm, TAG_ANY);
+       algo = fr_pair_find_by_da(request->packet->vps, attr_digest_algorithm);
        if ((!algo) ||
            (strcasecmp(algo->vp_strvalue, "MD5") == 0)) {
                /*
@@ -243,7 +243,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
                a1[a1_len] = ':';
                a1_len++;
 
-               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_cnonce, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_cnonce);
                if (!vp) {
                        REDEBUG("No Digest-CNonce: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -271,7 +271,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        /*
         *      A2 = Digest-Method ":" Digest-URI
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_method, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_method);
        if (!vp) {
                REDEBUG("No Digest-Method: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -282,7 +282,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        a2[a2_len] = ':';
        a2_len++;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_uri, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_uri);
        if (!vp) {
                REDEBUG("No Digest-URI: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -293,7 +293,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        /*
         *  QOP is "auth-int", tack on ": Digest-Body-Digest"
         */
-       qop = fr_pair_find_by_da(request->packet->vps, attr_digest_qop, TAG_ANY);
+       qop = fr_pair_find_by_da(request->packet->vps, attr_digest_qop);
        if (qop) {
                if (strcasecmp(qop->vp_strvalue, "auth-int") == 0) {
                        VALUE_PAIR *body;
@@ -307,7 +307,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
                        /*
                         *  Must be a hex representation of an MD5 digest.
                         */
-                       body = fr_pair_find_by_da(request->packet->vps, attr_digest_body_digest, TAG_ANY);
+                       body = fr_pair_find_by_da(request->packet->vps, attr_digest_body_digest);
                        if (!body) {
                                REDEBUG("No Digest-Body-Digest: Cannot perform Digest authentication");
                                return RLM_MODULE_INVALID;
@@ -370,7 +370,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_nonce_count, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_nonce_count);
                if (!vp) {
                        REDEBUG("No Digest-Nonce-Count: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -381,7 +381,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_cnonce, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_digest_cnonce);
                if (!vp) {
                        REDEBUG("No Digest-CNonce: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -423,7 +423,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        /*
         *      Get the binary value of Digest-Response
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_response, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_digest_response);
        if (!vp) {
                REDEBUG("No Digest-Response attribute in the request.  Cannot perform digest authentication");
                return RLM_MODULE_INVALID;
index c5d9dc61826b8cf88de5c0bb9246d378656285c6..34d2a27b2e46e4bf1059664ea4c0062c9fd070aa 100644 (file)
@@ -259,7 +259,7 @@ static eap_type_t eap_process_nak(module_ctx_t const *mctx, REQUEST *request,
         *      Pick one type out of the one they asked for,
         *      as they may have asked for many.
         */
-       vp = fr_pair_find_by_da(request->control, attr_eap_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_eap_type);
        for (i = 0; i < nak->length; i++) {
                /*
                 *      Type 0 is valid, and means there are no
@@ -540,7 +540,7 @@ static rlm_rcode_t eap_method_select(module_ctx_t const *mctx, eap_session_t *ea
                /*
                 *      Allow per-user configuration of EAP types.
                 */
-               vp = fr_pair_find_by_da(eap_session->request->control, attr_eap_type, TAG_ANY);
+               vp = fr_pair_find_by_da(eap_session->request->control, attr_eap_type);
                if (vp) {
                        RDEBUG2("Using method from &control:EAP-Type");
                        next = vp->vp_uint32;
@@ -670,7 +670,7 @@ static rlm_rcode_t mod_authenticate(module_ctx_t const *mctx, REQUEST *request)
        eap_packet_raw_t        *eap_packet;
        rlm_rcode_t             rcode;
 
-       if (!fr_pair_find_by_da(request->packet->vps, attr_eap_message, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->packet->vps, attr_eap_message)) {
                REDEBUG("You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!");
                return RLM_MODULE_INVALID;
        }
@@ -863,7 +863,7 @@ static rlm_rcode_t mod_post_proxy(module_ctx_t const *mctx, REQUEST *request)
                        eap_session_destroy(&eap_session);
                }
 
-               username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+               username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
 
                /*
                 *      If it's an Access-Accept, RFC 2869, Section 2.3.1
@@ -901,12 +901,12 @@ static rlm_rcode_t mod_post_auth(module_ctx_t const *mctx, REQUEST *request)
         *      says that we MUST include a User-Name attribute in the
         *      Access-Accept.
         */
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if ((request->reply->code == FR_CODE_ACCESS_ACCEPT) && username) {
                /*
                 *      Doesn't exist, add it in.
                 */
-               vp = fr_pair_find_by_da(request->reply->vps, attr_user_name, TAG_ANY);
+               vp = fr_pair_find_by_da(request->reply->vps, attr_user_name);
                if (!vp) {
                        vp = fr_pair_copy(request->reply, username);
                        fr_pair_add(&request->reply->vps, vp);
@@ -931,12 +931,12 @@ static rlm_rcode_t mod_post_auth(module_ctx_t const *mctx, REQUEST *request)
         */
        if (request->reply->code != FR_CODE_ACCESS_REJECT) return RLM_MODULE_NOOP;
 
-       if (!fr_pair_find_by_da(request->packet->vps, attr_eap_message, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->packet->vps, attr_eap_message)) {
                RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
 
-       if (fr_pair_find_by_da(request->reply->vps, attr_eap_message, TAG_ANY)) {
+       if (fr_pair_find_by_da(request->reply->vps, attr_eap_message)) {
                RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
index 2e046fc09f727129aa0fa79e8e5dcf0fadb24445..d63be6487c06a4685bda46c5b70c630f9a894f29 100644 (file)
@@ -669,7 +669,7 @@ static FR_CODE eap_fast_eap_payload(REQUEST *request, eap_session_t *eap_session
        switch (fake->reply->code) {
        case 0:                 /* No reply code, must be proxied... */
 #ifdef WITH_PROXY
-               vp = fr_pair_find_by_da(fake->control, attr_proxy_to_realm, TAG_ANY);
+               vp = fr_pair_find_by_da(fake->control, attr_proxy_to_realm);
                if (vp) {
                        int                     ret;
                        eap_tunnel_data_t       *tunnel;
index c2c016cf169068ae6dae28e285cf9e272ab6a4a2..d35e0998623920fb3c09064f6ca46a9d944abe33 100644 (file)
@@ -575,7 +575,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         *      EAP-TLS-Require-Client-Cert attribute will override
         *      the require_client_cert configuration option.
         */
-       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert);
        if (vp) {
                client_cert = vp->vp_uint32 ? true : false;
        } else {
index 4f94ebb2c4c6ec7a07b82934f5be38de550d7bf0..837538108fb69c99b19182ab43883af806e7139a 100644 (file)
@@ -720,7 +720,7 @@ packet_ready:
                 *      in the user name, THEN discard the user name.
                 */
                if (inst->with_ntdomain_hack &&
-                   ((auth_challenge = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY)) != NULL) &&
+                   ((auth_challenge = fr_pair_find_by_da(request->packet->vps, attr_user_name)) != NULL) &&
                    ((username = memchr(auth_challenge->vp_octets, '\\', auth_challenge->vp_length)) != NULL)) {
                        /*
                         *      Wipe out the NT domain.
@@ -786,13 +786,13 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         */
        if (!fr_cond_assert(parent)) return RLM_MODULE_FAIL;
 
-       auth_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_challenge, TAG_ANY);
+       auth_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_challenge);
        if (auth_challenge && (auth_challenge->vp_length != MSCHAPV2_CHALLENGE_LEN)) {
                RWDEBUG("&parent.control:MS-CHAP-Challenge is incorrect length.  Ignoring it");
                auth_challenge = NULL;
        }
 
-       peer_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_peer_challenge, TAG_ANY);
+       peer_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_peer_challenge);
        if (peer_challenge && (peer_challenge->vp_length != MSCHAPV2_CHALLENGE_LEN)) {
                RWDEBUG("&parent.control:MS-CHAP-Peer-Challenge is incorrect length.  Ignoring it");
                peer_challenge = NULL;
@@ -801,7 +801,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
        if (auth_challenge) {
                created_auth_challenge = false;
 
-               peer_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_peer_challenge, TAG_ANY);
+               peer_challenge = fr_pair_find_by_da(parent->control, attr_ms_chap_peer_challenge);
                if (peer_challenge && (peer_challenge->vp_length != MSCHAPV2_CHALLENGE_LEN)) {
                        RWDEBUG("&parent.control:MS-CHAP-Peer-Challenge is incorrect length.  Ignoring it");
                        peer_challenge = NULL;
index 17b1cf293a24b76e1c96dd213533c08d52485fe1..2b55ccc099a12a7fff77514ac37477845ef33cb6 100644 (file)
@@ -265,7 +265,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         *      EAP-TLS-Require-Client-Cert attribute will override
         *      the require_client_cert configuration option.
         */
-       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert);
        if (vp) {
                client_cert = vp->vp_uint32 ? true : false;
        } else {
index 6803b8bf9367947c3a2450ca5bd63485c665458d..048c5e679934e057a97606659e3e61e585704a6d 100644 (file)
@@ -505,7 +505,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         *      The admin can dynamically change the MTU.
         */
        session->mtu = inst->fragment_size;
-       vp = fr_pair_find_by_da(request->packet->vps, attr_framed_mtu, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_framed_mtu);
 
        /*
         *      session->mtu is *our* MTU.  We need to subtract off the EAP
index 6f691f9afc4b0ded23fc00f71530443cce21e5cc..9253448971a04190b39ebf7e6cd76f46cf249733 100644 (file)
@@ -147,7 +147,7 @@ static rlm_rcode_t eap_tls_virtual_server(rlm_eap_tls_t *inst, REQUEST *request,
        VALUE_PAIR      *vp;
 
        /* set the virtual server to use */
-       vp = fr_pair_find_by_da(request->control, attr_virtual_server, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_virtual_server);
        if (vp) {
                server_cs = virtual_server_find(vp->vp_strvalue);
                if (!server_cs) {
@@ -267,7 +267,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         *      EAP-TLS-Require-Client-Cert attribute will override
         *      the require_client_cert configuration option.
         */
-       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert);
        if (vp) {
                client_cert = vp->vp_uint32 ? true : false;
        } else {
index b6edc9a1c2a563f4ba632431a95ab48378d1bf8f..009fcf87f4dbf8caa6c9efa7075446e84346796e 100644 (file)
@@ -277,7 +277,7 @@ static rlm_rcode_t mod_session_init(module_ctx_t const *mctx, REQUEST *request)
         *      EAP-TLS-Require-Client-Cert attribute will override
         *      the require_client_cert configuration option.
         */
-       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_eap_tls_require_client_cert);
        if (vp) {
                client_cert = vp->vp_uint32 ? true : false;
        } else {
index 4607e7585c512755ff6faaaf8a57c24d38cf8f16..66bef2d01b298f92430908a52a7c65f07a8cbb71 100644 (file)
@@ -649,14 +649,14 @@ FR_CODE eap_ttls_process(REQUEST *request, eap_session_t *eap_session, fr_tls_se
        /*
         *      No User-Name, try to create one from stored data.
         */
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) {
                /*
                 *      No User-Name in the stored data, look for
                 *      an EAP-Identity, and pull it out of there.
                 */
                if (!t->username) {
-                       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_message, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_message);
                        if (vp &&
                            (vp->vp_length >= EAP_HEADER_LEN + 2) &&
                            (vp->vp_strvalue[0] == FR_EAP_CODE_RESPONSE) &&
index 1fdbe756ba7bf4ee13682e900a288c0fa02a4af3..6341b8f5d4ebd0c1a267fcac81193d0e139b9199 100644 (file)
@@ -229,13 +229,8 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf)
 
        if (!inst->program) return 0;
 
-       /*
-        *      Parse the program to execute into a template.
-        */
-       MEM(inst->tmpl = tmpl_alloc(inst, TMPL_TYPE_EXEC, inst->program, strlen(inst->program), T_BACK_QUOTED_STRING));
-
-       slen = xlat_tokenize_argv(inst->tmpl, &tmpl_xlat(inst->tmpl), inst->program, strlen(inst->program),
-                                 &(tmpl_rules_t) { .dict_def = fr_dict_internal() });
+       slen = tmpl_afrom_substr(inst, &inst->tmpl, &FR_SBUFF_IN(inst->program, strlen(inst->program)),
+                                T_BACK_QUOTED_STRING, NULL, &(tmpl_rules_t) { .dict_def = fr_dict_internal() });
        if (slen <= 0) {
                char *spaces, *text;
 
index 22afa48259fc0a180c938e8bb2cce4823e6afd95..a780c0ed631355a5dcc5b3dd2a8ada9707ebdb11 100644 (file)
@@ -59,7 +59,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED module_ctx_t const *mct
 {
        VALUE_PAIR *vp, *check_item = NULL;
 
-       check_item = fr_pair_find_by_da(request->control, attr_expiration, TAG_ANY);
+       check_item = fr_pair_find_by_da(request->control, attr_expiration);
        if (check_item != NULL) {
                uint32_t left;
 
index 146858701be57cad720eca0b802e8605257757bd..e372e6a6ae9a2f5488a6e56934c4d949fde6b113 100644 (file)
@@ -158,11 +158,26 @@ static bool get_expression(REQUEST *request, char const **string, int64_t *answe
 
 static bool get_number(REQUEST *request, char const **string, int64_t *answer)
 {
-       int64_t x;
-       bool invert = false;
-       bool negative = false;
-       char const *p = *string;
-       tmpl_t *vpt = NULL;
+       fr_sbuff_term_t const           bareword_terminals =
+                                       FR_SBUFF_TERMS(
+                                               L("\t"),
+                                               L("\n"),
+                                               L(" "),
+                                               L("%"),
+                                               L("&"),
+                                               L(")"),
+                                               L("+"),
+                                               L("-"),
+                                               L("/"),
+                                               L("^"),
+                                               L("|")
+                                       );
+       fr_sbuff_parse_rules_t const    p_rules = { .terminals = &bareword_terminals };
+       int64_t                         x;
+       bool                            invert = false;
+       bool                            negative = false;
+       char const                      *p = *string;
+       tmpl_t                          *vpt = NULL;
 
        /*
         *      Look for a number.
@@ -205,7 +220,10 @@ static bool get_number(REQUEST *request, char const **string, int64_t *answer)
                VALUE_PAIR      *vp;
                fr_cursor_t     cursor;
 
-               slen = tmpl_afrom_attr_substr(request, NULL, &vpt, p, -1, &(tmpl_rules_t){ .dict_def = request->dict });
+               slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
+                                             &FR_SBUFF_IN(p, strlen(p)),
+                                             &p_rules,
+                                             &(tmpl_rules_t){ .dict_def = request->dict });
                if (slen <= 0) {
                        RPEDEBUG("Failed parsing attribute name '%s'", p);
                        return false;
index 01bfebf01ea3a9911ad05e5265439efaddd2367a..c0c114767c724cccbdd978058cbc55579fc239de 100644 (file)
@@ -81,7 +81,7 @@ fr_dict_attr_autoload_t rlm_files_dict_attr[] = {
 static int fall_through(VALUE_PAIR *vp)
 {
        VALUE_PAIR *tmp;
-       tmp = fr_pair_find_by_da(vp, attr_fall_through, TAG_ANY);
+       tmp = fr_pair_find_by_da(vp, attr_fall_through);
 
        return tmp ? tmp->vp_uint32 : 0;
 }
index 0a6f06d463e06bbdf60a9ced1c21a165491a92d3..cafdac06689b132acd7ee32238e8bb6ed1a700b1 100644 (file)
@@ -126,8 +126,8 @@ static rlm_rcode_t CC_HINT(nonnull(1,2)) mod_authenticate(module_ctx_t const *mc
                return RLM_MODULE_FAIL;
        }
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        if (!username) {
                REDEBUG("Attribute \"User-Name\" is required for authentication");
index c77b524db771648f50b351e8866dbe53f950fcba..270fcf1051bfc40b89b59f4a7eda6cfabcfd0ea2 100644 (file)
@@ -1429,7 +1429,7 @@ static int parse_host(rlm_isc_dhcp_tokenizer_t *state, rlm_isc_dhcp_info_t *info
        /*
         *      The 'host' entry might not have a client identifier option.
         */
-       vp = fr_pair_find_by_da(info->options, attr_client_identifier, TAG_ANY);
+       vp = fr_pair_find_by_da(info->options, attr_client_identifier);
        if (vp) {
                my_uid = talloc_zero(info, isc_host_uid_t);
                my_uid->client = &vp->data;
@@ -1620,7 +1620,7 @@ static rlm_isc_dhcp_info_t *get_host(REQUEST *request, fr_hash_table_t *hosts_by
         *      If that doesn't match, use client hardware
         *      address.
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_client_identifier, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_client_identifier);
        if (vp) {
                isc_host_uid_t *client, my_client;
 
@@ -1634,7 +1634,7 @@ static rlm_isc_dhcp_info_t *get_host(REQUEST *request, fr_hash_table_t *hosts_by
        }
 
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_client_hardware_address, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_client_hardware_address);
        if (!vp) return NULL;
 
        memcpy(&my_ether.ether, vp->vp_ether, sizeof(my_ether.ether));
@@ -1758,7 +1758,7 @@ static int apply_fixed_ip(rlm_isc_dhcp_t const *inst, REQUEST *request)
        /*
         *      If there's already a fixed IP, don't do anything
         */
-       yiaddr = fr_pair_find_by_da(request->reply->vps, attr_your_ip_address, TAG_ANY);
+       yiaddr = fr_pair_find_by_da(request->reply->vps, attr_your_ip_address);
        if (yiaddr) return 0;
 
        host = get_host(request, inst->hosts_by_ether, inst->hosts_by_uid);
@@ -1812,7 +1812,7 @@ static int apply(rlm_isc_dhcp_t const *inst, REQUEST *request, rlm_isc_dhcp_info
        VALUE_PAIR *yiaddr;
 
        rcode = 0;
-       yiaddr = fr_pair_find_by_da(request->reply->vps, attr_your_ip_address, TAG_ANY);
+       yiaddr = fr_pair_find_by_da(request->reply->vps, attr_your_ip_address);
 
        /*
         *      First, apply any "host" options
@@ -1894,7 +1894,7 @@ recurse:
                     vp = fr_cursor_next(&option_cursor)) {
                        VALUE_PAIR *reply;
 
-                       reply = fr_pair_find_by_da(request->reply->vps, vp->da, TAG_ANY);
+                       reply = fr_pair_find_by_da(request->reply->vps, vp->da);
                        if (reply) continue;
 
                        /*
index 5b6137fc662f6bcb8b84106a61592a32cf577bf5..1fa233be97b36b3735099854f5d1e1641691df79 100644 (file)
@@ -226,8 +226,10 @@ static xlat_action_t json_encode_xlat(TALLOC_CTX *ctx, fr_cursor_t *out, REQUEST
                }
 
                /* Decode next attr template */
-               slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt, p, -1,
-                       &(tmpl_rules_t){ .dict_def = request->dict });
+               slen = tmpl_afrom_attr_substr(ctx, NULL, &vpt,
+                                             &FR_SBUFF_IN(p, strlen(p)),
+                                             NULL,
+                                             &(tmpl_rules_t){ .dict_def = request->dict });
 
                if (slen <= 0) {
                        REMARKER(start, (p-start) + (-slen), "Invalid input");
index 0069c309b22a018d46e889cec4931942cef692e5..82107034158d5332d02bda929730798ba2462241 100644 (file)
@@ -249,7 +249,7 @@ static rlm_rcode_t krb5_parse_user(krb5_principal *client, KRB5_UNUSED rlm_krb5_
        char *princ_name;
        VALUE_PAIR *username;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
 
        /*
         *      We can only authenticate user requests which HAVE
@@ -328,7 +328,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        krb5_principal          client = NULL;
        VALUE_PAIR              *password;
 
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        if (!password) {
                REDEBUG("Attribute \"User-Password\" is required for authentication");
@@ -423,7 +423,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        char                    *nonconst_password;             /* compiler warnings */
        VALUE_PAIR              *password;
 
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        if (!password) {
                REDEBUG("Attribute \"User-Password\" is required for authentication");
index e568b1b3f9db613edccf893c3d8bd14f5d565b8e..5840da6fb83d154aef79280acd180b4615160685 100644 (file)
@@ -729,8 +729,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        fr_ldap_sasl_t          sasl;
        VALUE_PAIR *username, *password;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
@@ -1021,7 +1021,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        /*
         *      We already have a Cleartext-Password.  Skip edir.
         */
-       if (fr_pair_find_by_da(request->control, attr_cleartext_password, TAG_ANY)) goto skip_edir;
+       if (fr_pair_find_by_da(request->control, attr_cleartext_password)) goto skip_edir;
 
        /*
         *      Retrieve Universal Password if we use eDirectory
index d4f0cac013580bdc712284751c3ef8f25c2de170..dedd9b0cbb41ac5b9d998581ab08ec9ae65e92de 100644 (file)
@@ -90,7 +90,7 @@ char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, fr_ldap
         *      If the caller isn't looking for the result we can just return the current userdn value.
         */
        if (!force) {
-               vp = fr_pair_find_by_da(request->control, attr_ldap_userdn, TAG_ANY);
+               vp = fr_pair_find_by_da(request->control, attr_ldap_userdn);
                if (vp) {
                        RDEBUG2("Using user DN from request \"%pV\"", &vp->data);
                        *rcode = RLM_MODULE_OK;
@@ -267,11 +267,11 @@ void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request, fr_ldap_conn
        */
        if (!inst->expect_password || !RDEBUG_ENABLED2) return;
 
-       if (!fr_pair_find_by_da(request->control, attr_cleartext_password, TAG_ANY) &&
-           !fr_pair_find_by_da(request->control, attr_nt_password, TAG_ANY) &&
-           !fr_pair_find_by_da(request->control, attr_user_password, TAG_ANY) &&
-           !fr_pair_find_by_da(request->control, attr_password_with_header, TAG_ANY) &&
-           !fr_pair_find_by_da(request->control, attr_crypt_password, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->control, attr_cleartext_password) &&
+           !fr_pair_find_by_da(request->control, attr_nt_password) &&
+           !fr_pair_find_by_da(request->control, attr_user_password) &&
+           !fr_pair_find_by_da(request->control, attr_password_with_header) &&
+           !fr_pair_find_by_da(request->control, attr_crypt_password)) {
                switch (conn->directory->type) {
                case FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY:
                        RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
index c545860dd14d4a793b8b5a129f5df1a42628e867..2651e844e2f486cac8002e6979816a0e3ef10d79 100644 (file)
@@ -448,7 +448,7 @@ static size_t linelog_escape_func(UNUSED REQUEST *request,
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(module_ctx_t const *mctx, REQUEST *request)
 {
-       rlm_linelog_t const     *inst = talloc_get_type_abort_const(mctx->instance, rlm_linelog_t);
+       rlm_linelog_t const             *inst = talloc_get_type_abort_const(mctx->instance, rlm_linelog_t);
        linelog_conn_t                  *conn;
        fr_time_delta_t                 timeout = 0;
        char                            buff[4096];
@@ -510,7 +510,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(module_ctx_t const *mctx, REQ
                tmpl_str = cf_pair_value(cp);
                if (!tmpl_str || (tmpl_str[0] == '\0')) {
                        RDEBUG2("Path \"%s\" resolves to an empty config pair", p);
-                       vpt_p = tmpl_init(&empty, TMPL_TYPE_UNRESOLVED, "", 0, T_DOUBLE_QUOTED_STRING);
+                       vpt_p = tmpl_init_shallow(&empty, TMPL_TYPE_UNRESOLVED, T_DOUBLE_QUOTED_STRING, "", 0);
                        goto build_vector;
                }
 
@@ -518,9 +518,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(module_ctx_t const *mctx, REQ
                 *      Alloc a template from the value of the CONF_PAIR
                 *      using request as the context (which will hopefully avoid an alloc).
                 */
-               slen = tmpl_afrom_str(request, &vpt, tmpl_str, talloc_array_length(tmpl_str) - 1,
-                                     cf_pair_value_quote(cp),
-                                     &(tmpl_rules_t){ .allow_unknown = true, .allow_unresolved = true }, true);
+               slen = tmpl_afrom_substr(request, &vpt,
+                                        &FR_SBUFF_IN(tmpl_str, talloc_array_length(tmpl_str) - 1),
+                                        cf_pair_value_quote(cp),
+                                        NULL,
+                                        &(tmpl_rules_t){
+                                               .allow_unknown = true,
+                                               .allow_unresolved = true
+                                        });
                if (slen <= 0) {
                        REMARKER(tmpl_str, -slen, "%s", fr_strerror());
                        return RLM_MODULE_FAIL;
@@ -571,14 +576,13 @@ build_vector:
                        switch (vp->vp_type) {
                        case FR_TYPE_OCTETS:
                        case FR_TYPE_STRING:
-                               vector[i].iov_base = vp->vp_ptr;
                                vector[i].iov_len = vp->vp_length;
+                               vector[i].iov_base = vp->vp_ptr;
                                break;
 
                        default:
-                               p = fr_pair_value_asprint(vector, vp, '\0');
+                               vector[i].iov_len = fr_value_box_aprint(vector, &p, &vp->data, NULL);
                                vector[i].iov_base = p;
-                               vector[i].iov_len = talloc_array_length(p) - 1;
                                break;
                        }
 
index 8656bddf0f1c514f41e8b9b0157b47af2626c2c5..b84d759daf95f33bec673f5d00539d3276ec4fd7 100644 (file)
@@ -157,7 +157,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        VALUE_PAIR              *ends, *vp;
        int32_t                 left;
 
-       ends = fr_pair_find_by_da(request->control, attr_login_time, TAG_ANY);
+       ends = fr_pair_find_by_da(request->control, attr_login_time);
        if (!ends) return RLM_MODULE_NOOP;
 
        /*
index 2430fd53d0f3c8f58680fe31d6c277546a25c5ae..8fb2d1cc6a35fadfc1e8758709e04948c75df847 100644 (file)
@@ -95,16 +95,16 @@ static int fr_lua_marshall(REQUEST *request, lua_State *L, VALUE_PAIR const *vp)
        case FR_TYPE_ABINARY:
        {
                char    buff[128];
-               size_t  len;
+               size_t  slen;
 
-               len = fr_pair_value_snprint(buff, sizeof(buff), vp, '\0');
-               if (is_truncated(len, sizeof(buff))) {
+               slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buff, sizeof(buff)), vp, T_BARE_WORD);
+               if (slen < 0) {
                        REDEBUG("Cannot convert %s to Lua type, insufficient buffer space",
                                fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
                        return -1;
                }
 
-               lua_pushlstring(L, buff, len);
+               lua_pushlstring(L, buff, (size_t)slen);
        }
                break;
 
@@ -717,7 +717,7 @@ static void _lua_fr_request_register(lua_State *L, REQUEST *request)
                fr_cursor_t     cursor;
 
                /* Attribute list table */
-               fr_pair_list_sort(&request->packet->vps, fr_pair_cmp_by_da_tag);
+               fr_pair_list_sort(&request->packet->vps, fr_pair_cmp_by_da);
                fr_cursor_init(&cursor, &request->packet->vps);
 
                /*
index a0312709b63f2172674a4f1437ba6911e68b8fe8..a32ae01fbce2a1265d9e82954e9011bf8f2a35f6 100644 (file)
@@ -219,16 +219,9 @@ static int mruby_vps_to_array(REQUEST *request, mrb_value *out, mrb_state *mrb,
        res = mrb_ary_new(mrb);
        for (vp = fr_cursor_init(&cursor, vps); vp; vp = fr_cursor_next(&cursor)) {
                mrb_value       tmp, key, val, to_cast;
-               char            *str;
 
                tmp = mrb_ary_new_capa(mrb, 2);
-               if (vp->da->flags.has_tag) {
-                       str = talloc_typed_asprintf(request, "%s:%d", vp->da->name, vp->tag);
-                       key = mrb_str_new(mrb, str, talloc_array_length(str) - 1);
-                       talloc_free(str);
-               } else {
-                       key = mrb_str_new(mrb, vp->da->name, strlen(vp->da->name));
-               }
+               key = mrb_str_new(mrb, vp->da->name, strlen(vp->da->name));
 
                /*
                 *      The only way to create floats, doubles, bools etc,
@@ -249,10 +242,11 @@ static int mruby_vps_to_array(REQUEST *request, mrb_value *out, mrb_state *mrb,
 
                default:
                {
-                       char *in;
+                       char    *in;
+                       size_t  len;
 
-                       in = fr_value_box_asprint(request, &vp->data, '\0');
-                       to_cast = mrb_str_new(mrb, in, talloc_array_length(in) - 1);
+                       len = fr_value_box_aprint(request, &in, &vp->data, NULL);
+                       to_cast = mrb_str_new(mrb, in, len);
                        talloc_free(in);
                }
                        break;
index d77c8074f5cc637cd051ae1cfcdaa5d9f8b87a3b..fe417a948d90b4162e84c4801bfa42fa27a02a3b 100644 (file)
@@ -182,13 +182,13 @@ int do_auth_wbclient(rlm_mschap_t const *inst, REQUEST *request,
                        fr_box_strvalue_buffer(normalised_username));
 
                /* Recalculate hash */
-               vp_challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge, TAG_ANY);
+               vp_challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge);
                if (!vp_challenge) {
                        RERROR("Unable to get MS-CHAP-Challenge");
                        goto release;
                }
 
-               vp_response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY);
+               vp_response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response);
                if (!vp_response) {
                        RERROR("Unable to get MS-CHAP2-Response");
                        goto release;
index 42d37f714a6880f82c8789c134969206d01d8f09..db56bb7289730365cc233bf62fe8177c2d0b0426 100644 (file)
@@ -239,7 +239,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        unsigned int t;
 #endif
 
-       response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY);
+       response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response);
 
        username_string = talloc_array(request, char, usernamepair->vp_length + 1);
        if (!username_string) return RLM_MODULE_FAIL;
index dc54a995d48ab959b87213c9f1a3842c427ecdbe..aaf35eca6e7e672fd99dcfe2fc469b6914200446 100644 (file)
@@ -196,10 +196,10 @@ static VALUE_PAIR *mschap_identity_find(REQUEST *request)
 {
        VALUE_PAIR *vp;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (vp) return vp;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_identity, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_eap_identity);
        if (vp) return vp;
 
        REDEBUG("No user identity found in current request");
@@ -311,7 +311,7 @@ static ssize_t mschap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
         *      hash of MS-CHAPv2 challenge, and peer challenge.
         */
        if (strncasecmp(fmt, "Challenge", 9) == 0) {
-               chap_challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge, TAG_ANY);
+               chap_challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge);
                if (!chap_challenge) {
                        REDEBUG("No MS-CHAP-Challenge in the request");
                        return -1;
@@ -336,7 +336,7 @@ static ssize_t mschap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
                        char const      *username_str;
                        size_t          username_len;
 
-                       response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY);
+                       response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response);
                        if (!response) {
                                REDEBUG("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge");
                                return -1;
@@ -368,7 +368,7 @@ static ssize_t mschap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
                         *      We prefer this to the User-Name in the
                         *      packet.
                         */
-                       response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name, TAG_ANY);
+                       response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name);
                        if (response_name) {
                                name_vp = response_name;
                        } else {
@@ -418,8 +418,8 @@ static ssize_t mschap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
         *      response.
         */
        } else if (strncasecmp(fmt, "NT-Response", 11) == 0) {
-               response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response, TAG_ANY);
-               if (!response) response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY);
+               response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response);
+               if (!response) response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response);
                if (!response) {
                        REDEBUG("No MS-CHAP-Response or MS-CHAP2-Response was found in the request");
                        return -1;
@@ -447,7 +447,7 @@ static ssize_t mschap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
         *      in MS-CHAPv1, and not often there.
         */
        } else if (strncasecmp(fmt, "LM-Response", 11) == 0) {
-               response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response, TAG_ANY);
+               response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response);
                if (!response) {
                        REDEBUG("No MS-CHAP-Response was found in the request");
                        return -1;
@@ -1375,12 +1375,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        rlm_mschap_t const      *inst = talloc_get_type_abort_const(mctx->instance, rlm_mschap_t);
        VALUE_PAIR              *challenge = NULL;
 
-       challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge, TAG_ANY);
+       challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge);
        if (!challenge) return RLM_MODULE_NOOP;
 
-       if (!fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response, TAG_ANY) &&
-           !fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY) &&
-           !fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_cpw, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response) &&
+           !fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response) &&
+           !fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_cpw)) {
                RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP response or Change-Password");
                return RLM_MODULE_NOOP;
        }
@@ -1813,7 +1813,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2,3,4,7,8)) mschap_process_v2_response(int
                 *      We prefer this to the User-Name in the
                 *      packet.
                 */
-               response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name, TAG_ANY);
+               response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name);
                name_vp = response_name ? response_name : user_name;
 
                /*
@@ -1853,7 +1853,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2,3,4,7,8)) mschap_process_v2_response(int
 #endif
                peer_challenge = response->vp_octets + 2;
 
-               peer_challenge_attr = fr_pair_find_by_da(request->control, attr_ms_chap_peer_challenge, TAG_ANY);
+               peer_challenge_attr = fr_pair_find_by_da(request->control, attr_ms_chap_peer_challenge);
                if (peer_challenge_attr) {
                        RDEBUG2("Overriding peer challenge");
                        peer_challenge = peer_challenge_attr->vp_octets;
@@ -1885,7 +1885,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2,3,4,7,8)) mschap_process_v2_response(int
 
 #ifdef WITH_AUTH_WINBIND
                if (inst->wb_retry_with_normalised_username) {
-                       response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name, TAG_ANY);
+                       response_name = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_user_name);
                        if (response_name) {
                                if (strcmp(username_str, response_name->vp_strvalue)) {
                                        RDEBUG2("Normalising username %pV -> %pV",
@@ -1950,7 +1950,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
         *      want to suppress it.
         */
        if (method != AUTH_INTERNAL) {
-               VALUE_PAIR *vp = fr_pair_find_by_da(request->control, attr_ms_chap_use_ntlm_auth, TAG_ANY);
+               VALUE_PAIR *vp = fr_pair_find_by_da(request->control, attr_ms_chap_use_ntlm_auth);
                if (vp && vp->vp_bool == false) method = AUTH_INTERNAL;
        }
 
@@ -1958,11 +1958,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
         *      Find the SMB-Account-Ctrl attribute, or the
         *      SMB-Account-Ctrl-Text attribute.
         */
-       smb_ctrl = fr_pair_find_by_da(request->control, attr_smb_account_ctrl, TAG_ANY);
+       smb_ctrl = fr_pair_find_by_da(request->control, attr_smb_account_ctrl);
        if (!smb_ctrl) {
                VALUE_PAIR *smb_account_ctrl_text;
 
-               smb_account_ctrl_text = fr_pair_find_by_da(request->control, attr_smb_account_ctrl_text, TAG_ANY);
+               smb_account_ctrl_text = fr_pair_find_by_da(request->control, attr_smb_account_ctrl_text);
                if (smb_account_ctrl_text) {
                        MEM(pair_add_control(&smb_ctrl, attr_smb_account_ctrl) >= 0);
                        smb_ctrl->vp_uint32 = pdb_decode_acct_ctrl(smb_account_ctrl_text->vp_strvalue);
@@ -1996,7 +1996,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
         *      Check to see if this is a change password request, and process
         *      it accordingly if so.
         */
-       cpw = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_cpw, TAG_ANY);
+       cpw = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_cpw);
        if (cpw) {
                uint8_t         *p;
 
@@ -2028,7 +2028,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
                memcpy(p + 2, cpw->vp_octets + 18, 48);
        }
 
-       challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge, TAG_ANY);
+       challenge = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_challenge);
        if (!challenge) {
                REDEBUG("&control:Auth-Type = %s set for a request that does not contain &%s",
                        inst->name, attr_ms_chap_challenge->name);
@@ -2039,14 +2039,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        /*
         *      We also require an MS-CHAP-Response.
         */
-       if ((response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response, TAG_ANY))) {
+       if ((response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap_response))) {
                rcode = mschap_process_response(&mschap_version, nthashhash,
                                                inst, request,
                                                smb_ctrl, nt_password,
                                                challenge, response,
                                                method);
                if (rcode != RLM_MODULE_OK) goto finish;
-       } else if ((response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response, TAG_ANY))) {
+       } else if ((response = fr_pair_find_by_da(request->packet->vps, attr_ms_chap2_response))) {
                rcode = mschap_process_v2_response(&mschap_version, nthashhash,
                                                   inst, request,
                                                   smb_ctrl, nt_password,
index 6ed725d76c146cf605f02b3381b4417b2349ac3f..7db09e85e185d411f8734b9e621fc3d393c89b7a 100644 (file)
@@ -312,8 +312,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED module_ctx_t const *
        long            odResult = eDSAuthFailed;
        VALUE_PAIR *username, *password;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
@@ -400,7 +400,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
         *      We can only authenticate user requests which HAVE
         *      a User-Name attribute.
         */
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) {
                RDEBUG2("OpenDirectory requires a User-Name attribute");
                return RLM_MODULE_NOOP;
index c019c36ed4e990b7e2880d1829f393847e80dd7c..f9bd0efc6727087986f3791c7445050b60c63c1e 100644 (file)
@@ -220,8 +220,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        char const              *pam_auth_string = data->pam_auth_name;
        VALUE_PAIR              *username, *password;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
@@ -258,7 +258,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
         *      Let control list over-ride the PAM auth name string,
         *      for backwards compatibility.
         */
-       pair = fr_pair_find_by_da(request->control, attr_pam_auth, TAG_ANY);
+       pair = fr_pair_find_by_da(request->control, attr_pam_auth);
        if (pair) pam_auth_string = pair->vp_strvalue;
 
        ret = do_pam(request, username->vp_strvalue, password->vp_strvalue, pam_auth_string);
index 67fdbb48a268f6075e1667083be0ba7ee0f11e17..b2665bbe1bdf0950831cedb7527b0f121c271190 100644 (file)
@@ -126,12 +126,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        rlm_pap_t const         *inst = talloc_get_type_abort_const(mctx->instance, rlm_pap_t);
        VALUE_PAIR              *password;
 
-       if (fr_pair_find_by_da(request->control, attr_auth_type, TAG_ANY) != NULL) {
+       if (fr_pair_find_by_da(request->control, attr_auth_type) != NULL) {
                RDEBUG3("Auth-Type is already set.  Not setting 'Auth-Type := %s'", inst->name);
                return RLM_MODULE_NOOP;
        }
 
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
        if (!password) {
                RDEBUG2("No %s attribute in the request.  Cannot do PAP", attr_user_password->name);
                return RLM_MODULE_NOOP;
@@ -854,7 +854,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        pap_auth_func_t         auth_func;
        bool                    ephemeral;
 
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
        if (!password) {
                REDEBUG("You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
                return RLM_MODULE_INVALID;
index ab15b6dcb4b8c3fb762f2d2fa088ffca4d24d51a..ee9013e5d649fe9d734b6ad6ebe8e04359fbc8ee 100644 (file)
@@ -300,7 +300,7 @@ static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht,
        int h;
        struct mypasswd * hashentry;
 
-       if (!ht || !name || *name == '\0') return NULL;
+       if (!ht || !name || (*name == '\0')) return NULL;
        *last_found = NULL;
        if (ht->tablesize > 0) {
                h = hash (name, ht->tablesize);
@@ -542,7 +542,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(module_ctx_t const *mctx, REQ
        fr_cursor_t             cursor;
        int                     found = 0;
 
-       key = fr_pair_find_by_da(request->packet->vps, inst->keyattr, TAG_ANY);
+       key = fr_pair_find_by_da(request->packet->vps, inst->keyattr);
        if (!key) return RLM_MODULE_NOTFOUND;
 
        for (i = fr_cursor_iter_by_da_init(&cursor, &key, inst->keyattr);
@@ -551,7 +551,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(module_ctx_t const *mctx, REQ
                /*
                 *      Ensure we have the string form of the attribute
                 */
-               fr_pair_value_snprint(buffer, sizeof(buffer), i, 0);
+#ifdef __clang_analyzer__
+               /*
+                *      clang scan misses that fr_pair_print_value_quoted
+                *      always terminates the buffer.
+                */
+               buffer[0] = '\0';
+#endif
+               fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), i, T_BARE_WORD);
                pw = get_pw_nam(buffer, inst->ht, &last_found);
                if (!pw) continue;
 
index 24b15694f51485ad8e7aac86bf89376e6d2e2c74..2a26bd1106d830330acfba72a963732f519e9148 100644 (file)
@@ -647,12 +647,14 @@ static void perl_vp_to_svpvn_element(REQUEST *request, AV *av, VALUE_PAIR const
        default:
        {
                char    buffer[1024];
-               size_t  len;
+               size_t  slen;
 
-               len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
-               RDEBUG2("$%s{'%s'}[%i] = &%s:%s -> '%s'", hash_name, vp->da->name, *i,
-                       list_name, vp->da->name, buffer);
-               sv = newSVpvn(buffer, truncate_len(len, sizeof(buffer)));
+               slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vp, T_BARE_WORD);
+               if (slen < 0) return;
+
+               RDEBUG2("$%s{'%s'}[%i] = &%s:%s -> '%pV'", hash_name, vp->da->name, *i,
+                       list_name, vp->da->name, fr_box_strvalue_len(buffer, (size_t)slen));
+               sv = newSVpvn(buffer, (size_t)slen);
        }
                break;
        }
@@ -679,26 +681,13 @@ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR
        fr_cursor_t cursor;
 
        RINDENT();
-       fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(vps, fr_pair_cmp_by_da);
        for (vp = fr_cursor_init(&cursor, vps);
             vp;
             vp = fr_cursor_next(&cursor)) {
                VALUE_PAIR *next;
-
                char const *name;
-               char namebuf[256];
-
-               /*
-                *      Tagged attributes are added to the hash with name
-                *      <attribute>:<tag>, others just use the normal attribute
-                *      name as the key.
-                */
-               if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
-                       snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
-                       name = namebuf;
-               } else {
-                       name = vp->da->name;
-               }
+               name = vp->da->name;
 
                /*
                 *      We've sorted by type, then tag, so attributes of the
@@ -739,13 +728,13 @@ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR
                default:
                {
                        char buffer[1024];
-                       size_t len;
+                       size_t slen;
 
-                       len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
-                       RDEBUG2("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name,
-                              list_name, vp->da->name, buffer);
+                       slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vp, T_BARE_WORD);
+                       RDEBUG2("$%s{'%s'} = &%s:%s -> '%pV'", hash_name, vp->da->name,
+                               list_name, vp->da->name, fr_box_strvalue_len(buffer, (size_t)slen));
                        (void)hv_store(rad_hv, name, strlen(name),
-                                      newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0);
+                                      newSVpvn(buffer, (size_t)(slen)), 0);
                }
                        break;
                }
@@ -969,7 +958,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
        VALUE_PAIR              *pair;
        int                     acct_status_type = 0;
 
-       pair = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
+       pair = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type);
        if (pair != NULL) {
                acct_status_type = pair->vp_uint32;
        } else {
index 8685dbcb2067c45a1ba01c47b78e44c185402d9d..0695398a38b58e8f46cde8b1e5a358cdd121fda5 100644 (file)
@@ -391,12 +391,7 @@ static int mod_populate_vptuple(rlm_python_t const *inst, REQUEST *request, PyOb
        PyObject *value = NULL;
 
        /* Look at the fr_pair_fprint_name? */
-       if (vp->da->flags.has_tag) {
-               attribute = PyUnicode_FromFormat("%s:%d", vp->da->name, vp->tag);
-       } else {
-               attribute = PyUnicode_FromString(vp->da->name);
-       }
-
+       attribute = PyUnicode_FromString(vp->da->name);
        if (!attribute) return -1;
 
        switch (vp->vp_type) {
@@ -466,11 +461,18 @@ static int mod_populate_vptuple(rlm_python_t const *inst, REQUEST *request, PyOb
        case FR_TYPE_ETHERNET:
        case FR_TYPE_IPV4_PREFIX:
        {
-               size_t len;
+               size_t slen;
                char buffer[256];
 
-               len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
-               value = PyUnicode_FromStringAndSize(buffer, len);
+               slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer, sizeof(buffer)), vp, T_BARE_WORD);
+               if (slen < 0) {
+               error:
+                       ROPTIONAL(REDEBUG, ERROR, "Failed marshalling %pP to Python value", vp);
+                       python_error_log(inst, request);
+                       Py_XDECREF(attribute);
+                       return -1;
+               }
+               value = PyUnicode_FromStringAndSize(buffer, (size_t)slen);
        }
                break;
 
@@ -479,12 +481,7 @@ static int mod_populate_vptuple(rlm_python_t const *inst, REQUEST *request, PyOb
                return -1;
        }
 
-       if (value == NULL) {
-               ROPTIONAL(REDEBUG, ERROR, "Failed marshalling %pP to Python value", vp);
-               python_error_log(inst, request);
-               Py_XDECREF(attribute);
-               return -1;
-       }
+       if (value == NULL) goto error;
 
        PyTuple_SET_ITEM(pp, 0, attribute);
        PyTuple_SET_ITEM(pp, 1, value);
index 8d9ad0e891a0764a68a97d082616f4ec0e799add..59281e7ce5424dc5939aebf7db18cec4042d569d 100644 (file)
@@ -419,8 +419,8 @@ static void radius_fixups(rlm_radius_t const *inst, REQUEST *request)
 
        if (request->packet->code != FR_CODE_ACCESS_REQUEST) return;
 
-       if (fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY) &&
-           !fr_pair_find_by_da(request->packet->vps, attr_chap_challenge, TAG_ANY)) {
+       if (fr_pair_find_by_da(request->packet->vps, attr_chap_password) &&
+           !fr_pair_find_by_da(request->packet->vps, attr_chap_challenge)) {
                MEM(pair_add_request(&vp, attr_chap_challenge) >= 0);
                fr_pair_value_memdup(vp, request->packet->vector, sizeof(request->packet->vector), true);
        }
index 52825c141705f0968645b2779c3dac410fff9016..0bc820e8f65077b654a9234ee35885268224e210 100644 (file)
@@ -401,7 +401,7 @@ static void CC_HINT(nonnull) status_check_alloc(fr_event_list_t *el, udp_handle_
         *      Ensure that there's a NAS-Identifier, if one wasn't
         *      already added.
         */
-       if (!fr_pair_find_by_da(request->packet->vps, attr_nas_identifier, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->packet->vps, attr_nas_identifier)) {
                VALUE_PAIR *vp;
 
                MEM(pair_add_request(&vp, attr_nas_identifier) >= 0);
@@ -413,7 +413,7 @@ static void CC_HINT(nonnull) status_check_alloc(fr_event_list_t *el, udp_handle_
         *      at which the first packet is sent.  Or for
         *      Status-Server, the time of the current packet.
         */
-       if (!fr_pair_find_by_da(request->packet->vps, attr_event_timestamp, TAG_ANY)) {
+       if (!fr_pair_find_by_da(request->packet->vps, attr_event_timestamp)) {
                MEM(pair_add_request(NULL, attr_event_timestamp) >= 0);
        }
 
@@ -1326,7 +1326,7 @@ static int encode(rlm_radius_udp_t const *inst, REQUEST *request, udp_request_t
                VALUE_PAIR *vp;
 
                proxy_state = 0;
-               vp = fr_pair_find_by_da(request->packet->vps, attr_event_timestamp, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_event_timestamp);
                if (vp) vp->vp_date = fr_time_to_unix_time(u->retry.updated);
 
                if (u->code == FR_CODE_STATUS_SERVER) u->can_retransmit = false;
@@ -1459,7 +1459,7 @@ static int encode(rlm_radius_udp_t const *inst, REQUEST *request, udp_request_t
         *      received the request.
         */
        if ((u->code == FR_CODE_ACCOUNTING_REQUEST) &&
-           (fr_pair_find_by_da(request->packet->vps, attr_acct_delay_time, TAG_ANY) != NULL)) {
+           (fr_pair_find_by_da(request->packet->vps, attr_acct_delay_time) != NULL)) {
                uint8_t *attr, *end;
                uint32_t delay;
                fr_time_t now;
@@ -2427,7 +2427,7 @@ static void request_demux(fr_trunk_connection_t *tconn, fr_connection_t *conn, U
                if ((u->code == FR_CODE_ACCESS_REQUEST) && (code == FR_CODE_ACCESS_CHALLENGE)) {
                        VALUE_PAIR      *vp;
 
-                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type);
                        if (!vp) {
                                MEM(vp = fr_pair_afrom_da(request->reply, attr_packet_type));
                                vp->vp_uint32 = FR_CODE_ACCESS_CHALLENGE;
@@ -2447,7 +2447,7 @@ static void request_demux(fr_trunk_connection_t *tconn, fr_connection_t *conn, U
                 *      reply:Message-Authenticator attribute, so that
                 *      it ends up in our reply.
                 */
-               if (fr_pair_find_by_da(reply, attr_message_authenticator, TAG_ANY)) {
+               if (fr_pair_find_by_da(reply, attr_message_authenticator)) {
                        VALUE_PAIR *vp;
 
                        fr_pair_delete_by_da(&reply, attr_message_authenticator);
@@ -2703,7 +2703,7 @@ static rlm_rcode_t mod_enqueue(void **rctx_out, void *instance, void *thread, RE
         *
         *      @todo - don't edit the input packet!
         */
-       if (fr_pair_find_by_da(request->packet->vps, attr_message_authenticator, TAG_ANY)) {
+       if (fr_pair_find_by_da(request->packet->vps, attr_message_authenticator)) {
                u->require_ma = true;
                pair_delete_request(attr_message_authenticator);
        }
index 23dc6ccaeb38680f367f88e4a6e484c2e71ca252..40909c5ebeec82c6677a0afe76a26fce15b252b7 100644 (file)
@@ -204,7 +204,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
        /*
         *      Which type is this.
         */
-       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type)) == NULL) {
                RDEBUG2("No Accounting-Status-Type record");
                return RLM_MODULE_NOOP;
        }
@@ -227,10 +227,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
                int check1 = 0;
                int check2 = 0;
 
-               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_session_time, TAG_ANY))
+               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_session_time))
                     == NULL || vp->vp_uint32 == 0)
                        check1 = 1;
-               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_session_id, TAG_ANY))
+               if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_session_id))
                     != NULL && vp->vp_length == 8 &&
                     memcmp(vp->vp_strvalue, "00000000", 8) == 0)
                        check2 = 1;
index ea0e043eabd5d0ea064fb2e933af242aef16b208..33750339913b5d207fe9595e5353cdb9bbd93fdc 100644 (file)
@@ -99,8 +99,8 @@ do { \
                goto finish; \
        } \
        _p += (size_t)_slen;\
-       _slen = fr_pair_value_snprint((char *)_p, sizeof(_buff) - (_p - _buff), _ip, '\0'); \
-       if (is_truncated((size_t)_slen, sizeof(_buff) - (_p - _buff))) { \
+       _slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT((char *)_p, sizeof(_buff) - (_p - _buff)), _ip, T_BARE_WORD); \
+       if (_slen < 0) { \
                REDEBUG("IP key too long"); \
                ret = IPPOOL_RCODE_FAIL; \
                goto finish; \
index ffbffc0de76f532c26c004063e8b0c4e7c10cead..4a135f5f9561c48ee1d94ed4dd3805a2cb77a051 100644 (file)
@@ -668,7 +668,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQU
                        .rhs = &ip_rhs
                };
 
-               tmpl_init(&ip_rhs, TMPL_TYPE_DATA, "", 0, T_BARE_WORD);
+               tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0);
                switch (reply->element[1]->type) {
                /*
                 *      Destination attribute may not be IPv4, in which case
@@ -731,7 +731,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQU
                                .rhs = &range_rhs
                        };
 
-                       tmpl_init(&range_rhs, TMPL_TYPE_DATA, "", 0, T_DOUBLE_QUOTED_STRING);
+                       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
                        fr_value_box_bstrndup_shallow(&range_map.rhs->data.literal,
                                                      NULL, reply->element[2]->str, reply->element[2]->len, true);
                        if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) {
@@ -763,7 +763,7 @@ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQU
                        .rhs = &expiry_rhs
                };
 
-               tmpl_init(&expiry_rhs, TMPL_TYPE_DATA, "", 0, T_DOUBLE_QUOTED_STRING);
+               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
                if (reply->element[3]->type != REDIS_REPLY_INTEGER) {
                        REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])",
                                fr_table_str_by_value(redis_reply_types, reply->element[3]->type, "<UNKNOWN>"));
@@ -801,7 +801,7 @@ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, REQUES
        tmpl_t          range_rhs;
        vp_map_t                range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs };
 
-       tmpl_init(&range_rhs, TMPL_TYPE_DATA, "", 0, T_DOUBLE_QUOTED_STRING);
+       tmpl_init_shallow(&range_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
 
        now = fr_time_to_timeval(fr_time());
 
@@ -909,7 +909,7 @@ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, REQUES
                };
 
 
-               tmpl_init(&expiry_rhs, TMPL_TYPE_DATA, "", 0, T_DOUBLE_QUOTED_STRING);
+               tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
 
                fr_value_box_shallow(&expiry_map.rhs->data.literal, expires, false);
                if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) {
@@ -1254,13 +1254,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
        /*
         *      Pool-Action override
         */
-       vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_pool_action);
        if (vp) return mod_action(inst, request, vp->vp_uint32);
 
        /*
         *      Otherwise, guess the action by Acct-Status-Type
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type);
        if (!vp) {
                RDEBUG2("Couldn't find &request:Acct-Status-Type or &control:Pool-Action, doing nothing...");
                return RLM_MODULE_NOOP;
@@ -1292,7 +1292,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
         *      Unless it's overridden the default action is to allocate
         *      when called in Post-Auth.
         */
-       vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_pool_action);
        return mod_action(inst, request, vp ? vp->vp_uint32 : POOL_ACTION_ALLOCATE);
 }
 
@@ -1306,7 +1306,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
         *      Unless it's overridden the default action is to allocate
         *      when called in Post-Auth.
         */
-       vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_pool_action);
        if (vp) {
                if ((vp->vp_uint32 > 0) && (vp->vp_uint32 <= POOL_ACTION_BULK_RELEASE)) {
                        action = vp->vp_uint32;
@@ -1317,7 +1317,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
                }
 
        } else if (request->dict == dict_dhcpv4) {
-               vp = fr_pair_find_by_da(request->control, attr_message_type, TAG_ANY);
+               vp = fr_pair_find_by_da(request->control, attr_message_type);
                if (!vp) goto run;
 
                if (vp->vp_uint8 == FR_DHCP_REQUEST) action = POOL_ACTION_UPDATE;
@@ -1337,7 +1337,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_request(module_ctx_t const *mctx, REQUES
         *      when called by DHCP request
         */
 
-       vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_pool_action);
        return mod_action(inst, request, vp ? vp->vp_uint32 : POOL_ACTION_UPDATE);
 }
 
index f557a78838c506f9317ad5e4d1e6fd5d25d9c174..db4f7b304e123224d985383b4cbccf04205fd612 100644 (file)
@@ -205,7 +205,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
        CONF_SECTION            *cs;
        char const              *insert, *trim, *expire;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type);
        if (!vp) {
                RDEBUG2("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
index 965b313866a48b0a19d8a0e2e46398d85f88c726..f922e31c53a1391da880cb773806ec1feddd925b 100644 (file)
@@ -227,8 +227,6 @@ typedef struct {
                                // (multiple values not supported).
        fr_token_t op;          //!< The operator that determines how the new VP
                                // is processed. @see fr_tokens_table
-
-       int8_t tag;             //!< Tag to assign to VP.
 } json_flags_t;
 #endif
 
@@ -368,6 +366,7 @@ static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userd
        char                    *escaped;       /* Pointer to current URL escaped data */
 
        size_t                  len = 0;
+       ssize_t                 slen;
        size_t                  freespace = (size * nmemb) - 1;
 
        /* Allow manual chunking */
@@ -436,14 +435,14 @@ static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userd
                /*
                 *  Write out single attribute string.
                 */
-               len = fr_pair_value_snprint(p, freespace, vp, 0);
-               if (is_truncated(len, freespace)) goto no_space;
+               slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(p, freespace), vp, T_BARE_WORD);
+               if (slen < 0) return 0;
 
                RINDENT();
-               RDEBUG3("Length : %zd", len);
+               RDEBUG3("Length : %zd", (size_t)slen);
                REXDENT();
-               if (len > 0) {
-                       escaped = curl_escape(p, len);
+               if (slen > 0) {
+                       escaped = curl_escape(p, (size_t)slen);
                        if (!escaped) {
                                REDEBUG("Failed escaping string \"%s\"", vp->da->name);
                                return 0;
@@ -930,7 +929,6 @@ static VALUE_PAIR *json_pair_alloc_leaf(UNUSED rlm_rest_t const *instance, UNUSE
        }
 
        vp->op = flags->op;
-       vp->tag = flags->tag;
 
        return vp;
 }
@@ -1039,8 +1037,6 @@ static int json_pair_alloc(rlm_rest_t const *instance, rlm_rest_section_t const
                        continue;
                }
 
-               flags.tag = tmpl_tag(dst);
-
                if (radius_request(&current, tmpl_request(dst)) < 0) {
                        RWDEBUG("Attribute name refers to outer request but not in a tunnel (skipping)");
                        continue;
index e44ce517c4bac324eb7fae2a60f97ec9df48713d..ea14cd72b54bf15357e763dc58f0abe0b2131cdc 100644 (file)
@@ -638,8 +638,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
 
        if (!section->name) return RLM_MODULE_NOOP;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
index 44b3dafe626f5fa8edb9ff172810196a4de1db61..e0567dfab4cb48bfc2c66543f68c27ce334873b8 100644 (file)
@@ -183,7 +183,7 @@ SECURID_SESSION *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
        /*
         *      We key the sessions off of the 'state' attribute
         */
-       state = fr_pair_find_by_da(request->packet->vps, attr_state, TAG_ANY);
+       state = fr_pair_find_by_da(request->packet->vps, attr_state);
        if (!state) {
                return NULL;
        }
index 5ec19ed3f9365546357b9928272d401a023dd17f..e9263216a27b3907efafb77ef15c10158fb2b046 100644 (file)
@@ -473,8 +473,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        VALUE_PAIR              *username, *password;
        VALUE_PAIR              *vp;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
index 26302f97cfb6bd0def1205367d255d49d4bddf25..1dd0c8586ab83ea5c4c7f22dd39b072ed554ee47 100644 (file)
@@ -85,19 +85,19 @@ static ssize_t soh_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        /*
         * There will be no point unless SoH-Supported = yes
         */
-       vp[0] = fr_pair_find_by_da(request->packet->vps, attr_soh_supported, TAG_ANY);
+       vp[0] = fr_pair_find_by_da(request->packet->vps, attr_soh_supported);
        if (!vp[0])
                return 0;
 
 
        if (strncasecmp(fmt, "OS", 2) == 0) {
                /* OS vendor */
-               vp[0] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_vendor, TAG_ANY);
-               vp[1] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_version, TAG_ANY);
-               vp[2] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_release, TAG_ANY);
-               vp[3] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_build, TAG_ANY);
-               vp[4] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_sp_version, TAG_ANY);
-               vp[5] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_sp_release, TAG_ANY);
+               vp[0] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_vendor);
+               vp[1] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_version);
+               vp[2] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_release);
+               vp[3] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_os_build);
+               vp[4] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_sp_version);
+               vp[5] = fr_pair_find_by_da(request->packet->vps, attr_soh_ms_machine_sp_release);
 
                if (vp[0] && vp[0]->vp_uint32 == VENDORPEC_MICROSOFT) {
                        if (!vp[1]) {
@@ -150,7 +150,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
 
        if (!inst->dhcp) return RLM_MODULE_NOOP;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_dhcp_vendor, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_dhcp_vendor);
        if (vp) {
                /*
                 * vendor-specific options contain
@@ -211,7 +211,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED module_ctx_t const *mct
        int rv;
 
        /* try to find the MS-SoH payload */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_ms_quarantine_soh, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_ms_quarantine_soh);
        if (!vp) {
                RDEBUG2("SoH radius VP not found");
                return RLM_MODULE_NOOP;
index 2deda0112f77b33a059fa8f22bb19822642f2183..2b726cccdf39537977d3ebbae23391d0f73cd3e8 100644 (file)
@@ -148,7 +148,7 @@ static size_t sql_escape_for_xlat_func(REQUEST *request, char *out, size_t outle
 static sql_fall_through_t fall_through(VALUE_PAIR *vp)
 {
        VALUE_PAIR *tmp;
-       tmp = fr_pair_find_by_da(vp, attr_fall_through, TAG_ANY);
+       tmp = fr_pair_find_by_da(vp, attr_fall_through);
 
        return tmp ? tmp->vp_uint32 : FALL_THROUGH_DEFAULT;
 }
@@ -1365,7 +1365,7 @@ skip_reply:
                 *  Check for a default_profile or for a User-Profile.
                 */
                RDEBUG3("... falling-through to profile processing");
-               user_profile = fr_pair_find_by_da(request->control, attr_user_profile, TAG_ANY);
+               user_profile = fr_pair_find_by_da(request->control, attr_user_profile);
 
                profile = user_profile ?
                                      user_profile->vp_strvalue :
index e86a06fcdfd335c24d8f96880c15c24daaace513..3306b76dc3742377898fc4314be7309adfbf73c9 100644 (file)
@@ -572,12 +572,12 @@ static int mod_bootstrap(void *instance, CONF_SECTION *conf)
        fr_assert(inst->limit_attr);
 
        memset(&flags, 0, sizeof(flags));
-       if (tmpl_unresolved_attr_add(fr_dict_unconst(dict_freeradius), inst->paircmp_attr, FR_TYPE_UINT64, &flags) < 0) {
+       if (tmpl_attr_unresolved_add(fr_dict_unconst(dict_freeradius), inst->paircmp_attr, FR_TYPE_UINT64, &flags) < 0) {
                cf_log_perr(conf, "Failed defining counter attribute");
                return -1;
        }
 
-       if (tmpl_unresolved_attr_add(fr_dict_unconst(dict_freeradius), inst->limit_attr, FR_TYPE_UINT64, &flags) < 0) {
+       if (tmpl_attr_unresolved_add(fr_dict_unconst(dict_freeradius), inst->limit_attr, FR_TYPE_UINT64, &flags) < 0) {
                cf_log_perr(conf, "Failed defining check attribute");
                return -1;
        }
index e46812840cd63e3fd00d8d12e83cd352153aee94..817b695c90ae264c9a69c0bf8295cf92656e459d 100644 (file)
@@ -514,13 +514,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_alloc(module_ctx_t const *mctx, REQUEST
        /*
         *      If there is a Framed-IP-Address attribute in the reply do nothing
         */
-       if (fr_pair_find_by_da(request->reply->vps, inst->framed_ip_address, TAG_ANY) != NULL) {
+       if (fr_pair_find_by_da(request->reply->vps, inst->framed_ip_address) != NULL) {
                RDEBUG2("%s already exists", inst->framed_ip_address->name);
 
                return do_logging(inst, request, inst->log_exists, RLM_MODULE_NOOP);
        }
 
-       if (fr_pair_find_by_da(request->control, attr_pool_name, TAG_ANY) == NULL) {
+       if (fr_pair_find_by_da(request->control, attr_pool_name) == NULL) {
                RDEBUG2("No %s defined", attr_pool_name->name);
 
                return do_logging(inst, request, inst->log_nopool, RLM_MODULE_NOOP);
@@ -556,7 +556,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_alloc(module_ctx_t const *mctx, REQUEST
         *      and a query to find whether it is available then try that
         */
        if (allocation_len == 0 && inst->alloc_requested && *inst->alloc_requested &&
-           fr_pair_find_by_da(request->packet->vps, inst->req_framed_ip_address, TAG_ANY) != NULL) {
+           fr_pair_find_by_da(request->packet->vps, inst->req_framed_ip_address) != NULL) {
                allocation_len = sqlippool_query1(allocation, sizeof(allocation),
                                                  inst->alloc_requested, &handle,
                                                  inst, request, (char *) NULL, 0);
@@ -817,7 +817,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
 
        int                     acct_status_type;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type);
        if (!vp) {
                RDEBUG2("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
index 6ddaa3627f663fa54b9c0861c80b27f9feb6ba63..6bb620f9e4992d75aa9d423a7cc81d7e5f6bbe23 100644 (file)
@@ -239,7 +239,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_stats(module_ctx_t const *mctx, REQUEST
                return RLM_MODULE_NOOP;
        }
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_type, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_type);
        if (!vp) {
                stats_type = FR_FREERADIUS_STATS4_TYPE_VALUE_GLOBAL;
        } else {
@@ -273,8 +273,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_stats(module_ctx_t const *mctx, REQUEST
                break;
 
        case FR_FREERADIUS_STATS4_TYPE_VALUE_CLIENT:                    /* src */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY);
-               if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address);
+               if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address);
                if (!vp) return RLM_MODULE_NOOP;
 
                mydata.ipaddr = vp->vp_ip;
@@ -282,8 +282,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_stats(module_ctx_t const *mctx, REQUEST
                break;
 
        case FR_FREERADIUS_STATS4_TYPE_VALUE_LISTENER:                  /* dst */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address, TAG_ANY);
-               if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv4_address);
+               if (!vp) vp = fr_pair_find_by_da(request->packet->vps, attr_freeradius_stats4_ipv6_address);
                if (!vp) return RLM_MODULE_NOOP;
 
                mydata.ipaddr = vp->vp_ip;
index 2c3775fd761133a17e3fdbcba4096554d306433e..90b2dd5c54ef732d7716467a0f2ba7ee9a011165 100644 (file)
@@ -112,7 +112,7 @@ static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *
        /*
         *      No user name, can't compare.
         */
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) return -1;
 
        if (rad_getpwnam(request, &pwd, username->vp_strvalue) < 0) {
@@ -202,7 +202,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED module_ctx_t const *mct
         *      We can only authenticate user requests which HAVE
         *      a User-Name attribute.
         */
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!username) return RLM_MODULE_NOOP;
 
        name = username->vp_strvalue;
@@ -373,7 +373,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
        /*
         *      Which type is this.
         */
-       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type)) == NULL) {
                RDEBUG2("no Accounting-Status-Type attribute in request");
                return RLM_MODULE_NOOP;
        }
@@ -390,7 +390,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(module_ctx_t const *mctx, REQ
         *      We're only interested in accounting messages
         *      with a username in it.
         */
-       if (fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY) == NULL)
+       if (fr_pair_find_by_da(request->packet->vps, attr_user_name) == NULL)
                return RLM_MODULE_NOOP;
 
        t = fr_time_to_sec(request->packet->timestamp);
index 50254b2ffac61750ad5ec10b318fce45e82d0ad3..d99369986d19ae957c33e7d58cfadf24a33c4615 100644 (file)
@@ -66,6 +66,7 @@ static ssize_t unpack_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
        char *data_name, *data_size, *data_type;
        char *p;
        size_t len, input_len;
+       ssize_t slen;
        int offset;
        fr_type_t type;
        fr_dict_attr_t const *da;
@@ -207,14 +208,14 @@ static ssize_t unpack_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
                break;
        }
 
-       len = fr_pair_value_snprint(*out, outlen, cast, 0);
+       slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(*out, outlen), cast, T_BARE_WORD);
        talloc_free(cast);
-       if (is_truncated(len, outlen)) {
+       if (slen < 0) {
                REDEBUG("Insufficient buffer space to unpack data");
                goto nothing;
        }
 
-       return len;
+       return slen;
 }
 
 
index bcdfa15d788aa332c3390e15fed3f0b02957a005..45dfb4ae21f21e89a3ecccef882346dcb0dece2e 100644 (file)
@@ -123,7 +123,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED module_ctx_t const *mct
        /*
         *      Fix Calling-Station-Id.  Damn you, WiMAX!
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_calling_station_id, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_calling_station_id);
        if (vp && (vp->vp_length == 6)) {
                int     i;
                char    *p;
@@ -173,8 +173,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
        uint8_t                 mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
        uint8_t                 mip_rk[2 * EVP_MAX_MD_SIZE];
 
-       msk = fr_pair_find_by_da(request->reply->vps, attr_eap_msk, TAG_ANY);
-       emsk = fr_pair_find_by_da(request->reply->vps, attr_eap_emsk, TAG_ANY);
+       msk = fr_pair_find_by_da(request->reply->vps, attr_eap_msk);
+       emsk = fr_pair_find_by_da(request->reply->vps, attr_eap_emsk);
        if (!msk || !emsk) {
                REDEBUG2("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys");
                return RLM_MODULE_NOOP;
@@ -248,8 +248,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
        /*
         *      Calculate mobility keys
         */
-       mn_nai = fr_pair_find_by_da(request->packet->vps, attr_wimax_mn_nai, TAG_ANY);
-       if (!mn_nai) mn_nai = fr_pair_find_by_da(request->reply->vps, attr_wimax_mn_nai, TAG_ANY);
+       mn_nai = fr_pair_find_by_da(request->packet->vps, attr_wimax_mn_nai);
+       if (!mn_nai) mn_nai = fr_pair_find_by_da(request->reply->vps, attr_wimax_mn_nai);
        if (!mn_nai) {
                RWDEBUG("%s was not found in the request or in the reply", attr_wimax_mn_nai->name);
                RWDEBUG("We cannot calculate MN-HA keys");
@@ -259,7 +259,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
         *      WiMAX-IP-Technology
         */
        vp = NULL;
-       if (mn_nai) vp = fr_pair_find_by_da(request->reply->vps, attr_wimax_ip_technology, TAG_ANY);
+       if (mn_nai) vp = fr_pair_find_by_da(request->reply->vps, attr_wimax_ip_technology);
        if (!vp) {
                RWDEBUG("%s not found in reply", attr_wimax_ip_technology->name);
                RWDEBUG("Not calculating MN-HA keys");
@@ -270,7 +270,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
                /*
                 *      Look for WiMAX-hHA-IP-MIP4
                 */
-               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip4, TAG_ANY);
+               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip4);
                if (!ip) {
                        RWDEBUG("%s not found.  Cannot calculate MN-HA-PMIP4 key", attr_wimax_hha_ip_mip4->name);
                        break;
@@ -304,7 +304,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
                /*
                 *      Look for WiMAX-hHA-IP-MIP4
                 */
-               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip4, TAG_ANY);
+               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip4);
                if (!ip) {
                        RWDEBUG("%s not found.  Cannot calculate MN-HA-CMIP4 key", attr_wimax_hha_ip_mip4->name);
                        break;
@@ -338,7 +338,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
                /*
                 *      Look for WiMAX-hHA-IP-MIP6
                 */
-               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip6, TAG_ANY);
+               ip = fr_pair_find_by_da(request->reply->vps, attr_wimax_hha_ip_mip6);
                if (!ip) {
                        RWDEBUG("%s not found.  Cannot calculate MN-HA-CMIP6 key", attr_wimax_hha_ip_mip6->name);
                        break;
@@ -377,7 +377,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
         *
         *      FA-RK= H(MIP-RK, "FA-RK")
         */
-       fa_rk = fr_pair_find_by_da(request->reply->vps, attr_wimax_fa_rk_key, TAG_ANY);
+       fa_rk = fr_pair_find_by_da(request->reply->vps, attr_wimax_fa_rk_key);
        if (fa_rk && (fa_rk->vp_length <= 1)) {
                HMAC_Init_ex(hmac, mip_rk, rk_len, EVP_sha1(), NULL);
 
@@ -402,7 +402,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
         *
         *      WiMAX-RRQ-MN-HA-SPI
         */
-       vp = fr_pair_find_by_da(request->packet->vps, attr_wimax_rrq_mn_ha_spi, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_wimax_rrq_mn_ha_spi);
        if (vp) {
                REDEBUG2("Client requested MN-HA key: Should use SPI to look up key from storage");
                if (!mn_nai) {
@@ -412,14 +412,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(module_ctx_t const *mctx, REQU
                /*
                 *      WiMAX-RRQ-HA-IP
                 */
-               if (!fr_pair_find_by_da(request->packet->vps, attr_wimax_rrq_ha_ip, TAG_ANY)) {
+               if (!fr_pair_find_by_da(request->packet->vps, attr_wimax_rrq_ha_ip)) {
                        RWDEBUG("HA-IP was not found!");
                }
 
                /*
                 *      WiMAX-HA-RK-Key-Requested
                 */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_wimax_ha_rk_key_requested, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_wimax_ha_rk_key_requested);
                if (vp && (vp->vp_uint32 == 1)) {
                        REDEBUG2("Client requested HA-RK: Should use IP to look it up from storage");
                }
index 7f8cbc547cc409e114f207b9be960ceb89154814..4f2d56db657a55301b5a32090f0d0d6ed8034773 100644 (file)
@@ -106,7 +106,7 @@ static int winbind_group_cmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR
        ssize_t                 slen;
        size_t                  backslash = 0;
 
-       vp_username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+       vp_username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
        if (!vp_username) return -1;
 
        RINDENT();
@@ -417,9 +417,14 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf)
                        goto no_domain;
                }
 
-               tmpl_afrom_str(instance, &inst->wb_domain, wb_info->netbios_domain,
-                              strlen(wb_info->netbios_domain), T_SINGLE_QUOTED_STRING,
-                              &(tmpl_rules_t){ .allow_unknown = true, .allow_unresolved = true }, false);
+               tmpl_afrom_substr(instance, &inst->wb_domain,
+                                 &FR_SBUFF_IN(wb_info->netbios_domain, strlen(wb_info->netbios_domain)),
+                                 T_SINGLE_QUOTED_STRING,
+                                 NULL,
+                                 &(tmpl_rules_t){
+                                       .allow_unknown = true,
+                                       .allow_unresolved = true
+                                 });
 
                cf_log_err(conf, "Using winbind_domain '%s'", inst->wb_domain->name);
 
@@ -465,7 +470,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        rlm_winbind_t const     *inst = talloc_get_type_abort_const(mctx->instance, rlm_winbind_t);
        VALUE_PAIR              *vp;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_user_password);
        if (!vp) {
                REDEBUG2("No User-Password found in the request; not doing winbind authentication.");
                return RLM_MODULE_NOOP;
@@ -495,8 +500,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        rlm_winbind_t const     *inst = talloc_get_type_abort_const(mctx->instance, rlm_winbind_t);
        VALUE_PAIR              *username, *password;
 
-       username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
 
        /*
         *      We can only authenticate user requests which HAVE
index 6e2218dd381b2e4cc464348311ac5d284516a1da..3a8e8bb32d179c6c860e5af76037371bd2df0d08 100644 (file)
@@ -30,7 +30,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t const *inst, REQUEST *request, cha
 
        VALUE_PAIR *key, *vp;
 
-       key = fr_pair_find_by_da(request->control, attr_yubikey_key, TAG_ANY);
+       key = fr_pair_find_by_da(request->control, attr_yubikey_key);
        if (!key) {
                REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
                return RLM_MODULE_INVALID;
@@ -92,7 +92,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t const *inst, REQUEST *request, cha
        /*
         *      Now we check for replay attacks
         */
-       vp = fr_pair_find_by_da(request->control, attr_yubikey_counter, TAG_ANY);
+       vp = fr_pair_find_by_da(request->control, attr_yubikey_counter);
        if (!vp) {
                RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks");
                return RLM_MODULE_OK;
index 660e24f1d19f5af1ecf9a8a971587cb1f6aaa133..c45ff06c1b3b758f5a6af7e72e4ab52b511a4d4b 100644 (file)
@@ -257,7 +257,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(module_ctx_t const *mctx, REQU
        /*
         *      Can't do yubikey auth if there's no password.
         */
-       password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+       password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
        if (!password) {
                /*
                 *      Don't print out debugging messages if we know
@@ -364,13 +364,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(module_ctx_t const *mctx, R
        size_t                  len;
        int                     ret;
 
-       vp = fr_pair_find_by_da(request->packet->vps, attr_yubikey_otp, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, attr_yubikey_otp);
        if (!vp) {
                RDEBUG2("No Yubikey-OTP attribute found, falling back to User-Password");
                /*
                 *      Can't do yubikey auth if there's no password.
                 */
-               vp = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+               vp = fr_pair_find_by_da(request->packet->vps, attr_user_password);
                if (!vp) {
                        REDEBUG("No User-Password in the request. Can't do Yubikey authentication");
                        return RLM_MODULE_INVALID;
index eefd90262687f704368ea72bb563317f88651c50..e1e7b7d56b1a8d14391c7008700e4f0ee441c22a 100644 (file)
@@ -193,7 +193,7 @@ int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
        if (a_82 && !b_82) return +1;
        if (!a_82 && !b_82) return -1;
 
-       return fr_pair_cmp_by_parent_num_tag(my_a, my_b);
+       return fr_pair_cmp_by_parent_num(my_a, my_b);
 }
 
 /** Check received DHCP request is valid and build RADIUS_PACKET structure if it is
@@ -310,7 +310,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
         */
 
        /* DHCP-DHCP-Maximum-Msg-Size */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_dhcp_maximum_msg_size, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_dhcp_maximum_msg_size);
        if (vp && (vp->vp_uint32 > mms)) {
                mms = vp->vp_uint32;
 
@@ -318,7 +318,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        }
 #endif
 
-       vp = fr_pair_find_by_da(vps, attr_dhcp_opcode, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_opcode);
        if (vp) {
                *p++ = vp->vp_uint32 & 0xff;
        } else {
@@ -326,7 +326,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        }
 
        /* DHCP-Hardware-Type */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_type, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_type);
        if (vp) {
                *p = vp->vp_uint8;
 
@@ -337,7 +337,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 1;
 
        /* DHCP-Hardware-Address-len */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_address_length, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_hardware_address_length);
        if (vp) {
                *p = vp->vp_uint8;
 
@@ -348,7 +348,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 1;
 
        /* DHCP-Hop-Count */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_hop_count, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_hop_count);
        if (vp) {
                *p = vp->vp_uint8;
 
@@ -364,7 +364,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 4;
 
        /* DHCP-Number-of-Seconds */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_number_of_seconds, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_number_of_seconds);
        if (vp) {
                svalue = htons(vp->vp_uint16);
                memcpy(p, &svalue, 2);
@@ -372,7 +372,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 2;
 
        /* DHCP-Flags */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_flags, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_flags);
        if (vp) {
                svalue = htons(vp->vp_uint16);
                memcpy(p, &svalue, 2);
@@ -380,12 +380,12 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 2;
 
        /* DHCP-Client-IP-Address */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_client_ip_address, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_client_ip_address);
        if (vp) memcpy(p, &vp->vp_ipv4addr, 4);
        p += 4;
 
        /* DHCP-Your-IP-address */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_your_ip_address, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_your_ip_address);
        if (vp) {
                lvalue = vp->vp_ipv4addr;
        } else {
@@ -395,7 +395,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 4;
 
        /* DHCP-Server-IP-Address */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_server_ip_address, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_server_ip_address);
        if (vp) {
                lvalue = vp->vp_ipv4addr;
        } else {
@@ -407,7 +407,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        /*
         *      DHCP-Gateway-IP-Address
         */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_gateway_ip_address, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_gateway_ip_address);
        if (vp) {
                lvalue = vp->vp_ipv4addr;
                memcpy(p, &lvalue, 4);
@@ -422,7 +422,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += 4;
 
        /* DHCP-Client-Hardware-Address */
-       if ((vp = fr_pair_find_by_da(vps, attr_dhcp_client_hardware_address, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_da(vps, attr_dhcp_client_hardware_address))) {
                if (vp->vp_type == FR_TYPE_ETHERNET) {
                        /*
                         *      Ensure that we mark the packet as being Ethernet.
@@ -440,7 +440,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
        p += DHCP_CHADDR_LEN;
 
        /* DHCP-Server-Host-Name */
-       if ((vp = fr_pair_find_by_da(vps, attr_dhcp_server_host_name, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_da(vps, attr_dhcp_server_host_name))) {
                if (vp->vp_length > DHCP_SNAME_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_SNAME_LEN);
                } else {
@@ -460,7 +460,7 @@ ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original
         */
 
        /* DHCP-Boot-Filename */
-       vp = fr_pair_find_by_da(vps, attr_dhcp_boot_filename, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_dhcp_boot_filename);
        if (vp) {
                if (vp->vp_length > DHCP_FILE_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
@@ -667,11 +667,6 @@ static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *pare
                return false;
        }
 
-       if (flags->has_tag) {
-               fr_strerror_printf("Tagged attributes cannot be used with DHCP");
-               return false;
-       }
-
        /*
         *      "extra" signifies that subtype is being used by the
         *      dictionaries itself.
index 8d2348ce5cc8fce23f8f99e84a22d2214a794f85..d8dd63ff6cc05c20d4e6a734dd0f7a529bc8b166 100644 (file)
@@ -218,7 +218,7 @@ int fr_dhcpv4_decode(TALLOC_CTX *ctx, uint8_t const *data, size_t data_len, VALU
                }
 
                if (code) {
-                       vp = fr_pair_find_by_da(head, attr_dhcp_message_type, TAG_ANY);
+                       vp = fr_pair_find_by_da(head, attr_dhcp_message_type);
                        if (vp) {
                                *code = vp->vp_uint8;
                        }
@@ -228,7 +228,7 @@ int fr_dhcpv4_decode(TALLOC_CTX *ctx, uint8_t const *data, size_t data_len, VALU
                 *      If option Overload is present in the 'options' field, then fields 'file' and/or 'sname'
                 *      are used to hold more options. They are partitioned and must be interpreted in sequence.
                 */
-               vp = fr_pair_find_by_da(head, attr_dhcp_overload, TAG_ANY);
+               vp = fr_pair_find_by_da(head, attr_dhcp_overload);
                if (vp) {
                        if ((vp->vp_uint8 & 1) == 1) {
                                /*
@@ -281,14 +281,14 @@ int fr_dhcpv4_decode(TALLOC_CTX *ctx, uint8_t const *data, size_t data_len, VALU
                /*
                 *      DHCP Opcode is request
                 */
-               vp = fr_pair_find_by_da(head, attr_dhcp_opcode, TAG_ANY);
+               vp = fr_pair_find_by_da(head, attr_dhcp_opcode);
                if (vp && vp->vp_uint8 == 1) {
                        /*
                         *      Vendor is "MSFT 98"
                         */
-                       vp = fr_pair_find_by_da(head, attr_dhcp_vendor_class_identifier, TAG_ANY);
+                       vp = fr_pair_find_by_da(head, attr_dhcp_vendor_class_identifier);
                        if (vp && (vp->vp_length == 7) && (memcmp(vp->vp_strvalue, "MSFT 98", 7) == 0)) {
-                               vp = fr_pair_find_by_da(head, attr_dhcp_flags, TAG_ANY);
+                               vp = fr_pair_find_by_da(head, attr_dhcp_flags);
 
                                /*
                                 *      Reply should be broadcast.
@@ -302,8 +302,8 @@ int fr_dhcpv4_decode(TALLOC_CTX *ctx, uint8_t const *data, size_t data_len, VALU
         *      Client can request a LARGER size, but not a smaller
         *      one.  They also cannot request a size larger than MTU.
         */
-       maxms = fr_pair_find_by_da(head, attr_dhcp_dhcp_maximum_msg_size, TAG_ANY);
-       mtu = fr_pair_find_by_da(head, attr_dhcp_interface_mtu_size, TAG_ANY);
+       maxms = fr_pair_find_by_da(head, attr_dhcp_dhcp_maximum_msg_size);
+       mtu = fr_pair_find_by_da(head, attr_dhcp_interface_mtu_size);
 
        if (mtu && (mtu->vp_uint16 < DEFAULT_PACKET_SIZE)) {
                fr_strerror_printf("Client says MTU is smaller than minimum permitted by the specification");
@@ -344,7 +344,7 @@ int fr_dhcpv4_packet_encode(RADIUS_PACKET *packet)
        if (packet->code == 0) packet->code = FR_DHCP_NAK;
 
        /* store xid */
-       if ((vp = fr_pair_find_by_da(packet->vps, attr_dhcp_transaction_id, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_da(packet->vps, attr_dhcp_transaction_id))) {
                packet->id = vp->vp_uint32;
        } else {
                packet->id = fr_rand();
index e7dd9d3151cdf286d270b72e8ace72ce9fef9399..b993ab7723f9af76db85e63aa1ea2645bff26238 100644 (file)
@@ -117,7 +117,7 @@ int fr_dhcpv4_raw_packet_send(int sockfd, struct sockaddr_ll *link_layer, RADIUS
 
        /* set ethernet source address to our MAC address (DHCP-Client-Hardware-Address). */
        uint8_t dhmac[ETH_ADDR_LEN] = { 0 };
-       if ((vp = fr_pair_find_by_da(packet->vps, attr_dhcp_client_hardware_address, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_da(packet->vps, attr_dhcp_client_hardware_address))) {
                if (vp->vp_type == FR_TYPE_ETHERNET) memcpy(dhmac, vp->vp_ether, sizeof(vp->vp_ether));
        }
 
@@ -226,7 +226,7 @@ RADIUS_PACKET *fr_dhcv4_raw_packet_recv(int sockfd, struct sockaddr_ll *link_lay
         *      Check if it matches the source HW address used (DHCP-Client-Hardware-Address = 267)
         */
        if ((memcmp(&eth_bcast, &eth_hdr->dst_addr, ETH_ADDR_LEN) != 0) &&
-           (vp = fr_pair_find_by_da(request->vps, attr_dhcp_client_hardware_address, TAG_ANY)) &&
+           (vp = fr_pair_find_by_da(request->vps, attr_dhcp_client_hardware_address)) &&
            ((vp->vp_type == FR_TYPE_ETHERNET) && (memcmp(vp->vp_ether, &eth_hdr->dst_addr, ETH_ADDR_LEN) != 0))) {
 
                /* No match. */
index 5068f648f9f08ecb5a922690e3f35f1d2ee60c04..0238822e754e2e8af9bf436ef6c2c759242ce337 100644 (file)
@@ -594,7 +594,7 @@ ssize_t     fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *orig
        root = fr_dict_root(dict_dhcpv6);
 
        if (!msg_type) {
-               vp = fr_pair_find_by_da(vps, attr_packet_type, TAG_ANY);
+               vp = fr_pair_find_by_da(vps, attr_packet_type);
                if (vp) msg_type = vp->vp_uint32;
        }
 
@@ -614,7 +614,7 @@ ssize_t     fr_dhcpv6_encode(uint8_t *packet, size_t packet_len, uint8_t const *orig
                /*
                 *      We can set an XID, or we can pick a random one.
                 */
-               vp = fr_pair_find_by_da(vps, attr_transaction_id, TAG_ANY);
+               vp = fr_pair_find_by_da(vps, attr_transaction_id);
                if (vp && (vp->vp_length >= 3)) {
                        memcpy(packet + 1, vp->vp_octets, 3);
                } else {
@@ -714,11 +714,6 @@ static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *pare
                return false;
        }
 
-       if (flags->has_tag) {
-               fr_strerror_printf("Tagged attributes cannot be used with DHCP");
-               return false;
-       }
-
        /*
         *      "extra" signifies that subtype is being used by the
         *      dictionaries itself.
index d48e1886cb6102f6145c93918a34b88c883a6d61..dea3aca632308320ffb998480b3396e36eeab9fe 100644 (file)
@@ -1003,7 +1003,7 @@ ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dic
                                    uint8_t const *data, size_t const attr_len, size_t const packet_len,
                                    void *decoder_ctx)
 {
-       int8_t                  tag = TAG_NONE;
+//     int8_t                  tag = 0;
        size_t                  data_len;
        ssize_t                 rcode;
        uint32_t                vendor;
@@ -1067,12 +1067,12 @@ ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dic
 
                        if (parent->type == FR_TYPE_STRING) {
                                memcpy(buffer, p + 1, data_len - 1);
-                               tag = p[0];
+                               //tag = p[0];
                                data_len -= 1;
 
                        } else if (parent->type == FR_TYPE_UINT32) {
                                memcpy(buffer, p, attr_len);
-                               tag = buffer[0];
+                               //tag = buffer[0];
                                buffer[0] = 0;
                        }
 
@@ -1421,7 +1421,7 @@ ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dic
                        fr_strerror_printf("%s: Internal sanity check %d", __FUNCTION__, __LINE__);
                        return -1;
                }
-               tag = TAG_NONE;
+               //tag = 0;
 #ifndef NDEBUG
                /*
                 *      Fix for Coverity.
@@ -1440,7 +1440,7 @@ ssize_t fr_radius_decode_pair_value(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dic
         */
        vp = fr_pair_afrom_da(ctx, parent);
        if (!vp) return -1;
-       vp->tag = tag;
+//     vp->tag = tag;
 
        switch (parent->type) {
        case FR_TYPE_OCTETS:
index 5f9a2fddfe81f5b39168cf684320f085cf9d7843..c0f3483690471b905cfa8a39a97aec131d3b4665 100644 (file)
@@ -32,6 +32,9 @@ RCSID("$Id$")
 #include <freeradius-devel/io/test_point.h>
 #include "attrs.h"
 
+#define TAG_VALID(x)           ((x) > 0 && (x) < 0x20)
+#define TAG_VALID_ZERO(x)              ((x) >= 0 && (x) < 0x20)
+
 static ssize_t encode_value(uint8_t *out, size_t outlen,
                            fr_da_stack_t *da_stack, unsigned int depth,
                            fr_cursor_t *cursor, void *encoder_ctx);
@@ -72,7 +75,7 @@ void fr_radius_encode_chap_password(uint8_t out[static 1 + RADIUS_CHAP_CHALLENGE
         *      Use Chap-Challenge pair if present,
         *      Request Authenticator otherwise.
         */
-       challenge = fr_pair_find_by_da(packet->vps, attr_chap_challenge, TAG_ANY);
+       challenge = fr_pair_find_by_da(packet->vps, attr_chap_challenge);
        if (challenge) {
                fr_md5_update(md5_ctx, challenge->vp_octets, challenge->vp_length);
        } else {
@@ -457,9 +460,9 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
         *      tag, then we always encode a tag byte, even one that
         *      is zero.
         */
-       if ((vp->da->type == FR_TYPE_STRING) && vp->da->flags.has_tag && (TAG_VALID(vp->tag) || TAG_VALID_ZERO(vp->vp_strvalue[0]))) {
+       if ((vp->da->type == FR_TYPE_STRING) && vp->da->flags.has_tag) {
                CHECK_FREESPACE(out_end - out_p, 1);
-               *out_p++ = vp->tag;
+//             *out_p++ = vp->tag;
                value_start = out_p;
        }
 
@@ -628,7 +631,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
                 *
                 *      Not sure why we do this, but the old code did...
                 */
-               if (vp->da->flags.has_tag && !TAG_VALID(vp->tag)) out_p++;
+               if (vp->da->flags.has_tag) out_p++;
 
                slen = encode_tunnel_password(out_p, out_end - out_p,
                                              value_start, value_end - value_start, packet_ctx);
@@ -647,7 +650,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
                 *      Do this after so we don't mess up the input
                 *      value.
                 */
-               if (vp->da->flags.has_tag && !TAG_VALID(vp->tag)) *value_start = 0x00;
+               if (vp->da->flags.has_tag) *value_start = 0x00;
 
                out_p += slen;
        }
@@ -690,7 +693,7 @@ static ssize_t encode_value(uint8_t *out, size_t outlen,
                        fr_strerror_printf("Integer overflow for tagged uint32 attribute");
                        return PAIR_ENCODE_SKIPPED;
                }
-               value_start[0] = vp->tag;
+//             value_start[0] = vp->tag;
        }
 
        FR_PROTO_HEX_DUMP(out, out_p - out, "value %s",
@@ -1499,7 +1502,7 @@ static ssize_t fr_radius_encode_proto(UNUSED TALLOC_CTX *ctx, VALUE_PAIR *vps, u
        VALUE_PAIR *vp;
        ssize_t slen;
 
-       vp = fr_pair_find_by_da(vps, attr_packet_type, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_packet_type);
        if (vp) packet_type = vp->vp_uint32;
 
        if ((packet_type == FR_CODE_ACCESS_REQUEST) || (packet_type == FR_CODE_STATUS_SERVER)) {
index 6c5b5c58fa1bfdfa64ac9c7711d2e894058135c6..38f45dc71248f455c1b778064e2d69cd8ff1de1e 100644 (file)
@@ -105,7 +105,6 @@ fr_dict_attr_autoload_t libfreeradius_tacacs_dict_attr[] = {
        { NULL }
 };
 
-
 char const *fr_tacacs_packet_codes[] = {
        [FR_PACKET_TYPE_VALUE_AUTHENTICATION_START] = "Authentication-Start",
        [FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE] = "Authentication-Continue",
index 1bf3e77e7488f771776740c428ddcbbd2ab89135..805c69dcd1d442e7f68a78ddcdbcbd21b84183be 100644 (file)
@@ -107,7 +107,7 @@ static ssize_t tacacs_encode_field(VALUE_PAIR *vps, fr_dict_attr_t const *da, ui
        VALUE_PAIR *vp;
        uint8_t *p = *data;
 
-       vp = fr_pair_find_by_da(vps, da, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, da);
        if (!vp || !vp->vp_length) return 0;
 
        if (vp->vp_length > max_len) return 0;
@@ -127,7 +127,7 @@ static ssize_t tacacs_encode_field(VALUE_PAIR *vps, fr_dict_attr_t const *da, ui
  *     doesn't specify them, then they don't get encoded.
  */
 #define ENCODE_FIELD_UINT8(_field, _da) do { \
-       vp = fr_pair_find_by_da(vps, _da, TAG_ANY); \
+       vp = fr_pair_find_by_da(vps, _da); \
        _field = (vp) ? vp->vp_uint8 : 0; \
 } while (0)
 
@@ -508,11 +508,11 @@ ssize_t fr_tacacs_encode(uint8_t *buffer, size_t buffer_len, uint8_t const *orig
 
                check_request:
                        if (!buffer[0]) buffer[0] = 0xc1; /* version 12.1 */
-                       
+
                        /*
                         *      If the caller didn't set a session ID, use a random one.
                         */
-                       if (!fr_pair_find_by_da(vps, attr_tacacs_session_id, TAG_ANY)) {
+                       if (!fr_pair_find_by_da(vps, attr_tacacs_session_id)) {
                                packet->hdr.session_id = fr_rand();
                        }
 
index a8b210a2e3ba25e94779126bdfb5b1993e41b92b..77073aeb1024efecb0099066ea0d9e76a328fd50 100644 (file)
@@ -282,14 +282,14 @@ ssize_t fr_vmps_encode(uint8_t *buffer, size_t buflen, uint8_t const *original,
        }
 
        /* If the 'code' exist in the VP's, use it. */
-       vp = fr_pair_find_by_da(vps, attr_packet_type, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_packet_type);
        if (vp) our_code = vp->vp_uint8;
 
        /* fill up */
        buffer[0] = FR_VQP_VERSION;     /* Version */
        buffer[1] = our_code;           /* Opcode */
 
-       vp = fr_pair_find_by_da(vps, attr_error_code, TAG_ANY);
+       vp = fr_pair_find_by_da(vps, attr_error_code);
        buffer[2] = (vp) ? vp->vp_uint8 : FR_ERROR_CODE_VALUE_NO_ERROR; /* Response Code */
 
        buffer[3] = 0;                  /* Data Count */
@@ -301,7 +301,7 @@ ssize_t fr_vmps_encode(uint8_t *buffer, size_t buflen, uint8_t const *original,
                uint32_t our_seq_no = seq_no;
                uint32_t sequence;
 
-               vp = fr_pair_find_by_da(vps, attr_sequence_number, TAG_ANY);
+               vp = fr_pair_find_by_da(vps, attr_sequence_number);
                if (vp) our_seq_no = vp->vp_uint32;
 
                sequence = htonl(our_seq_no);
@@ -631,4 +631,4 @@ extern fr_test_point_proto_encode_t vmps_tp_encode_proto;
 fr_test_point_proto_encode_t vmps_tp_encode_proto = {
        .test_ctx       = encode_test_ctx,
        .func           = fr_vmps_encode_proto
-};
\ No newline at end of file
+};
index 302f394e22cb8db90de1f837622f6d1733030258..dcf36a7a43c99acb26dba2f3769e986b72b308cb 100644 (file)
@@ -6,5 +6,5 @@
 #  Set CHAP-Password
 #
 update request {
-       &CHAP-Password := "%{chap_password:%{request:CHAP-Password}}"
+       &CHAP-Password := "%{chap_password:%{request.CHAP-Password}}"
 }
index e70ee15ff16f67b7baf1a6e13801384aa8329878..3321c16e332401bbb609c1c71f060a451577a517 100644 (file)
@@ -10,5 +10,5 @@ update control {
 #  Set CHAP-Password
 #
 update request {
-       &CHAP-Password := "%{chap_password:%{request:CHAP-Password}}"
+       &CHAP-Password := "%{chap_password:%{request.CHAP-Password}}"
 }
index 8481a55aa62e474d0e88e3c0d24cd744b3f06b7e..d1cd000cc32335a10218678d5a55f69d82d5fe47 100644 (file)
@@ -8,7 +8,7 @@ update {
 }
 
 update {
-       &request:Tmp-String-1 := "%{concat:, %{request:[*]}}"
+       &request:Tmp-String-1 := "%{concat:, %{request[*]}}"
        &request:Tmp-String-2 := "%{concat:, %{Tmp-String-0[*]}}"
 }
 
index 6eb186cec4896d6c3e3d1301cc629d080a874ddb..b11fd23ef1813082912c9d9b3767de9512a027bd 100644 (file)
@@ -14,35 +14,35 @@ update request {
        &Tmp-String-7 := 'Å’=C4=8Cÿ'
 }
 
-if (<string>"%{escape:%{request:Tmp-String-0}}" != &Tmp-String-0) {
+if (<string>"%{escape:%{Tmp-String-0}}" != &Tmp-String-0) {
        test_fail
 }
 
-if (<string>"%{escape:%{request:Tmp-String-1}}" != &Tmp-String-3) {
+if (<string>"%{escape:%{Tmp-String-1}}" != &Tmp-String-3) {
        test_fail
 }
 
-if (<string>"%{escape:%{request:Tmp-String-2}}" != &Tmp-String-4) {
+if (<string>"%{escape:%{Tmp-String-2}}" != &Tmp-String-4) {
        test_fail
 }
 
-if (<string>"%{unescape:%{request:Tmp-String-0}}" != &Tmp-String-0) {
+if (<string>"%{unescape:%{Tmp-String-0}}" != &Tmp-String-0) {
        test_fail
 }
 
-if (<string>"%{unescape:%{request:Tmp-String-3}}" != "%{Tmp-String-1}") {
+if (<string>"%{unescape:%{Tmp-String-3}}" != "%{Tmp-String-1}") {
        test_fail
 }
 
-if (<string>"%{unescape:%{request:Tmp-String-4}}" != &Tmp-String-2) {
+if (<string>"%{unescape:%{Tmp-String-4}}" != &Tmp-String-2) {
        test_fail
 }
 
-if (<string>"%{escape:%{request:Tmp-String-6}}" != &Tmp-String-7) {
+if (<string>"%{escape:%{Tmp-String-6}}" != &Tmp-String-7) {
        test_fail
 }
 
-if (<string>"%{unescape:%{request:Tmp-String-7}}" != &Tmp-String-6) {
+if (<string>"%{unescape:%{Tmp-String-7}}" != &Tmp-String-6) {
        test_fail
 }
 
index 362a2242670d6ac397b28c02d415389e3571762d..dfffe7b17e919c327149fd789253af4555fe2757 100644 (file)
@@ -111,7 +111,7 @@ if (&request:Tmp-String-1 =~ /%{Tmp-String-2[#]}$/) {
 #
 #  XLAT virtual comparisons
 #
-if (&control:Tmp-Integer-0[*] != "%{control:Tmp-Integer-0[#]}") {
+if (&control:Tmp-Integer-0[*] != "%{control.Tmp-Integer-0[#]}") {
        test_fail
 }
 
index 996512a0e09e119fc4a5df34ae920682473faa3d..cee2384dd5c92b9f40d220024041f7334ed0bb10 100644 (file)
@@ -6,18 +6,20 @@
 #  Malformed NAS-IP-Address
 #
 update request {
-       &raw.Attr-4 = 0x010203
+       &raw.4 = 0x010203
 }
 
-if (!&raw.Attr-4) {
+debug_request
+
+if (!&raw.4) {
        update reply {
                &Filter-Id := "Cannot find newly added raw attribute"
        }
 }
 else {
-       if (&raw.Attr-4 != 0x010203) {
+       if (&raw.4 != 0x010203) {
                update reply {
-                       &Filter-Id := "Attr-4 is not 0x0120203"
+                       &Filter-Id := "raw.4 is not 0x0120203"
                }
        }
 }
index 19e0a7d66ab5a97ad32e5fe78747f6eed4daaeac..5b851abf71ec1d9f4d2ce05cc11e8b572bf8e3ae 100644 (file)
@@ -8,11 +8,11 @@ update control {
        &Framed-IP-Address := 198.51.0.1
 }
 
-if ("%{control:Tmp-Cast-IPv6Prefix}" != '::198.51.0.0/112') {
+if (&control:Tmp-Cast-IPv6Prefix != '::198.51.0.0/112') {
        test_fail
 }
 
-if ("%{control:Tmp-Cast-IPv4Prefix}" != '198.51.0.0/16') {
+if (&control:Tmp-Cast-IPv4Prefix != '198.51.0.0/16') {
        test_fail
 }
 
index 170cf74b5cd9d07fb1af656a96ff103bf982c5aa..1793b89693064cc6602fcac4ce1f6f42e094c403 100644 (file)
@@ -13,8 +13,8 @@ update {
 update request {
         &Tmp-Octets-1           := "%{md4:This is a string\n}"
         &Tmp-Octets-2           := "%{md4:%{Tmp-String-0}}"
-        &Tmp-Octets-3           := "%{md4:%{request:Tmp-String-0}}"
-        &Tmp-Octets-4           := "%{md4:%{request:Tmp-Octets-0}}"
+        &Tmp-Octets-3           := "%{md4:%{request.Tmp-String-0}}"
+        &Tmp-Octets-4           := "%{md4:%{request.Tmp-Octets-0}}"
         &Tmp-Octets-5           := "%{md4:%{Tmp-String-9}}"
 }
 
index dc7eb545816589f37ee9158fe422d7a81b028395..039f278be1f5f1c90d7756f1a4a24325c6aa80cb 100644 (file)
@@ -13,8 +13,8 @@ update {
 update request {
         &Tmp-Octets-1           := "%{md5:This is a string\n}"
         &Tmp-Octets-2           := "%{md5:%{Tmp-String-0}}"
-        &Tmp-Octets-3           := "%{md5:%{request:Tmp-String-0}}"
-        &Tmp-Octets-4           := "%{md5:%{request:Tmp-Octets-0}}"
+        &Tmp-Octets-3           := "%{md5:%{request.Tmp-String-0}}"
+        &Tmp-Octets-4           := "%{md5:%{request.Tmp-Octets-0}}"
         &Tmp-Octets-5           := "%{md5:%{Tmp-String-9}}"
         &Tmp-Octets-6           := "%{hmacmd5:%{Tmp-String-1} %{Tmp-String-2}}"
         &Tmp-Octets-7           := "%{hmacmd5:%{Tmp-String-3} %{Tmp-String-3}}"
index f7f3cc4f492e7d56beb9b3f223744ced5291dd75..c28de33ad8fd4949be977f001c49d33c6e9702e4 100644 (file)
@@ -6,7 +6,6 @@ update {
        &request:Tmp-String-0 += "This is another one"
        &request:Tmp-Octets-0 := 0x000504030201
        &request:Tmp-Integer-0 := 7331
-       &request:Tunnel-Private-Group-Id:5 = 127.0.0.1
 }
 
 update {
@@ -17,7 +16,7 @@ update {
        &request:Tmp-String-5 := "%{pairs:control:User-Name}"
 }
 
-if (&request:Tmp-String-1 != "User-Name = \"bob\", User-Password = \"hello\", Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\", Tmp-Octets-0 = 0x000504030201, Tmp-Integer-0 = 7331, Tunnel-Private-Group-Id:5 = \"127.0.0.1\"") {
+if (&request:Tmp-String-1 != "User-Name = \"bob\", User-Password = \"hello\", Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\", Tmp-Octets-0 = 0x000504030201, Tmp-Integer-0 = 7331") {
        test_fail
 }
 
index c13ae96cfed863293688837187c11e3e8ed1f3d7..5aa003f3aa3b85f61f47c2d73ffe4736d3e9593e 100644 (file)
@@ -10,7 +10,7 @@ update {
 #  Unencoded Cleartext-Password in password with header
 #
 update {
-       &control:Password-With-Header := "%{request:User-Password}"
+       &control:Password-With-Header := "%{User-Password}"
 }
 pap.authorize
 pap.authenticate {
@@ -31,7 +31,7 @@ update {
        &Tmp-String-1 := "{clear}%{User-Password}"
 }
 update {
-       &control:Password-With-Header := "%{base64:%{request:Tmp-String-1}}"
+       &control:Password-With-Header := "%{base64:%{Tmp-String-1}}"
 }
 pap.authorize
 pap.authenticate {
@@ -49,7 +49,7 @@ update {
 #  Hex encoded SSHA password
 #
 update {
-       &control:Password-With-Header += "{ssha}%{hex:%{sha1:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Password-With-Header += "{ssha}%{hex:%{sha1:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 pap.authorize
@@ -68,21 +68,21 @@ update {
 #  Base64 encoded SSHA password
 #
 update {
-       &control:Tmp-String-1 := "%{hex:%{sha1:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Tmp-String-1 := "%{hex:%{sha1:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 # To Binary
 update {
-       &control:Tmp-Octets-0 := "%{bin:%{control:Tmp-String-1}}"
+       &control:Tmp-Octets-0 := "%{bin:%{control.Tmp-String-1}}"
 }
 
 # To Base64
 update {
-       &control:Tmp-String-1 := "%{base64:%{control:Tmp-Octets-0}}"
+       &control:Tmp-String-1 := "%{base64:%{control.Tmp-Octets-0}}"
 }
 
 update {
-       &control:Password-With-Header += "{ssha}%{control:Tmp-String-1}"
+       &control:Password-With-Header += "{ssha}%{control.Tmp-String-1}"
 }
 
 pap.authorize
@@ -101,21 +101,21 @@ update {
 #  Base64 of Base64 encoded SSHA password
 #
 update {
-       &control:Tmp-String-1 := "%{hex:%{sha1:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Tmp-String-1 := "%{hex:%{sha1:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 # To Binary
 update {
-       &control:Tmp-Octets-0 := "%{bin:%{control:Tmp-String-1}}"
+       &control:Tmp-Octets-0 := "%{bin:%{control.Tmp-String-1}}"
 }
 
 # To Base64
 update {
-       &control:Tmp-String-1 := "{ssha}%{base64:%{control:Tmp-Octets-0}}"
+       &control:Tmp-String-1 := "{ssha}%{base64:%{control.Tmp-Octets-0}}"
 }
 
 update {
-       &control:Password-With-Header += "%{base64:%{control:Tmp-String-1}}"
+       &control:Password-With-Header += "%{base64:%{control.Tmp-String-1}}"
 }
 
 pap.authorize
index eb2651bdcb8280aaee820b8a0e5e3dfb83600eab..23301247aac9ed03e1c077fc2a7877fbc9785533 100644 (file)
@@ -14,14 +14,14 @@ if ('${feature.tls}' != 'yes') {
 
 update {
        &control: !* ANY
-       &request:Tmp-String-0 := "5RNqNl8iYLbkCc7JhR8as4TtDDCX6otuuWtcja8rITUyx9zrnHSe9tTHGmKK" # 60 byte salt
+       &Tmp-String-0 := "5RNqNl8iYLbkCc7JhR8as4TtDDCX6otuuWtcja8rITUyx9zrnHSe9tTHGmKK" # 60 byte salt
 }
 
 #
 #  Hex encoded SSHA2-512 password
 #
 update {
-       &control:Password-With-Header += "{ssha512}%{hex:%{sha2_512:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Password-With-Header += "{ssha512}%{hex:%{sha2_512:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 pap.authorize
@@ -40,21 +40,21 @@ update {
 #  Base64 encoded SSHA2-512 password
 #
 update {
-       &control:Tmp-String-1 := "%{hex:%{sha2_512:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Tmp-String-1 := "%{hex:%{sha2_512:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 # To Binary
 update {
-       &control:Tmp-Octets-0 := "%{bin:%{control:Tmp-String-1}}"
+       &control:Tmp-Octets-0 := "%{bin:%{control.Tmp-String-1}}"
 }
 
 # To Base64
 update {
-       &control:Tmp-String-1 := "%{base64:%{control:Tmp-Octets-0}}"
+       &control:Tmp-String-1 := "%{base64:%{control.Tmp-Octets-0}}"
 }
 
 update {
-       &control:Password-With-Header += "{ssha512}%{control:Tmp-String-1}"
+       &control:Password-With-Header += "{ssha512}%{control.Tmp-String-1}"
 }
 
 pap.authorize
@@ -73,21 +73,21 @@ update {
 #  Base64 of Base64 encoded SSHA2-512 password
 #
 update {
-       &control:Tmp-String-1 := "%{hex:%{sha2_512:%{request:User-Password}%{request:Tmp-String-0}}}%{hex:%{request:Tmp-String-0}}"
+       &control:Tmp-String-1 := "%{hex:%{sha2_512:%{User-Password}%{Tmp-String-0}}}%{hex:%{Tmp-String-0}}"
 }
 
 # To Binary
 update {
-       &control:Tmp-Octets-0 := "%{bin:%{control:Tmp-String-1}}"
+       &control:Tmp-Octets-0 := "%{bin:%{control.Tmp-String-1}}"
 }
 
 # To Base64
 update {
-       &control:Tmp-String-1 := "{ssha512}%{base64:%{control:Tmp-Octets-0}}"
+       &control:Tmp-String-1 := "{ssha512}%{base64:%{control.Tmp-Octets-0}}"
 }
 
 update {
-       &control:Password-With-Header += "%{base64:%{control:Tmp-String-1}}"
+       &control:Password-With-Header += "%{base64:%{control.Tmp-String-1}}"
 }
 
 pap.authorize
@@ -106,7 +106,7 @@ update {
 #  Base64 of SHA2-384 password (in SHA2-Password)
 #
 update control {
-       &control:SHA2-Password := "%{hex:%{sha2_384:%{request:User-Password}}}"
+       &control:SHA2-Password := "%{hex:%{sha2_384:%{User-Password}}}"
 }
 
 pap.authorize
@@ -129,7 +129,7 @@ update control {
 #  Base64 of SHA2-256 password (in SHA2-256-Password)
 #
 update control {
-       &control:SHA2-256-Password := "%{hex:%{sha2_256:%{request:User-Password}}}"
+       &control:SHA2-256-Password := "%{hex:%{sha2_256:%{User-Password}}}"
 }
 
 pap.authorize
@@ -148,7 +148,7 @@ update {
 #  Base64 of SHA2-224 password (in SHA2-224-Password - No hex armour)
 #
 update control {
-       &control:SHA2-224-Password := "%{sha2_224:%{request:User-Password}}"
+       &control:SHA2-224-Password := "%{sha2_224:%{User-Password}}"
 }
 
 pap.authorize
index 6d1bb6348d90cf6d74d52d05aac7c957eaa117d6..fe424019f875373f318f9853e112717351f9f2a3 100644 (file)
@@ -16,7 +16,7 @@ parallel {
        }
 }
 
-if ("%{control:Tmp-Integer-0[#]}" != 4) {
+if ("%{control.Tmp-Integer-0[#]}" != 4) {
        test_fail
 
        return
index ccbfaf3c1ea14eae8a40ee13863b3f9cb56456cc..e4968f4174a8f346be0f6958e99f4df70ebe2215 100644 (file)
@@ -12,8 +12,8 @@ update {
 update {
        &request:Tmp-Octets-2 := "%{sha1:This is a string\n}"
        &request:Tmp-Octets-3 := "%{sha1:%{Tmp-String-0}}"
-       &request:Tmp-Octets-4 := "%{sha1:%{request:Tmp-String-0}}"
-       &request:Tmp-Octets-5 := "%{sha1:%{request:Tmp-Octets-9}}"
+       &request:Tmp-Octets-4 := "%{sha1:%{request.Tmp-String-0}}"
+       &request:Tmp-Octets-5 := "%{sha1:%{request.Tmp-Octets-9}}"
        &request:Tmp-Octets-6 := "%{hmacsha1:%{Tmp-String-1} %{Tmp-String-2}}"
        &request:Tmp-Octets-7 := "%{hmacsha1:%{Tmp-String-3} %{Tmp-String-3}}"
        &request:Tmp-Octets-8 := "%{hmacsha1:%{Tmp-String-1} }"
index b73a7aac61627a6dad77680faf2e40668e8d0682..aef16ffb8f95ce94105d0cb5bfaaab42c02e0a96 100644 (file)
@@ -9,9 +9,9 @@ update {
 update {
        &request:Tmp-Octets-1 := "%{sha2_256:This is a string\n}"
        &request:Tmp-Octets-2 := "%{sha2_256:%{Tmp-String-0}}"
-       &request:Tmp-Octets-3 := "%{sha2_256:%{request:Tmp-String-0}}"
-       &request:Tmp-Octets-4 := "%{sha2_256:%{request:Tmp-Octets-0}}"
-       &request:Tmp-Octets-5 := "%{sha2_256:%{request:Tmp-Octets-9}}"
+       &request:Tmp-Octets-3 := "%{sha2_256:%{request.Tmp-String-0}}"
+       &request:Tmp-Octets-4 := "%{sha2_256:%{request.Tmp-Octets-0}}"
+       &request:Tmp-Octets-5 := "%{sha2_256:%{request.Tmp-Octets-9}}"
 }
 
 #
@@ -51,7 +51,7 @@ if (&request:Tmp-Octets-5 != 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca
 update {
        &request:Tmp-Octets-1 := "%{sha2_512:This is a string\n}"
        &request:Tmp-Octets-2 := "%{sha2_512:%{Tmp-String-0}}"
-       &request:Tmp-Octets-3 := "%{sha2_512:%{request:Tmp-Octets-0}}"
+       &request:Tmp-Octets-3 := "%{sha2_512:%{request.Tmp-Octets-0}}"
 }
 
 if (&request:Tmp-Octets-1 != 0x56b57df5cce42d4e35c644649798ea23ec16f4f4626e78faf4d2d8f430ea349bcc28cd5532457c82f0aa66bf68988346039fe75b900a92ff94fd53993d45990f) {
index 4aca8817611201c5c8afd04981e3a150017016be..8e2b1d0fed4698fd670fea924407aa9643a08f16 100644 (file)
@@ -2,7 +2,7 @@ update request {
        &Tmp-String-0 := 'foo'
 }
 
-switch "%{tolower:%{request:Tmp-String-0}}" {
+switch "%{tolower:%{Tmp-String-0}}" {
        case 'foo' {
                success
        }
@@ -16,7 +16,7 @@ switch "%{tolower:%{request:Tmp-String-0}}" {
        }
 }
 
-switch "%{request:Tmp-String-0}" {
+switch "%{Tmp-String-0}" {
        case 'foo' {
                success
        }
diff --git a/src/tests/keywords/switch-value-error2 b/src/tests/keywords/switch-value-error2
deleted file mode 100644 (file)
index a45de77..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-#  PRE: switch-value-error
-#
-#  The same as "switch-value-error", but the attribute
-#  is hidden inside of an xlat expansion.  We now turn
-#  simple attribute xlats into templates.
-#
-switch "%{Service-Type}" {
-       case "%{expr: 1 + 2}" {
-               test_fail
-       }
-
-       case Login-User {
-               test_fail
-       }
-
-       case No-Such-Value {    # ERROR
-               test_fail
-       }
-
-       case {
-               test_fail
-       }
-
-}
index 46a21ff751487c067031e54c6af8d48ce277119d..1eafcadaebd66c3e27e011195442d1e0b9970eab 100644 (file)
@@ -9,10 +9,10 @@ update request {
 #  Check that a known but malformed attr is malformed
 #
 update request {
-       &Attr-26.24757.84.9.5.7 = 0xab
+       &26.24757.84.9.5.7 = 0xab
 }
 
-if (&Attr-26.24757.84.9.5.7 != 0xab) {
+if (&26.24757.84.9.5.7 != 0xab) {
        test_fail
 }
 
@@ -20,10 +20,10 @@ if (&Attr-26.24757.84.9.5.7 != 0xab) {
 #  Check that an unknown attr is OK
 #
 update request {
-       &Attr-26.24757.84.9.5.15 = 0xabcdef
+       &26.24757.84.9.5.15 = 0xabcdef
 }
 
-if (&Attr-26.24757.84.9.5.15 != 0xabcdef) {
+if (&26.24757.84.9.5.15 != 0xabcdef) {
        test_fail
 }
 
@@ -31,16 +31,16 @@ if (&Attr-26.24757.84.9.5.15 != 0xabcdef) {
 #  Check that unknown attributes which are defined
 #  get automatically resolved to the real attribute.
 #
-if (&Attr-26.11344.1 == 127.0.0.1) {
+if (&26.11344.1 == 127.0.0.1) {
        test_fail
 }
 
-if (&Attr-26.11344.1 != 127.0.0.2) {
+if (&26.11344.1 != 127.0.0.2) {
        test_fail
 }
 
 update request {
-       &Attr-26.11344.1 := 0x7f000001
+       &26.11344.1 := 0x7f000001
 }
 
 if (&FreeRADIUS-Proxied-To == 127.0.0.2) {
@@ -51,19 +51,19 @@ if (&FreeRADIUS-Proxied-To != 127.0.0.1) {
        test_fail
 }
 
-if (&Attr-26.11344.1 == 127.0.0.2) {
+if (&26.11344.1 == 127.0.0.2) {
        test_fail
 }
 
-if (&Attr-26.11344.1 != 127.0.0.1) {
+if (&26.11344.1 != 127.0.0.1) {
        test_fail
 }
 
 update request {
-       &Attr-26.66.1 = 0x01020304
+       &26.66.1 = 0x01020304
 }
 
-if (&Attr-26.66.1 != 0x01020304) {
+if (&26.66.1 != 0x01020304) {
        test_fail
 }
 
diff --git a/src/tests/keywords/update-add-ref-index b/src/tests/keywords/update-add-ref-index
deleted file mode 100644 (file)
index 1425010..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-#  PRE: update array
-#
-
-update request {
-       &Class := 0x01020304
-       &Class += 0x05060708
-       &Class += 0x090a0b0c
-}
-
-#
-#  Copy all the class attributes to Proxy-State
-#
-update request {
-       &Proxy-State += &Class[*]
-}
-
-if (&Proxy-State[0] != 0x01020304) {
-       test_fail
-}
-
-#  Must be the same as above
-if (&Proxy-State[1] != 0x05060708) {
-       test_fail
-}
-
-if (&Proxy-State[2] != 0x090a0b0c) {
-       test_fail
-}
-
-# must not exist
-if (&Proxy-State[3]) {
-       test_fail
-}
-
-# Remove all the Proxy-State attributes
-update request {
-       &Proxy-State !* ANY
-}
-
-#
-#  Copy the first instance of class
-#
-update request {
-       &Proxy-State += &Class[0]
-}
-
-if (&Proxy-State[0] != 0x01020304) {
-       test_fail
-}
-
-#  Must be the same as above
-if (&Proxy-State[1]) {
-       test_fail
-}
-
-# Remove all the Proxy-State attributes
-update request {
-       &Proxy-State !* ANY
-}
-
-#
-#  Copy the first instance (explicitly) of class
-#
-update request {
-       &Proxy-State += &Class[0]
-}
-
-if (&Proxy-State[0] != 0x01020304) {
-       test_fail
-}
-
-#  Must be the same as above
-if (&Proxy-State[1]) {
-       test_fail
-}
-
-# Remove all the Proxy-State attributes
-update request {
-       &Proxy-State !* ANY
-}
-
-#
-#  Copy the second instance of class
-#
-update request {
-       &Proxy-State += &Class[1]
-}
-
-if (&Proxy-State[0] != 0x05060708) {
-       test_fail
-}
-
-#  Must be the same as above
-if (&Proxy-State[1]) {
-       test_fail
-}
-
-success
diff --git a/src/tests/keywords/update-add-ref-tag b/src/tests/keywords/update-add-ref-tag
deleted file mode 100644 (file)
index 2826352..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-#  PRE: update array
-#
-
-update request {
-       &request:Tunnel-Server-Endpoint += '192.0.1.1'
-       &request:Tunnel-Server-Endpoint += '192.0.1.2'
-       &request:Tunnel-Server-Endpoint:1 += '192.0.1.1'
-       &request:Tunnel-Server-Endpoint:2 += '192.0.2.1'
-       &request:Tunnel-Server-Endpoint:2 += '192.0.2.2'
-       &request:Tunnel-Server-Endpoint:3 += '192.0.3.1'
-}
-
-#
-#  Copy all Tunnel-Server-Endpoint attributes
-#
-update request {
-       &control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint[*]
-}
-
-if ((&control:Tunnel-Server-Endpoint[0] != '192.0.1.1') || \
-    (&control:Tunnel-Server-Endpoint[1] != '192.0.1.2') || \
-    (&control:Tunnel-Server-Endpoint[2] != '192.0.1.1') || \
-    (&control:Tunnel-Server-Endpoint[3] != '192.0.2.1') || \
-    (&control:Tunnel-Server-Endpoint[4] != '192.0.2.2') || \
-    (&control:Tunnel-Server-Endpoint[5] != '192.0.3.1') || \
-    &control:Tunnel-Server-Endpoint[6]) {
-       test_fail
-}
-
-#
-#  Clear out control attributes...
-#
-update control {
-       &Tunnel-Server-Endpoint !* ANY
-}
-
-#
-#  Copy all Tunnel-Server-Endpoint attributes with tag 2
-#
-update request {
-       &control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:2[*]
-}
-
-if ((&control:Tunnel-Server-Endpoint[0] != '192.0.2.1') || \
-    (&control:Tunnel-Server-Endpoint[1] != '192.0.2.2') || \
-    &control:Tunnel-Server-Endpoint[2]) {
-       test_fail
-}
-
-#
-#  Clear out control attributes...
-#
-update control {
-       &Tunnel-Server-Endpoint !* ANY
-}
-
-#
-#  Copy all Tunnel-Server-Endpoint attributes with no tag
-#
-update request {
-       &control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:0[*]
-}
-
-if ((&control:Tunnel-Server-Endpoint[0] != '192.0.1.1') || \
-    (&control:Tunnel-Server-Endpoint[1] != '192.0.1.2') || \
-    &control:Tunnel-Server-Endpoint[2]) {
-       test_fail
-}
-
-#
-#  Clear out control attributes...
-#
-update control {
-       &Tunnel-Server-Endpoint !* ANY
-}
-
-#
-#  Copy the first attribute with tag 2 (explicit)
-#
-update request {
-       &control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:2[0]
-}
-
-if ((&control:Tunnel-Server-Endpoint[0] != '192.0.2.1') || \
-    &control:Tunnel-Server-Endpoint[1]) {
-       test_fail
-}
-
-success
index 9684c21da1c8d6a332e6272474b3e9a39cb0e83c..d86edd491123ba1e4c1eae0ac071413dd4b43e57 100644 (file)
@@ -31,7 +31,7 @@ if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'd') ||
 
 # Verify we haven't acquired any extra..
 
-if ("%{control:Reply-Message[#]}" != 3) {
+if ("%{control.Reply-Message[#]}" != 3) {
        test_fail
 }
 
index ff0a97cc1e0b2ba86c178845ad9374a8a7118f27..f026bfecf505b3a3c8315b8841ba39f72066942c 100644 (file)
@@ -31,7 +31,7 @@ if (&Tmp-IP-Address-0 || ("%{Tmp-IP-Address-0[1]}" != '')) {
 }
 
 # But there should still be some in the control list
-if ((&control:Tmp-IP-Address-0 != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
+if ((&control.Tmp-IP-Address-0 != 192.0.2.1) || ("%{control.Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
        test_fail
 }
 
diff --git a/src/tests/keywords/update-remove-tag b/src/tests/keywords/update-remove-tag
deleted file mode 100644 (file)
index 31ee13f..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#
-# PRE: update update-remove-value update-remove-index update-tag
-#
-
-update {
-       &request:Tunnel-Server-Endpoint += '192.0.1.1'
-       &request:Tunnel-Server-Endpoint += '192.0.1.2'
-       &request:Tunnel-Server-Endpoint:1 += '192.0.1.1'
-       &request:Tunnel-Server-Endpoint:2 += '192.0.2.1'
-       &request:Tunnel-Server-Endpoint:2 += '192.0.2.2'
-       &request:Tunnel-Server-Endpoint:3 += '192.0.3.1'
-       &request:Tunnel-Server-Endpoint:3 += '192.0.3.2'
-       &request:Tunnel-Server-Endpoint:3 += '192.0.3.3'
-}
-update {
-       &control: += &request:
-}
-
-# Check [#] is working correctly (should probably be another set of tests)
-if (("%{request:Tunnel-Server-Endpoint:*[#]}" != 8) || \
-    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \
-    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
-    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 2) || \
-    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
-       test_fail
-}
-
-update {
-       &Tunnel-Server-Endpoint !* ANY
-}
-
-# List should now be empty
-if ("%{request:Tunnel-Server-Endpoint:*[#]}" != 0) {
-       test_fail
-}
-
-# Reset the list
-update {
-       &request: !* ANY
-       &request: += &control:
-}
-
-# Now remove all Tunnel-Server-Endpoint attributes with :2
-update {
-       &Tunnel-Server-Endpoint:2 !* ANY
-}
-
-if (("%{request:Tunnel-Server-Endpoint:*[#]}" != 6) || \
-    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \
-    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
-    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
-       test_fail
-}
-
-# Now remove all Tunnel-Server-Endpoint attributes with :0 (no tags)
-update {
-       &Tunnel-Server-Endpoint:0 !* ANY
-}
-
-if (("%{request:Tunnel-Server-Endpoint:*[#]}" != 4) || \
-    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
-    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
-       test_fail
-}
-
-# Now remove all Tunnel-Server-Endpoint attributes with :3
-update {
-       &Tunnel-Server-Endpoint:3 !* ANY
-}
-
-if (("%{request:Tunnel-Server-Endpoint:*[#]}" != 1) || \
-    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
-    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) {
-       test_fail
-}
-
-# Now remove all Tunnel-Server-Endpoint attributes with :1
-update {
-       &Tunnel-Server-Endpoint:1 !* ANY
-}
-
-if (("%{request:Tunnel-Server-Endpoint:*[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
-    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) {
-       test_fail
-}
-
-# Reset the list
-update {
-       &request: !* ANY
-       &request: += &control:
-}
-
-# Remove all Tunnel-Server-Endpoint attributes at :3[0] (none)
-update {
-       &Tunnel-Server-Endpoint:1[3] !* ANY
-}
-
-if (&Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[1] != '192.0.3.2') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[2] != '192.0.3.3') {
-       test_fail
-}
-
-# Remove all Tunnel-Server-Endpoint attributes at :3[1]
-update {
-       &Tunnel-Server-Endpoint:3[1] !* ANY
-}
-
-if (&Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
-       test_fail
-}
-
-# Remove any Tunnel-Server-Endpoint with a value of '192.0.1.1' (should remove both tagged and untagged versions)
-update {
-       &Tunnel-Server-Endpoint -= '192.0.1.1'
-}
-
-# Also checks whether presence checks for tagged attributes work correctly
-if (&request:Tunnel-Server-Endpoint:1) {
-       test_fail
-}
-
-if (&request:Tunnel-Server-Endpoint:0[0] != '192.0.1.2') {
-       test_fail
-}
-
-# Remove any Tunnel-Server-Endpoint with a value of '192.0.3.1'
-update {
-       &Tunnel-Server-Endpoint:3 -= '192.0.3.2'
-}
-
-if (&request:Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&request:Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
-       test_fail
-}
-
-# Reset the list
-update {
-       &request: !* ANY
-       &request: += &control:
-}
-
-# Remove only the tagged version of '192.0.1.1'
-update {
-       &request:Tunnel-Server-Endpoint:1 -= '192.0.1.1'
-}
-
-if (&request:Tunnel-Server-Endpoint:0[0] != '192.0.1.1') {
-       test_fail
-}
-
-# Reset the list
-update {
-       &request: !* ANY
-       &request: += &control:
-}
-
-# Remove only the untagged version of '192.0.1.1'
-update {
-       &request:Tunnel-Server-Endpoint:0 -= '192.0.1.1'
-}
-
-if (&request:Tunnel-Server-Endpoint:1[0] != '192.0.1.1') {
-       test_fail
-}
-
-# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.3' (which it does)
-update {
-       &Tunnel-Server-Endpoint:3[1] -= '192.0.3.2'
-}
-
-if (&Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
-       test_fail
-}
-
-# Reset the list
-update {
-       &request: !* ANY
-       &request: += &control:
-}
-
-# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.4' (which it doesn't)
-update {
-       &Tunnel-Server-Endpoint:3[1] -= '192.0.3.4'
-}
-
-if (&Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[1] != '192.0.3.2') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:3[2] != '192.0.3.3') {
-       test_fail
-}
-
-success
index 7c203d657133552e4bffe793f7eb671404986bce..882cd57684dc8a5435e45fafbf6188f317dce79b 100644 (file)
@@ -89,7 +89,7 @@ if ((&Tmp-String-0 != 'foobarbaz') || (&Tmp-Integer-0 != 123456789)) {
 }
 
 # But there should still be some in the control list
-if (("%{control:Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
+if (("%{control.Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{control.Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
        test_fail
 }
 
diff --git a/src/tests/keywords/update-tag b/src/tests/keywords/update-tag
deleted file mode 100644 (file)
index 60a96bf..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#
-# PRE: update
-#
-
-update request {
-       &Tunnel-Server-Endpoint:0 := '192.0.1.1'        # Should not be tagged
-       &Tunnel-Server-Endpoint:0 += '192.0.1.2'        # Should not be tagged
-       &Tunnel-Server-Endpoint:1 := '192.0.2.1'
-       &Tunnel-Server-Endpoint:1 += '192.0.2.2'
-       &Tunnel-Server-Endpoint:2 := '192.0.3.1'
-       &Tunnel-Server-Endpoint:2 += '192.0.3.2'
-}
-
-#
-# Selecting on attributes which have no tag (0)
-#
-if (&Tunnel-Server-Endpoint:0[0] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:0[1] != '192.0.1.2') {
-       test_fail
-}
-
-#
-# Selecting on attributes with no tag specified (should match all of that type)
-#
-if (&Tunnel-Server-Endpoint[0] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint[1] != '192.0.1.2') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint[2] != '192.0.2.1') {
-       test_fail
-}
-
-#
-# Now the none xlat version
-#
-# Check that access attributes by tag works first
-if (&Tunnel-Server-Endpoint:2 != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:2 == '192.0.3.2') {
-       test_fail
-}
-
-if (&Tunnel-Server-Endpoint:1 != '192.0.2.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tunnel-Server-Endpoint:2[0] != '192.0.3.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tunnel-Server-Endpoint:2[1] != '192.0.3.2') {
-       test_fail
-}
-
-#
-# Assignment (bare)
-#
-update request {
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:2[0]           # 0
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:2[0]           # 1
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:1[0]           # 2
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:2[0]           # 3
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:2[1]           # 4
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:0[0]           # 5
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:0[1]           # 6
-       &Tmp-String-1 += &Tunnel-Server-Endpoint:0[2]           # 7 (No attribute should be added here)
-       &Tmp-String-1 += &Tunnel-Server-Endpoint[0]             # 8
-       &Tmp-String-1 += &Tunnel-Server-Endpoint[1]             # 9
-       &Tmp-String-1 += &Tunnel-Server-Endpoint[2]             # 10
-}
-
-# Check that access attributes by tag works first
-if (&Tmp-String-1[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tmp-String-1[1] == '192.0.3.2') {
-       test_fail
-}
-
-if (&Tmp-String-1[2] != '192.0.2.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tmp-String-1[3] != '192.0.3.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tmp-String-1[4] != '192.0.3.2') {
-       test_fail
-}
-
-# Now check the assignment
-if (&Tmp-String-1[5] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tmp-String-1[6] != '192.0.1.2') {
-       test_fail
-}
-
-if (&Tmp-String-1[7] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tmp-String-1[8] != '192.0.1.2') {
-       test_fail
-}
-
-if (&Tmp-String-1[9] != '192.0.2.1') {
-       test_fail
-}
-
-success
index 4d372b28f9c5401fad2b72af34a265d6895a50c4..ad6cf32944778d1965fa6325c321bb11d3feee78 100644 (file)
 #
 
 update {
-       &Attr-26.6139.1 = 0x00000013
-       &Attr-26.9.42 = 0x0001020304
-       &Attr-255 = 0x00
+       &26.6139.1 = 0x00000013
+       &26.9.42 = 0x0001020304
+       &255 = 0x00
 }
 
-if (&Attr-26.6139.1 != 0x00000013) {
+if (&26.6139.1 != 0x00000013) {
        test_fail
 }
 
-if (&Attr-26.9.42 != 0x0001020304) {
+if (&26.9.42 != 0x0001020304) {
        test_fail
 }
 
-if (&Attr-255 != 0x00) {
+if (&255 != 0x00) {
        test_fail
 }
 
index 0077e700d2dc503fe92931866430298fb4ed1b0c..4ae02fda4ff8ab3521f832bf5e90deeca7970098 100644 (file)
@@ -34,10 +34,10 @@ update {
        &Tmp-Integer-0 := 66
 }
 update {
-       "Vendor-%{Tmp-Integer-0}-Attr-1" := 0x01020304
+       "Vendor-%{Tmp-Integer-0}-1" := 0x01020304
 }
 
-if (&Vendor-66-Attr-1 != 0x01020304) {
+if (&Vendor-66-1 != 0x01020304) {
        test_fail
 }
 
@@ -49,7 +49,7 @@ update {
        &Tmp-Integer-0 := 11344
 }
 update {
-       "Vendor-%{Tmp-Integer-0}-Attr-1" := 127.0.0.1
+       "Vendor-%{Tmp-Integer-0}-1" := 127.0.0.1
 }
 
 if (&FreeRADIUS-Proxied-To != 127.0.0.1) {
@@ -57,7 +57,7 @@ if (&FreeRADIUS-Proxied-To != 127.0.0.1) {
 }
 
 update {
-       "Attr-%{attr_num:Tmp-String-1}" := 'hello2'
+       "%{attr_num:Tmp-String-1}" := 'hello2'
 }
 
 if (&Tmp-String-1 != 'hello2') {
index 461dacffdef465f780a0dd8074aaa2004be95c09..3dfebec296304df8f7d253b1a1b7defad4b3c3be 100644 (file)
@@ -40,7 +40,7 @@ if (&Tmp-String-9 != &Tmp-String-4) {
 }
 
 update request {
-       &Tmp-String-1 := "%{urlunquote:%{request:Tmp-String-0}}"
+       &Tmp-String-1 := "%{urlunquote:%{Tmp-String-0}}"
        &Tmp-String-2 := "%{urlunquote:%%E,123}"
 }
 
index 6491cdb203935784eeb9b2f0bd8316059dee0e46..db596222e38ef32c871c593c97d60ccebc59f2f6 100644 (file)
@@ -4,13 +4,13 @@
 #  Tests for Vendor-Specific
 #
 update request {
-       &Attr-26 := 0x01020304
+       &raw.26 := 0x01020304
 }
 
-if (!&Attr-26) {
+if (!&raw.26) {
        test_fail
 }
-elsif (&Attr-26 != 0x01020304) {
+elsif (&raw.26 != 0x01020304) {
        test_fail
 }
 else {
index 4e785118ac20f762a6eba5eefba4bf30826f44a5..bd421b14f714b0e1317f6606675c2fdddcfd8d22 100644 (file)
@@ -1,6 +1,6 @@
 #
 #  PRE: update if
 #
-if (&request:Packet-Type == Access-Request) {
+if (&Packet-Type == Access-Request) {
        success
 }
index f8fc1903181e5f463b200b74610542c9b11b089e..f424c1c133490bf496885adc6155d430ce25e77e 100644 (file)
@@ -9,7 +9,7 @@ if (&WiMAX-PFDv2-Src-Port != 6809) {
 #
 #  This is known, and should be renamed
 update request {
-       &Attr-26.24757.84.9.5.7 = 0x01
+       &26.24757.84.9.5.7 = 0x01
 }
 
 if (&WiMAX-PFDv2-Src-Assigned != 1) {
diff --git a/src/tests/keywords/xlat-attr-tag b/src/tests/keywords/xlat-attr-tag
deleted file mode 100644 (file)
index db76a13..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#
-# PRE: update
-#
-
-update request {
-       &Tunnel-Server-Endpoint   := '192.0.1.1'        # Should not be tagged
-       &Tunnel-Server-Endpoint:0 += '192.0.1.2'        # Should not be tagged
-       &Tunnel-Server-Endpoint:1 := '192.0.2.1'
-       &Tunnel-Server-Endpoint:1 += '192.0.2.2'
-       &Tunnel-Server-Endpoint:2 := '192.0.3.1'
-       &Tunnel-Server-Endpoint:2 += '192.0.3.2'
-}
-
-update request {
-       &Tmp-Integer-0 := 0
-}
-
-# Check the tag printing xlat works correctly
-if ("%{tag:Tunnel-Server-Endpoint[0]}" != '') {
-       test_fail
-}
-
-if ("%{tag:Tunnel-Server-Endpoint[1]}" != '') {
-       test_fail
-}
-
-
-if ("%{tag:Tunnel-Server-Endpoint[2]}" != '1') {
-       test_fail
-}
-
-if ("%{tag:Tunnel-Server-Endpoint[5]}" != '2') {
-       test_fail
-}
-
-if ("%{tag:Tunnel-Server-Endpoint[6]}" != '') {
-       test_fail
-}
-
-if ("%{tag:control:Cleartext-Password[0]}" != '') {
-       test_fail
-}
-
-# There should be no failures so far
-if (&Module-Failure-Message) {
-       test_fail
-}
-
-# Test invalid attributes
-if ("%{tag:foo}" != "") {
-       test_fail
-}
-
-if (&Module-Failure-Message != "Unknown attribute 'foo'") {
-       test_fail
-}
-
-update request {
-       &Module-Failure-Message !* ANY
-}
-
-if ("%{tag:%{control:Cleartext-Password}}" != "") {
-       test_fail
-}
-
-if (&Module-Failure-Message != "Unknown attribute 'hello'") {
-       test_fail
-}
-
-update request {
-       &Module-Failure-Message !* ANY
-}
-
-# Check that access attributes by tag works first
-if ("%{Tunnel-Server-Endpoint:2}" != '192.0.3.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if ("%{Tunnel-Server-Endpoint:2[0]}" != '192.0.3.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if ("%{Tunnel-Server-Endpoint:2[1]}" != '192.0.3.2') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint:0[2]}" != '') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint:0[0]}" != '192.0.1.1') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint:0[1]}" != '192.0.1.2') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint:0[2]}" != '') {
-       test_fail
-}
-
-#
-# Selecting on attributes with no tag specified (should match all of that type)
-#
-if ("%{Tunnel-Server-Endpoint[0]}" != '192.0.1.1') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint[1]}" != '192.0.1.2') {
-       test_fail
-}
-
-if ("%{Tunnel-Server-Endpoint[2]}" != '192.0.2.1') {
-       test_fail
-}
-
-#
-# Assignment (xlat)
-#
-update request {
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[0]}"       #0
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[0]}"       #1
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:1[0]}"       #2
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[0]}"       #3
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[1]}"       #4
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[0]}"       #5
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[1]}"       #6
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[2]}"       #7 - Empty
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint[0]}"         #8
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint[1]}"         #9
-       &Tmp-String-0 += "%{Tunnel-Server-Endpoint[2]}"         #10
-}
-
-# Check that access attributes by tag works first
-if (&Tmp-String-0[0] != '192.0.3.1') {
-       test_fail
-}
-
-if (&Tmp-String-0[1] == '192.0.3.2') {
-       test_fail
-}
-
-if (&Tmp-String-0[2] != '192.0.2.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tmp-String-0[3] != '192.0.3.1') {
-       test_fail
-}
-
-# Get the first instance of Tunnel-Server-Endpoint:2
-if (&Tmp-String-0[4] != '192.0.3.2') {
-       test_fail
-}
-
-# Now check the assignment
-if (&Tmp-String-0[5] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tmp-String-0[6] != '192.0.1.2') {
-       test_fail
-}
-
-if (&Tmp-String-0[7] != '') {
-       test_fail
-}
-
-if (&Tmp-String-0[8] != '192.0.1.1') {
-       test_fail
-}
-
-if (&Tmp-String-0[9] != '192.0.1.2') {
-       test_fail
-}
-
-if (&Tmp-String-0[10] != '192.0.2.1') {
-       test_fail
-}
-
-success
index c7dc6d7570084bda490a6978cb22b28df44739bd..b9d97ae5029cd944557467aa778e58345bd4632a 100644 (file)
@@ -1,5 +1,5 @@
 #
-#  PRE: if xlat-attr-tag
+#  PRE: if
 #
 
 #
index 42c06f66b1767cb2d1a541140be8861c6430f333..8fc6e7be11b50398c595a70f3e67700f3dc1507d 100644 (file)
@@ -3,18 +3,18 @@ update control {
 }
 
 update control {   
-       &Tmp-String-1 := `/bin/echo hello 1234:%{control:Tmp-String-0} world`
+       &Tmp-String-1 := `/bin/echo hello 1234:%{control.Tmp-String-0} world`
 }
 
-if (&control:Tmp-String-1 != "hello 1234:foo world") {
+if (&control.Tmp-String-1 != "hello 1234:foo world") {
        test_fail
 }
 
 update control {   
-       &Tmp-String-1 := `/bin/echo hello %{control:Tmp-String-0}:1234 world`
+       &Tmp-String-1 := `/bin/echo hello %{control.Tmp-String-0}:1234 world`
 }
 
-if (&control:Tmp-String-1 != "hello foo:1234 world") {
+if (&control.Tmp-String-1 != "hello foo:1234 world") {
        test_fail
 }
 
index 3f2e7612d6db06effec2743367cab89f624f1604..b450b50e1c06d371bd0c7ae4450bf4d091a4a5fc 100644 (file)
@@ -27,17 +27,17 @@ if (&Class[3]) {
        test_fail
 }
 
-if ("%{explode:&control:Calling-Station-Id |}" != 0) {
+if ("%{explode:&control.Calling-Station-Id |}" != 0) {
        update reply {
                &Filter-Id += 'Fail 4'
        }
 }
 
-if ("%{explode:&control:User-Name[*] |}" != 5) {
+if ("%{explode:&control.User-Name[*] |}" != 5) {
        test_fail
 }
 
-if ("%{control:User-Name[#]}" != 5) {
+if ("%{control.User-Name[#]}" != 5) {
        test_fail
 }
 
@@ -57,7 +57,7 @@ if ("%{explode:&control:Reply-Message |}" != 0) {
        test_fail
 }
 
-if ("%{control:Reply-Message[#]}" != 1) {
+if ("%{control.Reply-Message[#]}" != 1) {
        test_fail
 }
 
index 1b25cee85b4539dc8f6be57acf2bf023da21d865..fd725c14532ff4ce97d909ed19377340ecf2643b 100644 (file)
@@ -21,8 +21,8 @@ if ("%{debug:%{Tmp-Integer-0}}" != "%{Tmp-Integer-0}") {
 }
 
 # Check for 'splosions
-"%{%{control:Cleartext-Password}:-%{control:User-Name}}"
-"%{%{control:User-Name}:-%{control:Cleartext-Password}}"
+"%{%{control.Cleartext-Password}:-%{control.User-Name}}"
+"%{%{control.User-Name}:-%{control.Cleartext-Password}}"
 
 if (&control:Cleartext-Password =~ /(h)(e)(l)(l)(o)/) {
        "%{0}"
index c7d03cfb2cebf296c46a0bc952ac63a384c7d002..b0b2d55a8669b41b916afc58027399099b9b959d 100644 (file)
@@ -10,31 +10,31 @@ update control {
        &Tmp-IP-Address-0 += 192.0.2.2
 }
 
-if ("%{control:[#]}" != 2) {
+if ("%{control[#]}" != 2) {
        test_fail
 }
 
 debug_control
 
-if (("%{control:[0]}" != 192.0.2.1) || ("%{control:[1]}" != 192.0.2.2)) {
+if (("%{control[0]}" != 192.0.2.1) || ("%{control[1]}" != 192.0.2.2)) {
        test_fail
 }
 
-if (("%{control:[n]}" != 192.0.2.2)) {
+if (("%{control[n]}" != 192.0.2.2)) {
        test_fail
 }
 
-if ("%{control:[*]}" != '192.0.2.1,192.0.2.2') {
+if ("%{control[*]}" != '192.0.2.1,192.0.2.2') {
        test_fail
 }
 
 # Try calling these xlats in mapping too, they may get optimised to VPTs which is a
 # different code path.
 update request {
-       &Tmp-IP-Address-1 += "%{control:[1]}"
-       &Tmp-IP-Address-1 += "%{control:[0]}"
-       &Tmp-String-0 = "%{concat:,%{control:[*]}}"
-       &Tmp-Integer-0 = "%{control:[#]}"
+       &Tmp-IP-Address-1 += "%{control[1]}"
+       &Tmp-IP-Address-1 += "%{control[0]}"
+       &Tmp-String-0 = "%{concat:,%{control[*]}}"
+       &Tmp-Integer-0 = "%{control[#]}"
 }
 
 if (&Tmp-String-0 != '192.0.2.1,192.0.2.2') {
index 259e03c01dcb058393cbca5d894e91d930b17623..1e781e5181089415f5dcf3dfc16f3caa808480b8 100644 (file)
@@ -20,7 +20,7 @@ if ("%{Packet-Type}" != 'Access-Request') {
 }
 
 # Response hasn't been set yet
-if ("%{reply:Packet-Type}" != '') {
+if ("%{reply.Packet-Type}" != '') {
        test_fail
 }
 
index 253501fcf7a4b38a468ad99c41c85dcf67d5a1da..bbe8c931a7d36e72c5dbc500ce52501d9e46f26a 100644 (file)
@@ -8,11 +8,7 @@ update {
        &reply:Reply-Message := 'hello'
        &reply:Reply-Message += 'goodbye'
 
-       &reply:Tmp-String-Tagged-0:1 := 'tagged1'
-       &reply:Tmp-String-Tagged-0:2 := 'tagged2'
-
        # Request attributes
-       &Tmp-String-Tagged-0:1 := 'tagged1'
        &Tmp-Integer-0 += 10
        &Tmp-Integer-0 += 20
        &Tmp-Integer-0 += 30
@@ -43,7 +39,7 @@ else {
 }
 
 # session-state should now contain all the reply attributes
-if ("%{session-state:[#]}" == 4) {
+if ("%{session-state[#]}" == 2) {
        test_pass
 }
 else {
@@ -64,20 +60,6 @@ else {
        test_fail
 }
 
-if (&session-state:Tmp-String-Tagged-0:1 == 'tagged1') {
-       test_pass
-}
-else {
-       test_fail
-}
-
-if (&session-state:Tmp-String-Tagged-0:2 == 'tagged2') {
-       test_pass
-}
-else {
-       test_fail
-}
-
 # Tmp-String-1 should hold the result of the exec
 if (&Tmp-String-1 == 'echo test') {
        test_pass
@@ -117,21 +99,6 @@ else {
        test_fail
 }
 
-# Test some tag copying
-if (&Tmp-String-Tagged-0:10 == 'foo') {
-       test_pass
-}
-else {
-       test_fail
-}
-
-if (&Tmp-String-Tagged-0:11 == 'tagged1') {
-       test_pass
-}
-else {
-       test_fail
-}
-
 # Clear out the reply list
 update {
     &reply: !* ANY
index 3da68b8898011112a20364491c9efb1963b132c7..c0501252c1d280be0d93bb214ad8588738ea18f8 100644 (file)
@@ -43,12 +43,6 @@ cache cache_update {
 
                &Tmp-String-2[1] := 'rab'
 
-               # Test tagged literal
-               &Tmp-String-Tagged-0:10 := 'foo'
-
-               # Test tagged attr ref
-               &Tmp-String-Tagged-0:11 := &Tmp-String-Tagged-0:1
-
                # Create three string values, then remove one
                &Tmp-String-3 += 'foo'
                &Tmp-String-3 += 'bar'
index f60fdb14ae0e783d2ac7369eccd7405d114cf0a3..91581b9190482fb0ca6b1490b7fa68d0b77e5da5 100644 (file)
@@ -4,7 +4,7 @@ update request {
 }
 
 update request {
-       &CHAP-Password := "%{chap_password:%{control:Cleartext-Password}}"
+       &CHAP-Password := "%{chap_password:%{control.Cleartext-Password}}"
 }
 
 chap.authenticate
index 72754653520323879b0ebb15c7b6552b04a8061d..2e6b26d7aec1d196a74a1e41dc4ef0df6603ad8f 100644 (file)
@@ -222,7 +222,7 @@ ldap {
                #  Filter to find group objects a user is a member of.
                #  That is, group objects with attributes that
                #  identify members (the inverse of membership_attribute).
-               membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+               membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
 
                #  The attribute in user objects which contain the names
                #  or DNs of groups a user is a member of.
index fabf5f11d42696047a87854a75a442163e8d5892..ca0db6de6d98a40d62cd3b53323327c5c6d9b525 100644 (file)
@@ -8,7 +8,7 @@ linelog linelog_fmt_and_ref {
 
        format = "%{User-Name} default"
 
-       reference = ".messages.%{control:Tmp-String-0}"
+       reference = ".messages.%{control.Tmp-String-0}"
 
        messages {
                test_exec       = `/bin/echo "%{User-Name} exec"`
@@ -27,7 +27,7 @@ linelog linelog_ref {
                filename = $ENV{MODULE_TEST_DIR}/test_b.log
        }
 
-       reference = ".messages.%{control:Tmp-String-0}"
+       reference = ".messages.%{control.Tmp-String-0}"
 
        messages {
                test_exec       = `/bin/echo "%{User-Name} exec"`
@@ -76,10 +76,10 @@ linelog linelog_escapes {
                filename = $ENV{MODULE_TEST_DIR}/test_escapes.log
        }
 
-       reference = ".messages.%{control:Tmp-String-0}"
+       reference = ".messages.%{control.Tmp-String-0}"
 
        messages {
-               test_attr       = &control:Tmp-String-1
+               test_attr       = &control.Tmp-String-1
                test_xlat       = "%{User-Name}\n\tbar"
                test_literal    = 'foo\nbar'
        }
@@ -108,11 +108,11 @@ linelog linelog_ref_multi {
 
        delimiter = ", "
 
-       reference = ".messages.%{control:Tmp-String-0}"
+       reference = ".messages.%{control.Tmp-String-0}"
 
        messages {
-               test_multi_str = &control:Reply-Message[*]
-               test_multi_octets = &control:Tmp-Octets-0[*]
-               test_empty = &control:User-Name[*]
+               test_multi_str = &control.Reply-Message[*]
+               test_multi_octets = &control.Tmp-Octets-0[*]
+               test_empty = &control.User-Name[*]
        }
 }
index af4a2db1278b49afc7384daea6230e3d11e70c89..8ee3682990ea81edcea64ed319f23b52af9973ed 100644 (file)
@@ -17,21 +17,21 @@ update control {
 }
 
 #  Hashes to Redis cluster node master 1 (1)
-if ("%{redis:SET b '%{control:Tmp-String-0}'}" == 'OK') {
+if ("%{redis:SET b '%{control.Tmp-String-0}'}" == 'OK') {
        test_pass
 } else {
        test_fail
 }
 
 #  Hashes to Redis cluster node master 3 (2)
-if ("%{redis:SET c '%{control:Tmp-String-1}'}" == 'OK') {
+if ("%{redis:SET c '%{control.Tmp-String-1}'}" == 'OK') {
        test_pass
 } else {
        test_fail
 }
 
 #  Hashes to Redis cluster node master 2 (3)
-if ("%{redis:SET d '%{control:Tmp-String-2}'}" == 'OK') {
+if ("%{redis:SET d '%{control.Tmp-String-2}'}" == 'OK') {
        test_pass
 } else {
        test_fail
@@ -40,19 +40,19 @@ if ("%{redis:SET d '%{control:Tmp-String-2}'}" == 'OK') {
 #
 #  Now check they are where we expect
 #
-if ("%{redis:@%{redis_node:b 0} GET b}" == "%{control:Tmp-String-0}") {
+if ("%{redis:@%{redis_node:b 0} GET b}" == "%{control.Tmp-String-0}") {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:@%{redis_node:c 0} GET c}" == "%{control:Tmp-String-1}") {
+if ("%{redis:@%{redis_node:c 0} GET c}" == "%{control.Tmp-String-1}") {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:@%{redis_node:d 0} GET d}" == "%{control:Tmp-String-2}") {
+if ("%{redis:@%{redis_node:d 0} GET d}" == "%{control.Tmp-String-2}") {
        test_pass
 } else {
        test_fail
index d00be31686a7e53f68dc1f0fd480f127cb85c6b7..50dc148715ee7a7ae85031467054bf6153cb851d 100644 (file)
@@ -34,10 +34,10 @@ if ("$ENV{REDIS_CLUSTER_CONTROL}" == '') {
 #  Reset the cluster
 #
 update control {
-       &Tmp-String-0 = `%{control:Tmp-String-8} stop`
-       &Tmp-String-0 = `%{control:Tmp-String-8} clean`
-       &Tmp-String-0 = `%{control:Tmp-String-8} start`
-       &Tmp-String-0 = `%{control:Tmp-String-8} create`
+       &Tmp-String-0 = `%{control.Tmp-String-8} stop`
+       &Tmp-String-0 = `%{control.Tmp-String-8} clean`
+       &Tmp-String-0 = `%{control.Tmp-String-8} start`
+       &Tmp-String-0 = `%{control.Tmp-String-8} create`
 }
 
 #
@@ -65,17 +65,17 @@ foreach &control:Tmp-Integer-0 {
        #
        if ("%{redis_remap:%{Tmp-String-0}:30001}" == 'success') {
                #  Hashes to Redis cluster node master 0 (1)
-               if (("%{redis:SET b '%{control:Tmp-String-0}'}" == 'OK') && \
-                   ("%{redis:SET c '%{control:Tmp-String-1}'}" == 'OK') && \
-                   ("%{redis:SET d '%{control:Tmp-String-2}'}" == 'OK')) {
+               if (("%{redis:SET b '%{control.Tmp-String-0}'}" == 'OK') && \
+                   ("%{redis:SET c '%{control.Tmp-String-1}'}" == 'OK') && \
+                   ("%{redis:SET d '%{control.Tmp-String-2}'}" == 'OK')) {
                        #
                        #  The actual node to keyslot mapping seems to be somewhat random
                        #  so we now need to figure out which slave each of those keys
                        #  ended up on.
                        #
-                       if (("%{redis:-@%{redis_node:b 1} GET b}" == "%{control:Tmp-String-0}") && \
-                           ("%{redis:-@%{redis_node:c 1} GET c}" == "%{control:Tmp-String-1}") && \
-                           ("%{redis:-@%{redis_node:d 1} GET d}" == "%{control:Tmp-String-2}")) {
+                       if (("%{redis:-@%{redis_node:b 1} GET b}" == "%{control.Tmp-String-0}") && \
+                           ("%{redis:-@%{redis_node:c 1} GET c}" == "%{control.Tmp-String-1}") && \
+                           ("%{redis:-@%{redis_node:d 1} GET d}" == "%{control.Tmp-String-2}")) {
                                break
                        }
                }
index fc3444e2c94d8fe8c492cd156e35d1ec2d8043e7..8032bb16a7209ca5c399a042901e1cd9f3a29406 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
@@ -37,13 +37,13 @@ update request {
        &Tmp-Date-0 := "%l"
 }
 
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 20) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 20) {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 40) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 40) {
        test_pass
 } else {
        test_fail
@@ -52,19 +52,19 @@ if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Add
 #
 #  Verify the IP hash has been set
 #
-if ("%{redis:HGET '{%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
+if ("%{redis:HGET '{%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}" == '127.0.0.1') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} gateway}" == '127.0.0.1') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} range}" == '192.168.0.0') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} range}" == '192.168.0.0') {
        test_pass
 } else {
        test_fail
@@ -79,7 +79,7 @@ if (&reply:Pool-Range == '192.168.0.0') {
 #
 #  Verify the lease has been associated with the device
 #
-if (&reply:Framed-IP-Address == "%{redis:GET '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}") {
+if (&reply:Framed-IP-Address == "%{redis:GET '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}") {
        test_pass
 } else {
        test_fail
@@ -105,7 +105,7 @@ update {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.1.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.1.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.1.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.1.0`
 }
 
 #
index 651bd217b1914c37add3ea6ea4a55c337a71b695..52547591f918a9feb01ef9a87ded35993aeb3fdb 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
@@ -36,13 +36,13 @@ if (&reply:Framed-IP-Address == 192.168.0.1) {
 #  Delete the IP address
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -d 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -d 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
 #  Verify the association with the device has been removed
 #
-if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
+if ("%{redis:EXISTS '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
        test_pass
 } else {
        test_fail
@@ -51,14 +51,14 @@ if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" ==
 #
 #  Verify the hash information is removed
 #
-if ("%{redis:EXISTS '{%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address}'}" == '0') {
+if ("%{redis:EXISTS '{%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address}'}" == '0') {
        test_pass
 } else {
        test_fail
 }
 
 # Check the ZSCORE
-if ("%{redis:ZCOUNT '{%{control:Pool-Name}%}:pool' -inf +inf}" == 0) {
+if ("%{redis:ZCOUNT '{%{control.Pool-Name}%}:pool' -inf +inf}" == 0) {
        test_pass
 } else {
        test_fail
index ffafa739a7b6e1d5bfeb659be62e1ea73a11d817..ecd533883b9b6a059ca8af3e07e9efd87cf03dfd 100644 (file)
@@ -11,13 +11,13 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
 #  Verify the range was set OK
 #
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:192.168.0.1 range}" == '192.168.0.0') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:192.168.0.1 range}" == '192.168.0.0') {
        test_pass
 } else {
        test_fail
@@ -27,13 +27,13 @@ if ("%{redis:HGET {%{control:Pool-Name}%}:ip:192.168.0.1 range}" == '192.168.0.0
 #  Modify the range
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -m 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 10.0.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -m 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 10.0.0.0`
 }
 
 #
 #  Check it was updated
 #
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:192.168.0.1 range}" == '10.0.0.0') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:192.168.0.1 range}" == '10.0.0.0') {
        test_pass
 } else {
        test_fail
index e2b6731e359052feff8b7fefbaabc3cd4af959ee..8ee61dc888d28e0da113ea5f133f7a297a632e69 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
@@ -36,13 +36,13 @@ if (&reply:Framed-IP-Address == 192.168.0.1) {
 #  Release the IP address
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -r 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -r 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
 #  Verify the association with the device has been removed
 #
-if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
+if ("%{redis:EXISTS '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
        test_pass
 } else {
        test_fail
@@ -51,26 +51,26 @@ if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" ==
 #
 #  Verify the hash information is retained
 #
-if ("%{redis:HGET '{%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
+if ("%{redis:HGET '{%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}" == '127.0.0.1') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} gateway}" == '127.0.0.1') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} range}" == '192.168.0.0') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} range}" == '192.168.0.0') {
        test_pass
 } else {
        test_fail
 }
 
 # Check the ZSCORE
-if ("%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'}" == 0) {
+if ("%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'}" == 0) {
        test_pass
 } else {
        test_fail
index 1c421da3af071dda341531fdbb9dda758cc8e62d..069a309cbb648b71a065c72630522b3a0cc9427f 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 #
@@ -51,7 +51,7 @@ if (updated) {
 #
 #  Verify the association with the device has been removed
 #
-if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
+if ("%{redis:EXISTS '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}" == '0') {
        test_pass
 } else {
        test_fail
@@ -60,19 +60,19 @@ if ("%{redis:EXISTS '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" ==
 #
 #  Verify the hash information is retained
 #
-if ("%{redis:HGET '{%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
+if ("%{redis:HGET '{%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address}' 'device'}" == '00:11:22:33:44:55') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}" == '127.0.0.1') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} gateway}" == '127.0.0.1') {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} range}" == '192.168.0.0') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} range}" == '192.168.0.0') {
        test_pass
 } else {
        test_fail
@@ -83,13 +83,13 @@ update request {
        &Tmp-Date-0 := "%l"
 }
 
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 0) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 0) {
        test_pass
 } else {
        test_fail
 }
 
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 10) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 10) {
        test_pass
 } else {
        test_fail
index 93c30edf01d33e1aabe729ae5ab0941a353948c4..c3b9f79fcc58a51213e7aa2418a25c1110c515fc 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 # 1. Check allocation
@@ -37,7 +37,7 @@ if (&reply:Session-Timeout == 30) {
 }
 
 # 4. Verify the gateway was set
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}" == '127.0.0.1') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} gateway}" == '127.0.0.1') {
        test_pass
 } else {
        test_fail
@@ -45,7 +45,7 @@ if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}
 
 # 5. Add another IP addresses
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.1.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.1.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.1.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.1.0`
 }
 
 # 6. Verify that the lease time is extended
@@ -73,35 +73,35 @@ update request {
        &Tmp-Date-0 := "%l"
 }
 
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 50) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" > 50) {
        test_pass
 } else {
        test_fail
 }
 
 # 9.
-if ("%{expr:%{redis:ZSCORE '{%{control:Pool-Name}%}:pool' '%{reply:Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 70) {
+if ("%{expr:%{redis:ZSCORE '{%{control.Pool-Name}%}:pool' '%{reply.Framed-IP-Address}'} - %{integer:&Tmp-Date-0}}" < 70) {
        test_pass
 } else {
        test_fail
 }
 
 # 10. Verify the lease is still associated with the device
-if (&reply:Framed-IP-Address == "%{redis:GET '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}") {
+if (&reply:Framed-IP-Address == "%{redis:GET '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}") {
        test_pass
 } else {
        test_fail
 }
 
 # 11. And that the device object will expire a suitable number of seconds into the future
-if ("%{redis:TTL '{%{control:Pool-Name}%}:device:%{Calling-Station-ID}'}" == 60) {
+if ("%{redis:TTL '{%{control.Pool-Name}%}:device:%{Calling-Station-ID}'}" == 60) {
        test_pass
 } else {
        test_fail
 }
 
 # 12. Verify the gateway was updated
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{request:Framed-IP-Address} gateway}" == '127.0.0.2') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{request.Framed-IP-Address} gateway}" == '127.0.0.2') {
        test_pass
 } else {
        test_fail
@@ -145,7 +145,7 @@ if (invalid) {
 }
 
 # 16. Verify the lease is still associated with the previous device
-if (&reply:Framed-IP-Address == "%{redis:GET '{%{control:Pool-Name}%}:device:00:11:22:33:44:55'}") {
+if (&reply:Framed-IP-Address == "%{redis:GET '{%{control.Pool-Name}%}:device:00:11:22:33:44:55'}") {
        test_pass
 } else {
        test_fail
index efb0e6ffee2c39d4e5531cfd1805a3fe28291847..4aed0045ac87ab91ca48ec4f5330580bb8fb70d3 100644 (file)
@@ -11,7 +11,7 @@ update control {
 #  Add IP addresses
 #
 update request {
-       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control:Pool-Name} 192.168.0.0`
+       &Tmp-String-0 := `./build/bin/local/rlm_redis_ippool_tool -a 192.168.0.1/32 $ENV{REDIS_IPPOOL_TEST_SERVER}:30001 %{control.Pool-Name} 192.168.0.0`
 }
 
 # 1. Check allocation
@@ -40,7 +40,7 @@ if (&reply:Session-Timeout == 60) {
 }
 
 # 4. Verify the gateway was set
-if ("%{redis:HGET {%{control:Pool-Name}%}:ip:%{reply:Framed-IP-Address} gateway}" == '127.0.0.1') {
+if ("%{redis:HGET {%{control.Pool-Name}%}:ip:%{reply.Framed-IP-Address} gateway}" == '127.0.0.1') {
        test_pass
 } else {
        test_fail
index d6169018ddc4166b7abcbc78a702952f1a062d01..e112cba489000d6ba78a5ed0e92e6bb75f887c76 100644 (file)
@@ -4,9 +4,9 @@
 
 # Module should return NOOP if there's no result set to work with
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   := 'username'
-       &control:Tmp-String-1   := 'groupname'
-       &control:Tmp-Integer-0  := 'priority'
+       &control.Tmp-String-0   := 'username'
+       &control.Tmp-String-1   := 'groupname'
+       &control.Tmp-Integer-0  := 'priority'
 }
 if (noop) {
        test_pass
@@ -15,21 +15,21 @@ else {
        test_fail
 }
 
-if (!&control:Tmp-String-0) {
+if (!&control.Tmp-String-0) {
        test_pass
 }
 else {
        test_fail
 }
 
-if (!&control:Tmp-String-1) {
+if (!&control.Tmp-String-1) {
        test_pass
 }
 else {
        test_fail
 }
 
-if (!&control:Tmp-Integer-0) {
+if (!&control.Tmp-Integer-0) {
        test_pass
 }
 else {
@@ -41,9 +41,9 @@ else {
 
 # Retrieve our test row
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   := 'username'
-       &control:Tmp-String-1   := 'groupname'
-       &control:Tmp-Integer-0  := 'priority'
+       &control.Tmp-String-0   := 'username'
+       &control.Tmp-String-1   := 'groupname'
+       &control.Tmp-Integer-0  := 'priority'
 }
 if (updated) {
        test_pass
@@ -52,21 +52,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'bob') {
+if (&control.Tmp-String-0 == 'bob') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'bar') {
+if (&control.Tmp-String-1 == 'bar') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
@@ -75,9 +75,9 @@ else {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 # Insert our second test row
@@ -85,9 +85,9 @@ update {
 
 # Retrieve our test row(s) - With := we should get the values from the second row
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   := 'username'
-       &control:Tmp-String-1   := 'groupname'
-       &control:Tmp-Integer-0  := 'priority'
+       &control.Tmp-String-0   := 'username'
+       &control.Tmp-String-1   := 'groupname'
+       &control.Tmp-Integer-0  := 'priority'
 }
 
 if (updated) {
@@ -97,21 +97,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'oof') {
+if (&control.Tmp-String-0 == 'oof') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'rab') {
+if (&control.Tmp-String-1 == 'rab') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
@@ -120,16 +120,16 @@ else {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 # Retrieve our test row(s) - With = we should get the values from the first row
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   = 'username'
-       &control:Tmp-String-1   = 'groupname'
-       &control:Tmp-Integer-0  = 'priority'
+       &control.Tmp-String-0   = 'username'
+       &control.Tmp-String-1   = 'groupname'
+       &control.Tmp-Integer-0  = 'priority'
 }
 
 if (updated) {
@@ -139,21 +139,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'bob') {
+if (&control.Tmp-String-0 == 'bob') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'bar') {
+if (&control.Tmp-String-1 == 'bar') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
@@ -162,54 +162,54 @@ else {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 # Retrieve our test row(s) - With += we should get the values from both rows
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   += 'username'
-       &control:Tmp-String-1   += 'groupname'
-       &control:Tmp-Integer-0  += 'priority'
+       &control.Tmp-String-0   += 'username'
+       &control.Tmp-String-1   += 'groupname'
+       &control.Tmp-Integer-0  += 'priority'
 }
 
-if ("%{control:Tmp-String-0[#]}" == 2) {
+if ("%{control.Tmp-String-0[#]}" == 2) {
        test_pass
 }
 else {
        test_fail
 }
 
-if ("%{control:Tmp-String-1[#]}" == 2) {
+if ("%{control.Tmp-String-1[#]}" == 2) {
        test_pass
 }
 else {
        test_fail
 }
 
-if ("%{control:Tmp-Integer-0[#]}" == 2) {
+if ("%{control.Tmp-Integer-0[#]}" == 2) {
        test_pass
 }
 else {
        test_fail
 }
 
-if ((&control:Tmp-String-0[0] == 'bob') && (&control:Tmp-String-0[1] == 'oof')) {
+if ((&control.Tmp-String-0[0] == 'bob') && (&control.Tmp-String-0[1] == 'oof')) {
        test_pass
 }
 else {
        test_fail
 }
 
-if ((&control:Tmp-String-1[0] == 'bar') && (&control:Tmp-String-1[1] == 'rab')) {
+if ((&control.Tmp-String-1[0] == 'bar') && (&control.Tmp-String-1[1] == 'rab')) {
        test_pass
 }
 else {
        test_fail
 }
 
-if ((&control:Tmp-Integer-0[0] == 0) && (&control:Tmp-Integer-0[1] == 0)) {
+if ((&control.Tmp-Integer-0[0] == 0) && (&control.Tmp-Integer-0[1] == 0)) {
        test_pass
 }
 else {
@@ -218,15 +218,15 @@ else {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 # Retrieve the username column only (should result in noop because not in map)
 map sql 'SELECT username FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-1   += 'groupname'
-       &control:Tmp-Integer-0  += 'priority'
+       &control.Tmp-String-1   += 'groupname'
+       &control.Tmp-Integer-0  += 'priority'
 }
 
 if (noop) {
@@ -238,9 +238,9 @@ else {
 
 # Retrieve test row with specific username (using xlat'd query)
 map sql "SELECT * FROM radusergroup WHERE priority = 0 AND username = '%{User-Name}'" {
-       &control:Tmp-String-0   = 'username'
-       &control:Tmp-String-1   = 'groupname'
-       &control:Tmp-Integer-0  = 'priority'
+       &control.Tmp-String-0   = 'username'
+       &control.Tmp-String-1   = 'groupname'
+       &control.Tmp-Integer-0  = 'priority'
 }
 
 if (updated) {
@@ -250,21 +250,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'bob') {
+if (&control.Tmp-String-0 == 'bob') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'bar') {
+if (&control.Tmp-String-1 == 'bar') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
@@ -277,16 +277,16 @@ update request {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 # Retrieve test row with specific username (using ref'd query)
 map sql &Tmp-String-0 {
-       &control:Tmp-String-0   = 'username'
-       &control:Tmp-String-1   = 'groupname'
-       &control:Tmp-Integer-0  = 'priority'
+       &control.Tmp-String-0   = 'username'
+       &control.Tmp-String-1   = 'groupname'
+       &control.Tmp-Integer-0  = 'priority'
 }
 
 if (updated) {
@@ -296,21 +296,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'bob') {
+if (&control.Tmp-String-0 == 'bob') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'bar') {
+if (&control.Tmp-String-1 == 'bar') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
@@ -319,9 +319,9 @@ else {
 
 # Clear the control list
 update {
-       &control:Tmp-String-0 !* ANY
-       &control:Tmp-String-1 !* ANY
-       &control:Tmp-Integer-0 !* ANY
+       &control.Tmp-String-0 !* ANY
+       &control.Tmp-String-1 !* ANY
+       &control.Tmp-Integer-0 !* ANY
 }
 
 update request {
@@ -331,9 +331,9 @@ update request {
 
 # Retrieve our test row(s) - Using different RHS types
 map sql 'SELECT * FROM radusergroup WHERE priority = 0' {
-       &control:Tmp-String-0   = &Tmp-String-0
-       &control:Tmp-String-1   = "group%{Tmp-String-1}"
-       &control:Tmp-Integer-0  = `/bin/echo priority`
+       &control.Tmp-String-0   = &Tmp-String-0
+       &control.Tmp-String-1   = "group%{Tmp-String-1}"
+       &control.Tmp-Integer-0  = `/bin/echo priority`
 }
 
 if (updated) {
@@ -343,21 +343,21 @@ else {
        test_fail
 }
 
-if (&control:Tmp-String-0 == 'bob') {
+if (&control.Tmp-String-0 == 'bob') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-String-1 == 'bar') {
+if (&control.Tmp-String-1 == 'bar') {
        test_pass
 }
 else {
        test_fail
 }
 
-if (&control:Tmp-Integer-0 == 0) {
+if (&control.Tmp-Integer-0 == 0) {
        test_pass
 }
 else {
index 008da2a9b6902afb5e0ac2c97341f457a2a59866..fd7635bc00fe7aa1c76f91f589ccedb7855fa506 100644 (file)
@@ -117,8 +117,8 @@ server test {
                        }
 
                        update reply {
-                               &raw.Attr-26 := &reply:Class
-                               &raw.Attr-26 += 0x483d342c493d43
+                               &raw.26 := &reply:Class
+                               &raw.26 += 0x483d342c493d43
                        }
                }
 
index 16b9a849cd43b13481b631389f9c4e727e9db790..a988760c749fd41285355a701762814240305844 100644 (file)
@@ -16,33 +16,30 @@ match &User-Name == &reply:User-Name
 
 # All IP address literals should be parsed as prefixes
 condition ("foo\
-match ERROR offset 6 End of string after escape
+match ERROR offset 2 Unterminated string
 
 condition ("foo
 match ERROR offset 2 Unterminated string
 
 condition ()
-
-match ERROR offset 1 Empty string is invalid
+match ERROR offset 1 Empty bareword is invalid
 
 condition (!)
-match ERROR offset 2 Empty string is invalid
+match ERROR offset 2 Empty bareword is invalid
 
-condition (foo == bar
-match ERROR offset 11 No closing brace at end of string
 
 condition (|| b)
-match ERROR offset 1 Empty string is invalid
+match ERROR offset 1 Empty bareword is invalid
 
 condition ((ok || handled) foo)
 match ERROR offset 17 Unexpected text after condition
 
 # escapes in names are illegal
 condition (ok\ foo || handled)
-match ERROR offset 3 Unexpected escape
+match ERROR offset 5 Invalid operator
 
 condition (ok FOO handled)
-match ERROR offset 4 Invalid text. Expected comparison operator
+match ERROR offset 4 Invalid operator
 
 condition (ok !x handled)
 match ERROR offset 4 Invalid operator
@@ -50,8 +47,11 @@ match ERROR offset 4 Invalid operator
 condition (ok =x handled)
 match ERROR offset 4 Invalid operator
 
-condition (ok == handled"foo")
-match ERROR offset 14 Unexpected start of string
+#
+# Re-enable when we have proper bareword xlat tokenization
+#
+#condition (ok == handled"foo")
+#match ERROR offset 14 Unexpected text after condition
 
 # And now we have a bunch of VALID conditions we want to parse.
 
@@ -145,7 +145,7 @@ condition <ipaddr>&Filter-Id == <integer>&Framed-IP-Address
 match ERROR offset 22 Unexpected cast
 
 condition <ipaddr>&Filter-Id == <blerg>&Framed-IP-Address
-match ERROR offset 22 Unexpected cast
+match ERROR offset 23 Unknown data type
 
 condition <blerg>&Filter-Id == "foo"
 match ERROR offset 1 Unknown data type
@@ -153,9 +153,6 @@ match ERROR offset 1 Unknown data type
 #
 #  Normalize things
 #
-condition <ipaddr>&Filter-Id == "127.0.0.1"
-match <ipaddr>&Filter-Id == '127.0.0.1'
-
 condition <ipaddr>127.0.0.1 < &Framed-IP-Address
 match &Framed-IP-Address > 127.0.0.1
 
@@ -207,8 +204,9 @@ match ERROR offset 14 Comparison value must be a quoted string
 condition &Session-Timeout == 10
 match &Session-Timeout == 10
 
+# Automatic type inference means this is fine
 condition &Session-Timeout == '10'
-match ERROR offset 21 Comparison value must be an unquoted string
+match &Session-Timeout == 10
 
 # Except for dates, which can be humanly readable!
 # This one is be an expansion, so it's left as-is.
@@ -233,26 +231,24 @@ match ERROR offset 13 Failed to parse value for attribute
 condition <ipaddr>127.0.0.1 == "127.0.0.1"
 match true
 
-condition <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
-match <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
+condition <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
+match <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
 
 #
 #  Bare %{...} is allowed.
 #
-condition <ipaddr>127.0.0.1 == %{sql:127.0.0.1}
-match <ipaddr>127.0.0.1 == "%{sql:127.0.0.1}"
+condition <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
+match <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
 
-condition <ipaddr>127.0.0.1 == %{sql: SELECT user FROM table WHERE user='%{User-Name}'}
-match <ipaddr>127.0.0.1 == "%{sql: SELECT user FROM table WHERE user='%{User-Name}'}"
+condition <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
+match <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
 
 condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
 match true
 
-condition <ether> 00:11:22:33:44:55 == "ff:11:22:33:44:55"
-match false
 
-condition <ether> 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
-match <ether>00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
+condition <ether> 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
+match <ether>00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
 
 condition <ether> 00:XX:22:33:44:55 == 00:11:22:33:44:55
 match ERROR offset 8 Failed to parse field
@@ -305,11 +301,11 @@ match true
 condition ('foo' == 'foo')
 match true
 
-condition ("foo" == "%{sql: foo}")
-match "foo" == "%{sql: foo}"
+condition ("foo" == "%{md4: foo}")
+match "foo" == "%{md4: foo}"
 
-condition ("foo bar" == "%{sql: foo}")
-match "foo bar" == "%{sql: foo}"
+condition ("foo bar" == "%{md4: foo}")
+match "foo bar" == "%{md4: foo}"
 
 condition ("foo" == "bar")
 match false
@@ -325,8 +321,8 @@ match false
 condition (&User-Name == "bob")
 match &User-Name == "bob"
 
-condition (&User-Name == "%{sql: blah}")
-match &User-Name == "%{sql: blah}"
+condition (&User-Name == "%{md4: blah}")
+match &User-Name == "%{md4: blah}"
 
 condition <ipaddr>127.0.0.1 == 2130706433
 match true
@@ -412,14 +408,15 @@ match ERROR offset 2 Forbidden data type in cast
 condition <string>"foo" == &User-Name
 match &User-Name == "foo"
 
-condition <integer>"%{expr: 1 + 1}" < &NAS-Port
-match &NAS-Port > "%{expr: 1 + 1}"
+# This used to be expr, but expr isn't a builtin, so it failed...
+condition <integer>"%{md4: 1 + 1}" < &NAS-Port
+match &NAS-Port > "%{md4: 1 + 1}"
 
 condition &Filter-Id == &Framed-IP-Address
-match ERROR offset 0 Attribute comparisons must be of the same data type
+match ERROR offset 0 Cannot compare attributes of type 'string' and 'ipaddr'
 
 condition <ipaddr>127.0.0.1 == &Filter-Id
-match ERROR offset 0 Attribute comparisons must be of the same data type
+match ERROR offset 0 Cannot compare types 'ipaddr' (cast) and 'string' (attr)
 
 condition &Tmp-Integer64-0 == &request:Foo-Stuff-Bar
 match &Tmp-Integer64-0 == &Foo-Stuff-Bar
@@ -446,21 +443,6 @@ match <ipaddr>&Filter-Id == &Framed-IP-Address
 condition <ipaddr>&Class == &Framed-IP-Address
 match <ipaddr>&Class == &Framed-IP-Address
 
-#
-#  Tags of zero mean there's no tag
-#
-condition &Tunnel-Password:0 == "Hello"
-match &Tunnel-Password == "Hello"
-
-condition &Tunnel-Password:1 == "Hello"
-match &Tunnel-Password:1 == "Hello"
-
-#
-# Single quoted strings are left as-is
-#
-condition &Tunnel-Password:1 == 'Hello'
-match &Tunnel-Password:1 == 'Hello'
-
 #
 #  zero offset into arrays get parsed and ignored
 #
@@ -473,12 +455,6 @@ match &User-Name[1] == "bob"
 condition &User-Name[n] == "bob"
 match &User-Name[n] == "bob"
 
-condition &Tunnel-Password:1[0] == "Hello"
-match &Tunnel-Password:1[0] == "Hello"
-
-condition &Tunnel-Password:1[3] == "Hello"
-match &Tunnel-Password:1[3] == "Hello"
-
 #
 #  This is allowed for pass2-fixups.  Foo-Bar MAY be an attribute.
 #  If so allow it so that pass2 can fix it up.  Until then,
@@ -504,7 +480,7 @@ match &Acct-Input-Octets-64 > "%{Session-Timeout}"
 #
 #  Parse OIDs into known attributes, where possible.
 #
-condition &Attr-26.24757.84.9.5.4 == 0x1a99
+condition &26.24757.84.9.5.4 == 0x1a99
 match &WiMAX-PFDv2-Src-Port == 6809
 
 #
@@ -512,8 +488,8 @@ match &WiMAX-PFDv2-Src-Port == 6809
 #  Allow it so that we can look for malformed attributes
 #  in packets.
 #
-condition &Attr-26.24757.84.9.5.7 == 0x1a99
-match &Attr-26.24757.84.9.5.7 == 0x1a99
+condition &raw.26.24757.84.9.5.7 == 0x1a99
+match &raw.WiMAX-PFDv2-Classifier-Source-Spec.7 == 0x1a99
 
 #  This one is really unknown
 condition &Attr-26.24757.84.9.5.15 == 0x1a99
@@ -523,49 +499,28 @@ match &Attr-26.24757.84.9.5.15 == 0x1a99
 #  Invalid array references.
 #
 condition &User-Name[a] == 'bob'
-match ERROR offset 11 Array index is not an integer
+match ERROR offset 11 Invalid array index
 
 condition &User-Name == &Filter-Id[a]
-match ERROR offset 25 Array index is not an integer
+match ERROR offset 25 Invalid array index
 
 #
-#  This one is still wrong.
+#  Attributes without an '&' prefix are
+#  just barewords.  So this is a comparison
+#  of 'User-Name[a]' == 'bob' which is
+#  false.T
 #
 condition User-Name[a] == 'bob'
-match ERROR offset 9 Invalid text. Expected comparison operator
+match false
 
 #
 #  Bounds checks...
 #
 condition &User-Name[1001] == 'bob'
-match ERROR offset 11 Invalid array reference '1001' (should be between 0-1000)
+match ERROR offset 11 Invalid array index '1001' (should be between 0-1000)
 
 condition &User-Name[-1] == 'bob'
-match ERROR offset 11 Array index is not an integer
-
-#
-#  Tags
-#
-condition &Tunnel-Private-Group-Id:10 == 'test'
-match &Tunnel-Private-Group-Id:10 == 'test'
-
-condition &User-Name:10 == 'test'
-match ERROR offset 10 Attribute 'User-Name' cannot have a tag
-
-#
-#  Tags are always wrong for attributes which aren't tagged.
-#
-condition &User-Name:0 == 'test'
-match ERROR offset 10 Attribute 'User-Name' cannot have a tag
-
-#
-#  Bounds checks...
-#
-condition &Tunnel-Private-Group-Id:32 == 'test'
-match ERROR offset 25 Invalid tag value '32' (should be between 0-31)
-
-condition &request:Tunnel-Private-Group-Id:-1 == 'test'
-match ERROR offset 33 Invalid tag value '-1' (should be between 0-31)
+match ERROR offset 11 Invalid array index '-1' (should be between 0-1000)
 
 #
 #  Sometimes the attribute/condition parser needs to fallback to bare words
@@ -573,28 +528,29 @@ match ERROR offset 33 Invalid tag value '-1' (should be between 0-31)
 condition request:Foo == 'request:Foo'
 match true
 
+# Bareword compared with bareword is true
 condition request:Foo+Bar == request:Foo+Bar
-match ERROR offset 11 Invalid text. Expected comparison operator
+match true
 
 condition &request:Foo+Bar == 'request:Foo+Bar'
-match ERROR offset 12 Invalid text. Expected comparison operator
+match ERROR offset 12 Unexpected text after attribute reference
 
 condition 'request:Foo+d' == &request:Foo+Bar
-match ERROR offset 31 Unexpected text after condition
+match ERROR offset 31 Unexpected text after attribute reference
 
 #  Attribute tags are not allowed for unknown attributes
 condition &request:FooBar:0 == &request:FooBar
-match ERROR offset 15 Unexpected text after attribute name
+match ERROR offset 15 Unexpected text after attribute reference
 
 condition &not-a-list:User-Name == &not-a-list:User-Name
-match ERROR offset 1 Invalid list qualifier
+match ERROR offset 11 Unexpected text after attribute reference
 
 # . is a valid dictionary name attribute, so we can't error out in pass1
 condition &not-a-packet.User-Name == &not-a-packet.User-Name
 match &not-a-packet.User-Name == &not-a-packet.User-Name
 
 #
-#  The LHS is a string with ASCII 5C 30 30 30 inside of it.
+#  The LHS is a string with ASCII 5C 30 30 30 inside of it vs the RHS which should contain ASCII 0.
 #
 condition ('i have scary embedded things\000 inside me' == "i have scary embedded things\000 inside me")
 match false
@@ -602,10 +558,10 @@ match false
 #
 #  'Unknown' attributes which are defined in the main dictionary
 #  should be resolved to their real names.
-condition &Attr-1 == 0x616263
+condition &1 == 0x616263
 match &User-Name == 'abc'
 
-condition &Attr-26.11344.1 == 0x7f000001
+condition &26.11344.1 == 0x7f000001
 match &FreeRADIUS-Proxied-To == 127.0.0.1
 
 #
@@ -622,10 +578,11 @@ condition &User-Name != "foo\nbar"
 match !&User-Name == "foo\nbar"
 
 #
-#  We require explicit casts
+#  We infer that the LHS is a prefix and the RHS is
+#  and ipaddr without requiring an explicit cast.
 #
 condition 192.168.0.0/16 > 192.168.1.2
-match false
+match true
 
 condition <ipv4prefix>192.168.0.0/16 > 192.168.1.2
 match true
@@ -645,8 +602,8 @@ match <ipv4prefix>&NAS-IP-Address < &PMIP6-Home-IPv4-HoA
 condition &NAS-IP-Address < 192.168/16
 match <ipv4prefix>&NAS-IP-Address < 192.168.0.0/16
 
-condition &NAS-IP-Address < "%{echo: 192.168/16}"
-match <ipv4prefix>&NAS-IP-Address < "%{echo: 192.168/16}"
+condition &NAS-IP-Address < "%{string: 192.168/16}"
+match <ipv4prefix>&NAS-IP-Address < "%{string: 192.168/16}"
 
 condition &NAS-IP-Address < `/bin/echo 192.168/16`
 match <ipv4prefix>&NAS-IP-Address < `/bin/echo 192.168/16`
@@ -665,4 +622,4 @@ condition ("$ENV{SOMETHING_OR_OTHER}" == '')
 match true
 
 count
-match 334
+match 306
index ccf1f24171ce64ee5ab6b0b8d040fcf6c1255d60..bf75375d6d0e226e9ade24df4b50c1fd31623e34 100644 (file)
@@ -4,12 +4,6 @@
 condition "bob" == 0x626f62
 match true
 
-condition 0x == '0x'
-match ERROR offset 0 Empty octet string is invalid
-
-condition 'foo' == 0x
-match ERROR offset 9 Empty octet string is invalid
-
 # \n gets escaped in double quoted strings
 condition "\n" == 0x0a
 match true
@@ -67,11 +61,11 @@ match true
 condition  0x626f62 == bob
 match true
 
-condition  \n == 0x0a
-match ERROR offset 0 Unexpected escape
+condition  \n == 0x5c6e
+match true
 
-condition  a\n == 0x610a
-match ERROR offset 1 Unexpected escape
+condition  a\n == 0x615c6e
+match true
 
 count
-match 44
+match 40
index a69c858fbe537749ecce17da36ea3f016296c02b..0e1988acf43c7d195a08a09c7b5806e5100f08f5 100644 (file)
@@ -9,9 +9,6 @@ match ERROR offset 7 Expected regular expression
 condition (ok == /foo/)
 match ERROR offset 7 Unexpected regular expression
 
-condition /foo/ =~ bar
-match ERROR offset 0 Unexpected regular expression
-
 #
 #  Convert !~ to !(COND) for regex
 #
@@ -40,19 +37,22 @@ condition foo =~ /bar/gimsux
 match foo =~ /bar/gimsux
 
 condition foo =~ /bar/ima
-match ERROR offset 14 Unexpected text after condition
+match ERROR offset 14 Unsupported regex flag 'a'
 
 condition foo =~ /bar/ii
-match ERROR offset 13 Duplicate flag
+match ERROR offset 13 Duplicate regex flag 'i'
 
 condition foo =~ /bar/iia
-match ERROR offset 13 Duplicate flag
+match ERROR offset 13 Duplicate regex flag 'i'
 
 #
 #  Escape the backslashes correctly
 #  And print them correctly
 #
 
+condition &User-Name =~ /@|./
+match &User-Name =~ /@|./
+
 condition &User-Name =~ /@|\\/
 match &User-Name =~ /@|\\/
 
@@ -68,13 +68,5 @@ match ERROR offset 2: Invalid regex reference.  Must be in range 0-32
 condition &User-Name == /foo/
 match ERROR offset 14 Unexpected regular expression
 
-#
-#  Allow a match operator.  Which the user will
-#  hopefully *not* use with a special character
-#  like '=' or '{' or '}.  If they do, too bad.
-#
-condition &User-Name =~ m,foo,
-match &User-Name =~ /foo/
-
 count
-match 41
+match 39
index 0e86e578f472fb2f11a488873728feabda7b5627..e56da934d2ff92c406dbacab6d8bc01598a87304 100644 (file)
@@ -118,7 +118,7 @@ match Packet-Type = Access-Request, Packet-Authentication-Vector = 0xecfe3d2fe44
 #    |  value     = c6d195032fdc30240f7313b231ef1d77
 #
 decode-proto 0b 05 00 6d f0 50 64 91 84 62 5d 36 f1 4c 90 75 b7 a4 8b 83 08 06 ff ff ff fe 0c 06 00 00 02 40 06 06 00 00 00 02 12 0b 48 65 6c 6c 6f 2c 20 25 75 4f 18 01 01 00 16 04 10 26 6b 0e 9a 58 32 2f 4d 01 ab 25 b3 5f 87 94 64 50 12 11 b5 04 3c 8a 28 87 58 17 31 33 a5 e0 74 34 cf 18 12 c6 d1 95 03 2f dc 30 24 0f 73 13 b2 31 ef 1d 77
-match Packet-Type = Access-Challenge, Packet-Authentication-Vector = 0xf050649184625d36f14c9075b7a48b83, Framed-IP-Address = 255.255.255.254, Framed-MTU = 576, Service-Type = Framed-User, Reply-Message = "Hello, %u", EAP-Message = 0x010100160410266b0e9a58322f4d01ab25b35f879464, Message-Authenticator = 0x11b5043c8a288758173133a5e07434cf, State = 0xc6d195032fdc30240f7313b231ef1d77
+match Packet-Type = Access-Challenge, Packet-Authentication-Vector = 0xf050649184625d36f14c9075b7a48b83, Framed-IP-Address = 255.255.255.254, Framed-MTU = 576, Service-Type = Framed-User, Reply-Message = "Hello, \%u", EAP-Message = 0x010100160410266b0e9a58322f4d01ab25b35f879464, Message-Authenticator = 0x11b5043c8a288758173133a5e07434cf, State = 0xc6d195032fdc30240f7313b231ef1d77
 
 #
 #  3.
index ed55a69f398ab9341e69cc9c4171aa78d74e9769..81051c122c319e6b384684fc462274dec7317155 100644 (file)
@@ -59,7 +59,7 @@ proto-dictionary radius
 #    |  value     = '\x00\x81 \x01\r\xb8\n\x0b\x12\xf0\x00\x00\x00\x00\x00\x00\x00\x01'
 #
 decode-proto 01 f0 00 8d 2a fd b0 90 41 8a c6 36 52 98 fb bb 15 e0 fd 2e 01 05 62 6f 62 02 12 45 e8 d5 da ea d8 7e 40 15 e0 fc c0 ec 0f 08 a1 5f 12 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 01 61 14 00 40 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 00 61 0c 00 40 20 01 0d b8 0a 0b 12 f0 61 04 00 00 61 03 00 61 15 00 40 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 00 00 61 14 00 81 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 01
-match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x2afdb090418ac6365298fbbb15e0fd2e, User-Name = "bob", User-Password = "\323\006\334\020\236%\004Z\005\246\373\344\354\033\212*", NAS-IPv6-Address = 2001:db8:a0b:12f0::1, Framed-IPv6-Prefix = 2001:db8:a0b:12f0::/64, Framed-IPv6-Prefix = 2001:db8:a0b:12f0::/64, Framed-IPv6-Prefix = ::/0, Attr-97 = 0x00, Attr-97 = 0x004020010db80a0b12f0000000000000000000, Attr-97 = 0x008120010db80a0b12f00000000000000001
+match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x2afdb090418ac6365298fbbb15e0fd2e, User-Name = "bob", User-Password = "\323\006\334\020\236\%\004Z\005\246\373\344\354\033\212*", NAS-IPv6-Address = 2001:db8:a0b:12f0::1, Framed-IPv6-Prefix = 2001:db8:a0b:12f0::/64, Framed-IPv6-Prefix = 2001:db8:a0b:12f0::/64, Framed-IPv6-Prefix = ::/0, Attr-97 = 0x00, Attr-97 = 0x004020010db80a0b12f0000000000000000000, Attr-97 = 0x008120010db80a0b12f00000000000000001
 
 encode-proto -
 match 01 00 00 85 00 44 be 93 a9 c4 d0 90 66 04 bc 31 93 7a 49 51 01 05 62 6f 62 02 12 3b ab 4a 9a db 55 c5 f9 98 99 d7 09 e3 c0 9e 32 5f 12 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 01 61 0c 00 40 20 01 0d b8 0a 0b 12 f0 61 0c 00 40 20 01 0d b8 0a 0b 12 f0 61 04 00 00 61 03 00 61 15 00 40 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 00 00 61 14 00 81 20 01 0d b8 0a 0b 12 f0 00 00 00 00 00 00 00 01
index 8927e305b9e0372dcbdb4d01c48bd5d82e1e806c..f47933e5288f7f0fb2601e611a827bf5c3d651c1 100644 (file)
@@ -646,7 +646,7 @@ match Packet-Type = Access-Reject, Packet-Authentication-Vector = 0x42d5fe68699d
 #    |  value     = c8f4e9e54928fbaec3bb91f76a43684c
 #
 decode-proto 01 b8 00 4b 00 ef a0 45 6c 07 55 d2 c0 50 93 38 be ff 48 40 01 07 73 74 65 76 65 02 12 46 b9 07 61 ed 64 b6 28 35 c1 66 d0 70 fb 53 eb 04 06 c0 a8 00 1c 05 06 00 00 00 7b 50 12 c8 f4 e9 e5 49 28 fb ae c3 bb 91 f7 6a 43 68 4c
-match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
+match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142\%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
 
 #
 #  16.
@@ -679,7 +679,7 @@ match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0
 #    |  value     = c8f4e9e54928fbaec3bb91f76a43684c
 #
 decode-proto 01 b8 00 4b 00 ef a0 45 6c 07 55 d2 c0 50 93 38 be ff 48 40 01 07 73 74 65 76 65 02 12 46 b9 07 61 ed 64 b6 28 35 c1 66 d0 70 fb 53 eb 04 06 c0 a8 00 1c 05 06 00 00 00 7b 50 12 c8 f4 e9 e5 49 28 fb ae c3 bb 91 f7 6a 43 68 4c
-match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
+match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142\%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
 
 #
 #  17.
@@ -712,7 +712,7 @@ match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0
 #    |  value     = c8f4e9e54928fbaec3bb91f76a43684c
 #
 decode-proto 01 b8 00 4b 00 ef a0 45 6c 07 55 d2 c0 50 93 38 be ff 48 40 01 07 73 74 65 76 65 02 12 46 b9 07 61 ed 64 b6 28 35 c1 66 d0 70 fb 53 eb 04 06 c0 a8 00 1c 05 06 00 00 00 7b 50 12 c8 f4 e9 e5 49 28 fb ae c3 bb 91 f7 6a 43 68 4c
-match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
+match Packet-Type = Access-Request, Packet-Authentication-Vector = 0x00efa0456c0755d2c0509338beff4840, User-Name = "steve", User-Password = "\320W\016\253\231\231\3142\%\207a\364p\357\321`", NAS-IP-Address = 192.168.0.28, NAS-Port = 123, Message-Authenticator = 0xc8f4e9e54928fbaec3bb91f76a43684c
 
 #
 #  18.
index 864b0eb09cf894296d828f16aec61737f1abdf8f..5de503ab29b7504407df8c516a56d04d113056ac 100644 (file)
@@ -45,15 +45,15 @@ match 40 06 00 00 00 01
 decode-pair -
 match Tunnel-Type = PPTP
 
-encode-pair Tunnel-Type:1 = PPTP
-match 40 06 01 00 00 01
+#encode-pair Tunnel-Type:1 = PPTP
+#match 40 06 01 00 00 01
 
-decode-pair -
-match Tunnel-Type:1 = PPTP
+#decode-pair -
+#match Tunnel-Type:1 = PPTP
 
 # Invalid tag on encoding
-encode-pair Tunnel-Type:32 = PPTP
-match Invalid tag for attribute Tunnel-Type:32
+#encode-pair Tunnel-Type:32 = PPTP
+#match Invalid tag for attribute Tunnel-Type:32
 
 # Invalid tag on decoding
 decode-pair 40 06 20 00 00 01
@@ -72,11 +72,11 @@ match Tunnel-Client-Endpoint = "foo"
 # The first byte of the string looks like a tag.  In order to avoid
 # confusion, we encode a zero tag.
 #
-encode-pair Tunnel-Client-Endpoint = "\001\002\003"
-match 42 06 00 01 02 03
+#encode-pair Tunnel-Client-Endpoint = "\001\002\003"
+#match 42 06 00 01 02 03
 
-decode-pair -
-match Tunnel-Client-Endpoint = "\001\002\003"
+#decode-pair -
+#match Tunnel-Client-Endpoint = "\001\002\003"
 
 count
-match 34
+match 24
index e29c012e22b1a9b9f8149338c8970a02cf226e5b..99cbe4467c312518202fb054e9e9de7bc7dc84dd 100644 (file)
@@ -33,11 +33,11 @@ match 1a 0e 00 00 60 b5 54 08 00 09 05 04 03 01
 #  @todo - ensure that unknown attributes still have the WiMAX
 #  "continuation" flag.
 #
-#encode-pair Attr-26.24757.84.9.4 = 0x01
+#encode-pair 26.24757.84.9.4 = 0x01
 #data 1a 0e 00 00 60 b5 54 08 00 09 05 04 03 01
 
 # and parsing the OID should parse LHS / RHS into the known attribute.
-#attribute Attr-26.24757.84.9.4 = 0x01
+#attribute 26.24757.84.9.4 = 0x01
 #data WiMAX-PFDv2-Classifier-Direction = 1
 
 encode-pair WiMAX-PFDv2-Classifier-Direction = 1, WiMAX-PFDv2-Src-Port = 6809
index ccade8f4718bf9d3ac5a385b025994b8dc938b1e..9fabb1fcdd7ac8eabdb132e7e2c3af70110c1499 100644 (file)
@@ -1,19 +1,19 @@
-encode Attr-240.1.1 = 0x01
+encode 240.1.1 = 0x01
 match Previous OID component specified a non-structural type
 
-encode Attr-240.26.1.1.1 = 0x01
+encode 240.26.1.1.1 = 0x01
 match Previous OID component specified a non-structural type
 
-encode Attr-246.26.1.1.1 = 0x01
+encode 246.26.1.1.1 = 0x01
 match Previous OID component specified a non-structural type
 
-encode Attr-240.9999999999999999999.1.1 = 0x01
+encode 240.9999999999999999999.1.1 = 0x01
 match Previous OID component specified a non-structural type
 
-encode Attr-26.1.1 = 0x01
+encode 26.1.1 = 0x01
 match 1a 09 00 00 00 01 01 03 01
 
-encode Attr-26.1344.1 = 0x01020304
+encode 26.1344.1 = 0x01020304
 match 1a 0c 00 00 05 40 01 06 01 02 03 04
 
 count
index b86a3c0c19ecad3b9eb0d7c38d140a8102a80fbd..62c8dc95880ae404164feece23d5676df57243b9 100644 (file)
@@ -10,18 +10,47 @@ xlat the quick brown fox jumped over the lazy dog
 match the quick brown fox jumped over the lazy dog
 
 # The second % should not be present in the output.  We should also not see 'Missing closing brace' type errors
-xlat should not start %%{expansion
-match should not start %{expansion
+xlat should not start \%{expansion
+match should not start \%{expansion
 
 xlat %
-match ERROR offset 1: Invalid variable expansion
+match \%
 
-xlat %%
-match %
+xlat \%
+match \%
 
 xlat \
-match ERROR offset 0: Invalid escape at end of string
+match \\
 
+#
+#  Literals mixed with attributes
+#
+xlat literal%{User-Password}
+match literal%{User-Password}
+
+xlat {literal}%{User-Password}
+match {literal}%{User-Password}
+
+xlat literal%{User-Password}{literal}
+match literal%{User-Password}{literal}
+
+xlat {literal%{User-Password}literal}
+match {literal%{User-Password}literal}
+
+xlat \%{literal%{User-Password}literal}
+match \%{literal%{User-Password}literal}
+
+xlat literal%{%{User-Password}:-literal}
+match literal%{%{User-Password}:-literal}
+
+xlat literal%{%{User-Password}:-\%{lit}eral}
+match literal%{%{User-Password}:-\%{lit}eral}
+
+xlat literal%{%{User-Password}:-%{User-Name}\%literal}
+match literal%{%{User-Password}:-%{User-Name}\%literal}
+
+xlat literal%{%{User-Password}:-\%literal}
+match literal%{%{User-Password}:-\%literal}
 #
 #  Regex capture groups
 #
@@ -35,13 +64,13 @@ xlat %{33}
 match ERROR offset 2: Invalid regex reference.  Must be in range 0-32
 
 xlat %{3a}
-match ERROR offset 2: Unknown attribute
+match ERROR offset 2: Unexpected text after attribute reference
 
 xlat %{-3}
-match ERROR offset 2: Unknown attribute
+match ERROR offset 2: Unresolved attributes not allowed in expansions here
 
 xlat %{3
-match ERROR offset 1: No matching closing brace
+match ERROR offset 3: Missing closing brace
 
 #
 #  Tests for xlat expansion
@@ -55,32 +84,8 @@ match %{test:bar}
 xlat %{Tunnel-Password}
 match %{Tunnel-Password}
 
-xlat %{Tunnel-Password:1}
-match %{Tunnel-Password:1}
-
-xlat %{Tunnel-Password:1[3]}
-match %{Tunnel-Password:1[3]}
-
-xlat %{Tunnel-Password:1[*]}
-match %{Tunnel-Password:1[*]}
-
-xlat %{Tunnel-Password:1[#]}
-match %{Tunnel-Password:1[#]}
-
-xlat %{reply:Tunnel-Password}
-match %{reply:Tunnel-Password}
-
-xlat %{reply:Tunnel-Password:1}
-match %{reply:Tunnel-Password:1}
-
-xlat %{reply:Tunnel-Password:1[3]}
-match %{reply:Tunnel-Password:1[3]}
-
-xlat %{reply:Tunnel-Password:1[*]}
-match %{reply:Tunnel-Password:1[*]}
-
-xlat %{reply:Tunnel-Password:1[#]}
-match %{reply:Tunnel-Password:1[#]}
+xlat %{reply.Tunnel-Password}
+match %{reply.Tunnel-Password}
 
 xlat %{User-Name[3]}
 match %{User-Name[3]}
@@ -94,13 +99,13 @@ match %{User-Name[#]}
 xlat %{User-Name[n]}
 match %{User-Name[n]}
 
-xlat %{request:User-Name[3]}
+xlat %{request.User-Name[3]}
 match %{User-Name[3]}
 
-xlat %{request:User-Name[*]}
+xlat %{request.User-Name[*]}
 match %{User-Name[*]}
 
-xlat %{request:User-Name[#]}
+xlat %{request.User-Name[#]}
 match %{User-Name[#]}
 
 xlat %{3GPP-SGSN-Address}
@@ -113,35 +118,36 @@ xlat \"%{Foreach-Variable-0}\"
 match \"%{Foreach-Variable-0}\"
 
 #
-#  3GPP stuff, to distinguish "list:3GPP" from
-#  "attribute:tag"
+#  3GPP stuff
 #
-xlat \"%{request:3GPP-IMSI}\"
+xlat \"%{request.3GPP-IMSI}\"
 match \"%{3GPP-IMSI}\"
 
-xlat \"%{reply:3GPP-IMSI}\"
-match \"%{reply:3GPP-IMSI}\"
+xlat \"%{reply.3GPP-IMSI}\"
+match \"%{reply.3GPP-IMSI}\"
 
-xlat \"%{reply:3GPP-IMSI[2]}\"
-match \"%{reply:3GPP-IMSI[2]}\"
+xlat \"%{reply.3GPP-IMSI[2]}\"
+match \"%{reply.3GPP-IMSI[2]}\"
 
 xlat /([A-Z0-9\\-]*)_%{Calling-Station-Id}/
 match /([A-Z0-9\\-]*)_%{Calling-Station-Id}/
 
 xlat %{length}
-match ERROR offset 2: Unknown attribute
+match %{length}
 
 xlat %{length:
-match ERROR offset 1: No matching closing brace
+match ERROR offset 9: Missing closing brace
 
 xlat %{length:1 + 2
-match ERROR offset 14: Missing closing brace at end of string
+match ERROR offset 14: Missing closing brace
 
 xlat \"%t\tfoo\"
 match \"%t\tfoo\"
 
+allow-unresolved yes
 xlat \"%t\t%{Client-IP-Address}\"
 match \"%t\t%{Client-IP-Address}\"
+allow-unresolved no
 
 xlat \"foo %{test:foo}\"
 match \"foo %{test:foo}\"
@@ -150,10 +156,10 @@ match \"foo %{test:foo}\"
 #  Alternations
 #
 xlat %{%{foo}:-%{bar}}
-match ERROR offset 4: Unknown attribute
+match ERROR offset 4: Unresolved attributes not allowed in expansions here
 
 xlat %{%{User-Name}:-%{bar}}
-match ERROR offset 18: Unknown attribute
+match ERROR offset 18: Unresolved attributes not allowed in expansions here
 
 xlat %{%{User-Name}:-bar}
 match %{%{User-Name}:-bar}
@@ -192,36 +198,36 @@ xlat %{%{}:-foo}
 match ERROR offset 4: Empty expression is invalid
 
 xlat %{%{User-Name}:-
-match ERROR offset 2: No matching closing brace
+match ERROR offset 16: Missing closing brace
 
 # Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
 xlat %{%{User-Name}:-foo
-match ERROR offset 19: Missing closing brace at end of string
+match ERROR offset 19: Missing closing brace
 
 xlat %{%{User-Name}:}
-match ERROR offset 15: Expected '-' after ':'
+match ERROR offset 14: Expected ':-' after first expansion
 
 xlat %{%{User-Name}}
-match ERROR offset 14: Expected ':' after first expansion, got '}'
+match ERROR offset 14: Expected ':-' after first expansion
 
 
 #
 #  Empty and malformed expansions
 #
 xlat %{
-match ERROR offset 1: No matching closing brace
+match ERROR offset 2: Missing closing brace
 
 xlat %{}
 match ERROR offset 2: Empty expression is invalid
 
 xlat %{:}
-match ERROR offset 2: Missing expansion function or list qualifier
+match ERROR offset 2: Missing expansion function
 
 xlat %{:bar}
-match ERROR offset 2: Missing expansion function or list qualifier
+match ERROR offset 2: Missing expansion function
 
 xlat %{foo: bar}
-match ERROR offset 2: Unknown expansion function or invalid list qualifier
+match ERROR offset 2: Unresolved expansion functions are not allowed here
 
 xlat %{[}
 match ERROR offset 2: Missing attribute name
@@ -233,10 +239,10 @@ xlat %{ }
 match ERROR offset 2: Invalid char ' ' in expression
 
 xlat %{\t}
-match ERROR offset 2: Invalid char '\t' in expression
+match ERROR offset 2: Invalid attribute name
 
 xlat %{\n}
-match ERROR offset 2: Invalid char '\n' in expression
+match ERROR offset 2: Invalid attribute name
 
 xlat %{foo }
 match ERROR offset 5: Invalid char ' ' in expression
@@ -245,17 +251,17 @@ xlat %{foo bar}
 match ERROR offset 5: Invalid char ' ' in expression
 
 xlat %{test:
-match ERROR offset 1: No matching closing brace
+match ERROR offset 7: Missing closing brace
 
 xlat %{test:%{User-Name
-match ERROR offset 8: No matching closing brace
+match ERROR offset 18: Missing closing brace
 
 # Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
 xlat %{test:%{User-Name}
-match ERROR offset 19: Missing closing brace at end of string
+match ERROR offset 19: Missing closing brace
 
 xlat %{myfirstxlat
-match ERROR offset 1: No matching closing brace
+match ERROR offset 13: Missing closing brace
 
 #
 #  API to split xlat strings into argv-style arguments.
@@ -264,31 +270,31 @@ xlat_argv /bin/sh %{User-Name}
 match [0]{ /bin/sh }, [1]{ %{User-Name} }
 
 xlat_argv /bin/sh "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ "%{User-Name}" }
+match [0]{ /bin/sh }, [1]{ %{User-Name} }
 
 # with quotes
 xlat_argv /bin/sh "foo" "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ "foo" }, [2]{ "%{User-Name}" }
+match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{User-Name} }
 
 # many spaces between arguments
 xlat_argv /bin/sh         "foo"       "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ "foo" }, [2]{ "%{User-Name}" }
+match [0]{ /bin/sh }, [1]{ foo }, [2]{ %{User-Name} }
 
 # and spaces in quotes
 xlat_argv /bin/sh "foo bar" "%{User-Name}"
-match [0]{ /bin/sh }, [1]{ "foo bar" }, [2]{ "%{User-Name}" }
+match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{User-Name} }
 
 # and multiple xlats inside of quotes
 xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id}"
-match [0]{ /bin/sh }, [1]{ "foo bar" }, [2]{ "%{User-Name} %{Filter-Id}" }
+match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{User-Name} %{Filter-Id} }
 
 # and errors
 xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id"
-match ERROR offset 33: No matching closing brace
+match ERROR offset 44: Missing closing brace
 
 # and text immediately after a variable expansion
 xlat_argv echo hello %{Tmp-String-0}:1234 world
 match [0]{ echo }, [1]{ hello }, [2]{ %{Tmp-String-0}:1234 }, [3]{ world }
 
 count
-match 173
+match 177