From: Arran Cudbard-Bell Date: Wed, 26 Aug 2020 17:49:07 +0000 (-0500) Subject: Start of nested attribute support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9bd3bbfb2cd9f24be62e604a1b191e1f2b648c3a;p=thirdparty%2Ffreeradius-server.git Start of nested attribute support 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 --- diff --git a/doc/antora/modules/howto/pages/modules/ldap/authorization/groups.adoc b/doc/antora/modules/howto/pages/modules/ldap/authorization/groups.adoc index 76259d639cf..ef6cbf0a0a1 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/authorization/groups.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/authorization/groups.adoc @@ -82,8 +82,8 @@ ldap { # example - 'cn' name_attribute = '' <3> - # example - "(member=%{control:Ldap-UserDn})" - membership_filter = "(=%{control:Ldap-UserDn})" <4> + # example - "(member=%{control.Ldap-UserDn})" + membership_filter = "(=%{control.Ldap-UserDn})" <4> } } ---- @@ -107,8 +107,8 @@ ldap { # example - 'cn' name_attribute = '' <3> - # example - "(memberUid=%{control:Ldap-UserDn})" - membership_filter = "(=%{control:Ldap-UserDn})" <4> + # example - "(memberUid=%{control.Ldap-UserDn})" + membership_filter = "(=%{control.Ldap-UserDn})" <4> } } ---- diff --git a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc index 67a1caebb5d..9ebf79a4a3c 100644 --- a/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc +++ b/doc/antora/modules/howto/pages/modules/ldap/ldapsearch/translating_to_the_ldap_module.adoc @@ -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 = "(=%{control:Ldap-UserDn})"``` +| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(=%{control.Ldap-UserDn})"``` |=== == Groups - variant 4 @@ -95,6 +95,6 @@ both on user DN and user name e.g. [source,config] ---- -membership_filter = "(|(=%{control:Ldap-UserDn})(=%{%{Stripped-User-Name}:-%{User-Name}}))" +membership_filter = "(|(=%{control.Ldap-UserDn})(=%{%{Stripped-User-Name}:-%{User-Name}}))" ---- **** diff --git a/doc/antora/modules/howto/pages/modules/sqlippool/index.adoc b/doc/antora/modules/howto/pages/modules/sqlippool/index.adoc index 91cc83457c9..d32d6b395d0 100644 --- a/doc/antora/modules/howto/pages/modules/sqlippool/index.adoc +++ b/doc/antora/modules/howto/pages/modules/sqlippool/index.adoc @@ -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}', \ diff --git a/doc/antora/modules/raddb/pages/mods-available/cache_eap.adoc b/doc/antora/modules/raddb/pages/mods-available/cache_eap.adoc index 4257dd74ce7..f16863932be 100644 --- a/doc/antora/modules/raddb/pages/mods-available/cache_eap.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/cache_eap.adoc @@ -68,7 +68,7 @@ update
{ ... }:: ``` cache cache_eap { - key = "%{%{control:State}:-%{%{reply:State}:-%{State}}}" + key = "%{%{control.State}:-%{%{reply.State}:-%{State}}}" ttl = 15 update reply { &reply: += &reply: diff --git a/doc/antora/modules/raddb/pages/mods-available/delay.adoc b/doc/antora/modules/raddb/pages/mods-available/delay.adoc index a7789b00fa4..3f0983b040e 100644 --- a/doc/antora/modules/raddb/pages/mods-available/delay.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/delay.adoc @@ -78,7 +78,7 @@ delay { # relative = no } delay delay_reject { - delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}" + delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}" relative = yes } ``` diff --git a/doc/antora/modules/raddb/pages/mods-available/dhcp_sqlippool.adoc b/doc/antora/modules/raddb/pages/mods-available/dhcp_sqlippool.adoc index d2aea4a18cb..aac4095cc6f 100644 --- a/doc/antora/modules/raddb/pages/mods-available/dhcp_sqlippool.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/dhcp_sqlippool.adoc @@ -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})" } ``` diff --git a/doc/antora/modules/raddb/pages/mods-available/json.adoc b/doc/antora/modules/raddb/pages/mods-available/json.adoc index 20077c67b11..093dea1936e 100644 --- a/doc/antora/modules/raddb/pages/mods-available/json.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/json.adoc @@ -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." } ---- diff --git a/doc/antora/modules/raddb/pages/mods-available/ldap.adoc b/doc/antora/modules/raddb/pages/mods-available/ldap.adoc index 0baf09786a0..789b8a04f2c 100644 --- a/doc/antora/modules/raddb/pages/mods-available/ldap.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/ldap.adoc @@ -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' diff --git a/doc/antora/modules/raddb/pages/mods-available/linelog.adoc b/doc/antora/modules/raddb/pages/mods-available/linelog.adoc index 0a54808cf51..5f38b8533a9 100644 --- a/doc/antora/modules/raddb/pages/mods-available/linelog.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/linelog.adoc @@ -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}" diff --git a/doc/antora/modules/raddb/pages/mods-available/sqlippool.adoc b/doc/antora/modules/raddb/pages/mods-available/sqlippool.adoc index 97135ec7d53..a11fb1a5ef1 100644 --- a/doc/antora/modules/raddb/pages/mods-available/sqlippool.adoc +++ b/doc/antora/modules/raddb/pages/mods-available/sqlippool.adoc @@ -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 diff --git a/doc/antora/modules/raddb/pages/sites-available/check-eap-tls.adoc b/doc/antora/modules/raddb/pages/sites-available/check-eap-tls.adoc index 1221322a178..dccce5852f3 100644 --- a/doc/antora/modules/raddb/pages/sites-available/check-eap-tls.adoc +++ b/doc/antora/modules/raddb/pages/sites-available/check-eap-tls.adoc @@ -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 # } diff --git a/doc/antora/modules/raddb/pages/sites-available/coa-relay.adoc b/doc/antora/modules/raddb/pages/sites-available/coa-relay.adoc index 268b7fa6430..d9ce274989d 100644 --- a/doc/antora/modules/raddb/pages/sites-available/coa-relay.adoc +++ b/doc/antora/modules/raddb/pages/sites-available/coa-relay.adoc @@ -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 { diff --git a/doc/antora/modules/raddb/pages/sites-available/default.adoc b/doc/antora/modules/raddb/pages/sites-available/default.adoc index db917afed02..ed05ad8c1e6 100644 --- a/doc/antora/modules/raddb/pages/sites-available/default.adoc +++ b/doc/antora/modules/raddb/pages/sites-available/default.adoc @@ -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) { diff --git a/doc/antora/modules/unlang/pages/call.adoc b/doc/antora/modules/unlang/pages/call.adoc index c6c27b5bcf2..819a3dc434a 100644 --- a/doc/antora/modules/unlang/pages/call.adoc +++ b/doc/antora/modules/unlang/pages/call.adoc @@ -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}" } } ---- diff --git a/doc/antora/modules/unlang/pages/type/string/double.adoc b/doc/antora/modules/unlang/pages/type/string/double.adoc index 22642af38fa..9e3feb3c92a 100644 --- a/doc/antora/modules/unlang/pages/type/string/double.adoc +++ b/doc/antora/modules/unlang/pages/type/string/double.adoc @@ -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. diff --git a/doc/antora/modules/unlang/pages/xlat/attribute.adoc b/doc/antora/modules/unlang/pages/xlat/attribute.adoc index fd80d4afa01..0464947273e 100644 --- a/doc/antora/modules/unlang/pages/xlat/attribute.adoc +++ b/doc/antora/modules/unlang/pages/xlat/attribute.adoc @@ -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: diff --git a/doc/antora/modules/unlang/pages/xlat/builtin.adoc b/doc/antora/modules/unlang/pages/xlat/builtin.adoc index 4d86133f56c..d311ebb3fd3 100644 --- a/doc/antora/modules/unlang/pages/xlat/builtin.adoc +++ b/doc/antora/modules/unlang/pages/xlat/builtin.adoc @@ -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 =~ /^(?(.*))@(?(.*))$/) { 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}}" } ---- diff --git a/man/man5/unlang.5 b/man/man5/unlang.5 index dc691387a3b..46a5befd797 100644 --- a/man/man5/unlang.5 +++ b/man/man5/unlang.5 @@ -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 diff --git a/raddb/mods-available/cache_eap b/raddb/mods-available/cache_eap index d70e420e032..c44116fb77b 100644 --- a/raddb/mods-available/cache_eap +++ b/raddb/mods-available/cache_eap @@ -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. diff --git a/raddb/mods-available/delay b/raddb/mods-available/delay index e2041833cbd..4f6984eeff2 100644 --- a/raddb/mods-available/delay +++ b/raddb/mods-available/delay @@ -77,6 +77,6 @@ delay { # received. # delay delay_reject { - delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}" + delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}" relative = yes } diff --git a/raddb/mods-available/dhcp_sqlippool b/raddb/mods-available/dhcp_sqlippool index d7dd2b9d3fe..f41d60041c2 100644 --- a/raddb/mods-available/dhcp_sqlippool +++ b/raddb/mods-available/dhcp_sqlippool @@ -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})" } diff --git a/raddb/mods-available/json b/raddb/mods-available/json index d32f7f66ee9..1e710e99864 100644 --- a/raddb/mods-available/json +++ b/raddb/mods-available/json @@ -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." # } # ---- # diff --git a/raddb/mods-available/ldap b/raddb/mods-available/ldap index 47f33fb8212..47670e9ee29 100644 --- a/raddb/mods-available/ldap +++ b/raddb/mods-available/ldap @@ -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}}" # } # ---- # diff --git a/raddb/mods-available/linelog b/raddb/mods-available/linelog index 29d4c5e0555..88dffa8a517 100644 --- a/raddb/mods-available/linelog +++ b/raddb/mods-available/linelog @@ -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 { ... }:: diff --git a/raddb/mods-available/sqlippool b/raddb/mods-available/sqlippool index 78394fbfbba..dbb8f055488 100644 --- a/raddb/mods-available/sqlippool +++ b/raddb/mods-available/sqlippool @@ -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})" } diff --git a/raddb/mods-config/sql/cui/mysql/queries.conf b/raddb/mods-config/sql/cui/mysql/queries.conf index e043cd61a01..7085b532235 100644 --- a/raddb/mods-config/sql/cui/mysql/queries.conf +++ b/raddb/mods-config/sql/cui/mysql/queries.conf @@ -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}'" } diff --git a/raddb/mods-config/sql/cui/postgresql/queries.conf b/raddb/mods-config/sql/cui/postgresql/queries.conf index cffd99e3e7c..373c61ac030 100644 --- a/raddb/mods-config/sql/cui/postgresql/queries.conf +++ b/raddb/mods-config/sql/cui/postgresql/queries.conf @@ -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" diff --git a/raddb/mods-config/sql/cui/sqlite/queries.conf b/raddb/mods-config/sql/cui/sqlite/queries.conf index e6c1d491853..a1a3df8b81f 100644 --- a/raddb/mods-config/sql/cui/sqlite/queries.conf +++ b/raddb/mods-config/sql/cui/sqlite/queries.conf @@ -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)" } diff --git a/raddb/mods-config/sql/ippool-dhcp/mssql/procedure.sql b/raddb/mods-config/sql/ippool-dhcp/mssql/procedure.sql index 05cea96570c..5605a29b90e 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mssql/procedure.sql +++ b/raddb/mods-config/sql/ippool-dhcp/mssql/procedure.sql @@ -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}, \ diff --git a/raddb/mods-config/sql/ippool-dhcp/mssql/queries.conf b/raddb/mods-config/sql/ippool-dhcp/mssql/queries.conf index acd60c396a4..5c17465d292 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mssql/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/mssql/queries.conf @@ -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}'" diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql index cbfec9326d4..7cdaef87775 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql @@ -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}, \ diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf b/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf index 97d2ffd293f..6c9bd1c65e8 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf @@ -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}'" diff --git a/raddb/mods-config/sql/ippool-dhcp/oracle/procedure.sql b/raddb/mods-config/sql/ippool-dhcp/oracle/procedure.sql index 6114a2c36f4..65a61eb3a59 100644 --- a/raddb/mods-config/sql/ippool-dhcp/oracle/procedure.sql +++ b/raddb/mods-config/sql/ippool-dhcp/oracle/procedure.sql @@ -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}, \ diff --git a/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf b/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf index cea76570b32..5fa398500bb 100644 --- a/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf @@ -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" diff --git a/raddb/mods-config/sql/ippool-dhcp/postgresql/procedure.sql b/raddb/mods-config/sql/ippool-dhcp/postgresql/procedure.sql index 1141057612b..773de2c5479 100644 --- a/raddb/mods-config/sql/ippool-dhcp/postgresql/procedure.sql +++ b/raddb/mods-config/sql/ippool-dhcp/postgresql/procedure.sql @@ -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}, \ diff --git a/raddb/mods-config/sql/ippool-dhcp/postgresql/queries.conf b/raddb/mods-config/sql/ippool-dhcp/postgresql/queries.conf index d1fc8f65e59..0610fd2bd64 100644 --- a/raddb/mods-config/sql/ippool-dhcp/postgresql/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/postgresql/queries.conf @@ -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}'" diff --git a/raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf b/raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf index 63e219490c0..b3df89e9e3a 100644 --- a/raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf @@ -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}'" diff --git a/raddb/mods-config/sql/ippool/mssql/procedure.sql b/raddb/mods-config/sql/ippool/mssql/procedure.sql index 54e9f26684f..52fe0b81dab 100644 --- a/raddb/mods-config/sql/ippool/mssql/procedure.sql +++ b/raddb/mods-config/sql/ippool/mssql/procedure.sql @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/mssql/queries.conf b/raddb/mods-config/sql/ippool/mssql/queries.conf index 9b560260b82..31b158e732b 100644 --- a/raddb/mods-config/sql/ippool/mssql/queries.conf +++ b/raddb/mods-config/sql/ippool/mssql/queries.conf @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/mysql/procedure-no-skip-locked.sql b/raddb/mods-config/sql/ippool/mysql/procedure-no-skip-locked.sql index cc934aaee1b..45088f72379 100644 --- a/raddb/mods-config/sql/ippool/mysql/procedure-no-skip-locked.sql +++ b/raddb/mods-config/sql/ippool/mysql/procedure-no-skip-locked.sql @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/mysql/procedure.sql b/raddb/mods-config/sql/ippool/mysql/procedure.sql index 3835cd55d09..70943352511 100644 --- a/raddb/mods-config/sql/ippool/mysql/procedure.sql +++ b/raddb/mods-config/sql/ippool/mysql/procedure.sql @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/mysql/queries.conf b/raddb/mods-config/sql/ippool/mysql/queries.conf index 1a4fb090df3..31106cc5196 100644 --- a/raddb/mods-config/sql/ippool/mysql/queries.conf +++ b/raddb/mods-config/sql/ippool/mysql/queries.conf @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/oracle/procedure.sql b/raddb/mods-config/sql/ippool/oracle/procedure.sql index d157112f605..a2e88f266ac 100644 --- a/raddb/mods-config/sql/ippool/oracle/procedure.sql +++ b/raddb/mods-config/sql/ippool/oracle/procedure.sql @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/oracle/queries.conf b/raddb/mods-config/sql/ippool/oracle/queries.conf index e13d3bfa3ee..de12f207939 100644 --- a/raddb/mods-config/sql/ippool/oracle/queries.conf +++ b/raddb/mods-config/sql/ippool/oracle/queries.conf @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/postgresql/procedure.sql b/raddb/mods-config/sql/ippool/postgresql/procedure.sql index bf94abd6995..5b3d46d8ddd 100644 --- a/raddb/mods-config/sql/ippool/postgresql/procedure.sql +++ b/raddb/mods-config/sql/ippool/postgresql/procedure.sql @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/postgresql/queries.conf b/raddb/mods-config/sql/ippool/postgresql/queries.conf index 01d474018af..a49b1bd041c 100644 --- a/raddb/mods-config/sql/ippool/postgresql/queries.conf +++ b/raddb/mods-config/sql/ippool/postgresql/queries.conf @@ -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}', \ diff --git a/raddb/mods-config/sql/ippool/sqlite/queries.conf b/raddb/mods-config/sql/ippool/sqlite/queries.conf index 4628cf993df..441559d522a 100644 --- a/raddb/mods-config/sql/ippool/sqlite/queries.conf +++ b/raddb/mods-config/sql/ippool/sqlite/queries.conf @@ -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" # diff --git a/raddb/mods-config/sql/main/cassandra/queries.conf b/raddb/mods-config/sql/main/cassandra/queries.conf index a575e44d528..42e56ba3a89 100644 --- a/raddb/mods-config/sql/main/cassandra/queries.conf +++ b/raddb/mods-config/sql/main/cassandra/queries.conf @@ -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}')" } diff --git a/raddb/mods-config/sql/main/mssql/queries.conf b/raddb/mods-config/sql/main/mssql/queries.conf index 6f1b9a063d8..b4e2754924e 100644 --- a/raddb/mods-config/sql/main/mssql/queries.conf +++ b/raddb/mods-config/sql/main/mssql/queries.conf @@ -353,6 +353,6 @@ post-auth { VALUES(\ '%{User-Name}', \ '%{%{User-Password}:-CHAP-PASSWORD}', \ - '%{reply:Packet-Type}', \ + '%{reply.Packet-Type}', \ '%S.%{expr:%M / 1000}')" } diff --git a/raddb/mods-config/sql/main/mysql/extras/wimax/queries.conf b/raddb/mods-config/sql/main/mysql/extras/wimax/queries.conf index 6923c11748a..83e90156148 100644 --- a/raddb/mods-config/sql/main/mysql/extras/wimax/queries.conf +++ b/raddb/mods-config/sql/main/mysql/extras/wimax/queries.conf @@ -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}' )" diff --git a/raddb/mods-config/sql/main/mysql/queries.conf b/raddb/mods-config/sql/main/mysql/queries.conf index 77daae146eb..96fcbe5036d 100644 --- a/raddb/mods-config/sql/main/mysql/queries.conf +++ b/raddb/mods-config/sql/main/mysql/queries.conf @@ -385,6 +385,6 @@ post-auth { VALUES ( \ '%{SQL-User-Name}', \ '%{%{User-Password}:-%{Chap-Password}}', \ - '%{reply:Packet-Type}', \ + '%{reply.Packet-Type}', \ '%S.%M')" } diff --git a/raddb/mods-config/sql/main/oracle/queries.conf b/raddb/mods-config/sql/main/oracle/queries.conf index 18bc1c88452..3bc510158ed 100644 --- a/raddb/mods-config/sql/main/oracle/queries.conf +++ b/raddb/mods-config/sql/main/oracle/queries.conf @@ -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'))" } diff --git a/raddb/mods-config/sql/main/postgresql/queries.conf b/raddb/mods-config/sql/main/postgresql/queries.conf index 528b421ddc7..72926eb6648 100644 --- a/raddb/mods-config/sql/main/postgresql/queries.conf +++ b/raddb/mods-config/sql/main/postgresql/queries.conf @@ -454,6 +454,6 @@ post-auth { VALUES(\ '%{User-Name}', \ '%{%{User-Password}:-Chap-Password}', \ - '%{reply:Packet-Type}', \ + '%{reply.Packet-Type}', \ '%S.%M')" } diff --git a/raddb/mods-config/sql/main/sqlite/queries.conf b/raddb/mods-config/sql/main/sqlite/queries.conf index fe51a85b076..3a14ca121ab 100644 --- a/raddb/mods-config/sql/main/sqlite/queries.conf +++ b/raddb/mods-config/sql/main/sqlite/queries.conf @@ -399,6 +399,6 @@ post-auth { VALUES ( \ '%{SQL-User-Name}', \ '%{%{User-Password}:-%{Chap-Password}}', \ - '%{reply:Packet-Type}', \ + '%{reply.Packet-Type}', \ '%S.%M')" } diff --git a/raddb/policy.d/cui b/raddb/policy.d/cui index 43d4dea9726..a2ed6e9cd50 100644 --- a/raddb/policy.d/cui +++ b/raddb/policy.d/cui @@ -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}:-}}}" } } } diff --git a/raddb/policy.d/tacacs b/raddb/policy.d/tacacs index 410767ea578..09e5995ae83 100644 --- a/raddb/policy.d/tacacs +++ b/raddb/policy.d/tacacs @@ -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 } diff --git a/raddb/sites-available/check-eap-tls b/raddb/sites-available/check-eap-tls index 81727b5132c..992abb84cd1 100644 --- a/raddb/sites-available/check-eap-tls +++ b/raddb/sites-available/check-eap-tls @@ -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 diff --git a/raddb/sites-available/coa-relay b/raddb/sites-available/coa-relay index 3941c522063..941cf1ccd01 100644 --- a/raddb/sites-available/coa-relay +++ b/raddb/sites-available/coa-relay @@ -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 { diff --git a/raddb/sites-available/default b/raddb/sites-available/default index 14aa785bd90..abe297f660d 100644 --- a/raddb/sites-available/default +++ b/raddb/sites-available/default @@ -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}" # } # diff --git a/raddb/sites-available/dhcp b/raddb/sites-available/dhcp index b4789c50727..038fd5285bf 100644 --- a/raddb/sites-available/dhcp +++ b/raddb/sites-available/dhcp @@ -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}}" } } diff --git a/src/bin/radclient.c b/src/bin/radclient.c index b8028fdbaee..74f51a1ac67 100644 --- a/src/bin/radclient.c +++ b/src/bin/radclient.c @@ -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++; diff --git a/src/bin/radsniff.c b/src/bin/radsniff.c index 8d038793f74..2b935473569 100644 --- a/src/bin/radsniff.c +++ b/src/bin/radsniff.c @@ -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; } diff --git a/src/bin/radsnmp.c b/src/bin/radsnmp.c index 56117977bd4..cd3706af12b 100644 --- a/src/bin/radsnmp.c +++ b/src/bin/radsnmp.c @@ -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; diff --git a/src/bin/unit_test_attribute.c b/src/bin/unit_test_attribute.c index 36ea629b4d7..bb0792989fe 100644 --- a/src/bin/unit_test_attribute.c +++ b/src/bin/unit_test_attribute.c @@ -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; } diff --git a/src/bin/unit_test_map.c b/src/bin/unit_test_map.c index a40204123d4..ab93dfed8ff 100644 --- a/src/bin/unit_test_map.c +++ b/src/bin/unit_test_map.c @@ -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"); diff --git a/src/bin/unit_test_module.c b/src/bin/unit_test_module.c index 1164014838b..b05996e6509 100644 --- a/src/bin/unit_test_module.c +++ b/src/bin/unit_test_module.c @@ -30,6 +30,7 @@ RCSID("$Id$") #include #include #include +#include #include @@ -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")) { diff --git a/src/lib/eap/base.c b/src/lib/eap/base.c index f62b5a916ea..c39651f71e4 100644 --- a/src/lib/eap/base.c +++ b/src/lib/eap/base.c @@ -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) { diff --git a/src/lib/eap/chbind.c b/src/lib/eap/chbind.c index 66a03a933ff..121ad73692d 100644 --- a/src/lib/eap/chbind.c +++ b/src/lib/eap/chbind.c @@ -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 { diff --git a/src/lib/eap/compose.c b/src/lib/eap/compose.c index 2440d71aea0..cc45b83e859 100644 --- a/src/lib/eap/compose.c +++ b/src/lib/eap/compose.c @@ -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; diff --git a/src/lib/eap/session.c b/src/lib/eap/session.c index 97a71b027e9..f64b63486e9 100644 --- a/src/lib/eap/session.c +++ b/src/lib/eap/session.c @@ -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 diff --git a/src/lib/eap_aka_sim/encode.c b/src/lib/eap_aka_sim/encode.c index 562d8b5ad73..e23ff855dcb 100644 --- a/src/lib/eap_aka_sim/encode.c +++ b/src/lib/eap_aka_sim/encode.c @@ -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 diff --git a/src/lib/eap_aka_sim/state_machine.c b/src/lib/eap_aka_sim/state_machine.c index db834ff3775..c84b5324694 100644 --- a/src/lib/eap_aka_sim/state_machine.c +++ b/src/lib/eap_aka_sim/state_machine.c @@ -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); diff --git a/src/lib/eap_aka_sim/vector.c b/src/lib/eap_aka_sim/vector.c index 1c8cd0d53e1..8cf2ad06aa5 100644 --- a/src/lib/eap_aka_sim/vector.c +++ b/src/lib/eap_aka_sim/vector.c @@ -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); diff --git a/src/lib/eap_aka_sim/xlat.c b/src/lib/eap_aka_sim/xlat.c index 8eae3a5a1c3..8cb1ff2dd12 100644 --- a/src/lib/eap_aka_sim/xlat.c +++ b/src/lib/eap_aka_sim/xlat.c @@ -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"); diff --git a/src/lib/io/app_io.c b/src/lib/io/app_io.c index ef05c66deac..b19d662c092 100644 --- a/src/lib/io/app_io.c +++ b/src/lib/io/app_io.c @@ -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) { diff --git a/src/lib/io/master.c b/src/lib/io/master.c index b244ad8e13a..ecfd632a1bf 100644 --- a/src/lib/io/master.c +++ b/src/lib/io/master.c @@ -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, ""); diff --git a/src/lib/json/json.c b/src/lib/json/json.c index 8370acef797..d8f08ddd530 100644 --- a/src/lib/json/json.c +++ b/src/lib/json/json.c @@ -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; diff --git a/src/lib/ldap/control.c b/src/lib/ldap/control.c index 87aa40ccf41..fdafe8774ec 100644 --- a/src/lib/ldap/control.c +++ b/src/lib/ldap/control.c @@ -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: diff --git a/src/lib/redis/redis.c b/src/lib/redis/redis.c index d2fcbc5d1b2..d61900e0aa2 100644 --- a/src/lib/redis/redis.c +++ b/src/lib/redis/redis.c @@ -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; diff --git a/src/lib/server/auth.c b/src/lib/server/auth.c index 33a8085da2d..a826c2ce964 100644 --- a/src/lib/server/auth.c +++ b/src/lib/server/auth.c @@ -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; diff --git a/src/lib/server/cf_file.c b/src/lib/server/cf_file.c index 1c314d564e6..b457563b9d8 100644 --- a/src/lib/server/cf_file.c +++ b/src/lib/server/cf_file.c @@ -32,12 +32,12 @@ RCSID("$Id$") #include #include -#include #include -#include +#include #include - +#include #include +#include #include #include @@ -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 */ diff --git a/src/lib/server/cf_parse.c b/src/lib/server/cf_parse.c index 6ebdc16c017..314f528d853 100644 --- a/src/lib/server/cf_parse.c +++ b/src/lib/server/cf_parse.c @@ -32,9 +32,9 @@ RCSID("$Id$") #include #include #include -#include #include - +#include +#include #include #include #include @@ -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; diff --git a/src/lib/server/cf_util.c b/src/lib/server/cf_util.c index d514f477cd0..5b50a7970e2 100644 --- a/src/lib/server/cf_util.c +++ b/src/lib/server/cf_util.c @@ -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); } } diff --git a/src/lib/server/client.c b/src/lib/server/client.c index bb30a220a46..2d4cf7fb19a 100644 --- a/src/lib/server/client.c +++ b/src/lib/server/client.c @@ -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); diff --git a/src/lib/server/cond.h b/src/lib/server/cond.h index cf21693f131..1a70e3d87ad 100644 --- a/src/lib/server/cond.h +++ b/src/lib/server/cond.h @@ -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); diff --git a/src/lib/server/cond_eval.c b/src/lib/server/cond_eval.c index 531105e3205..3836b52538e 100644 --- a/src/lib/server/cond_eval.c +++ b/src/lib/server/cond_eval.c @@ -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, ""), 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, "") : "none"); - INFO("\top : %s", fr_table_str_by_value(cond_op_table, cond->next_op, "")); - INFO("\tfixup : %s", fr_table_str_by_value(cond_pass2_table, cond->pass2_fixup, "")); + for (c = cond; c; c =c->next) { + INFO("cond %s (%p)", fr_table_str_by_value(cond_type_table, c->type, ""), 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, "") : "none"); + INFO("\top : %s", fr_table_str_by_value(cond_logical_op_table, c->next_op, "")); + INFO("\tfixup : %s", fr_table_str_by_value(cond_pass2_table, c->pass2_fixup, "")); - 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, ""),\ fr_table_str_by_value(fr_value_box_type_table, cast_type, ""));\ 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: diff --git a/src/lib/server/cond_eval.h b/src/lib/server/cond_eval.h index 259c1a5ef80..b509afaab1f 100644 --- a/src/lib/server/cond_eval.h +++ b/src/lib/server/cond_eval.h @@ -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 } diff --git a/src/lib/server/cond_tokenize.c b/src/lib/server/cond_tokenize.c index 94e4be7ba01..90f46437617 100644 --- a/src/lib/server/cond_tokenize.c +++ b/src/lib/server/cond_tokenize.c @@ -32,121 +32,125 @@ RCSID("$Id$") #include 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, ""), + fr_table_str_by_value(fr_value_box_type_table, tmpl_da(c->data.map->rhs)->type, "")); 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, ""), + fr_table_str_by_value(fr_value_box_type_table, tmpl_da(rhs)->type, "")); + 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. + * "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(®ex_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, ®ex_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; } /* - * "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, "")); + 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: diff --git a/src/lib/server/exec.c b/src/lib/server/exec.c index 5a31227435b..3cd99bcc7af 100644 --- a/src/lib/server/exec.c +++ b/src/lib/server/exec.c @@ -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); diff --git a/src/lib/server/log.c b/src/lib/server/log.c index 5896a4cfda2..f51c47bcc64 100644 --- a/src/lib/server/log.c +++ b/src/lib/server/log.c @@ -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); } diff --git a/src/lib/server/map.c b/src/lib/server/map.c index bf4e264b06b..39b48c02402 100644 --- a/src/lib/server/map.c +++ b/src/lib/server/map.c @@ -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, ""), 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, ""), rhs); break; diff --git a/src/lib/server/map.h b/src/lib/server/map.h index bae2350b01c..981123074d7 100644 --- a/src/lib/server/map.h +++ b/src/lib/server/map.h @@ -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)); diff --git a/src/lib/server/map_async.c b/src/lib/server/map_async.c index 415cc8ee57a..f7db4b1b060 100644 --- a/src/lib/server/map_async.c +++ b/src/lib/server/map_async.c @@ -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 */ diff --git a/src/lib/server/paircmp.c b/src/lib/server/paircmp.c index a85fd7cf6ae..d3db00af0cd 100644 --- a/src/lib/server/paircmp.c +++ b/src/lib/server/paircmp.c @@ -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; } /* diff --git a/src/lib/server/rcode.c b/src/lib/server/rcode.c index 3bf71a53a95..c236665d3e6 100644 --- a/src/lib/server/rcode.c +++ b/src/lib/server/rcode.c @@ -33,13 +33,13 @@ RCSID("$Id$") #include 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 }, }; diff --git a/src/lib/server/state.c b/src/lib/server/state.c index a53f96c0878..3321bf6691c 100644 --- a/src/lib/server/state.c +++ b/src/lib/server/state.c @@ -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); diff --git a/src/lib/server/tmpl.c b/src/lib/server/tmpl.c index c65cf4b5ade..9442687b562 100644 --- a/src/lib/server/tmpl.c +++ b/src/lib/server/tmpl.c @@ -35,31 +35,45 @@ RCSID("$Id$") #include #include +#include + #include /** 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, "")); + return; + } + + INFO("tmpl_t %s (%.8x) \"%pV\" (%p)", + fr_table_str_by_value(tmpl_type_table, vpt->type, ""), + 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, "")); + + 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, ""), rr->request); + i++; + } + i = 0; + + INFO("list: %s", fr_table_str_by_value(pair_list_table, vpt->data.attribute.list, "")); + + 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, ""), + 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, ""), + fr_table_str_by_value(fr_value_box_type_table, ar->da->type, ""), + 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, ""), + 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, ""), 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, ""), + 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, "")); + 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), "")); + 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, "")); - return; - } - - INFO("tmpl_t %s %s (%p)", fr_table_str_by_value(tmpl_type_table, vpt->type, ""), 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, ""), 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, ""), - 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, "")); - 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, ""), - fr_table_str_by_value(fr_value_box_type_table, type, "")); - 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, "")); - - /* - * 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 + * + * . + */ + 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, "")); + 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, "")); + /* 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, "")); 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, "")); + 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, ""), + fr_table_str_by_value(fr_value_box_type_table, type, "")); + 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, ""), + fr_table_str_by_value(fr_value_box_type_table, da->type, "")); + 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, ""); + 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), ""); + 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, "")); + 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, ""), - fr_table_str_by_value(fr_value_box_type_table, da->type, "")); - 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 ¤t. - */ - 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 ¤t. - */ - 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) { /* / + + / + \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; /* / + + / + \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. */ diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index 0b0dca32142..18b6d4aad52 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -32,13 +32,13 @@ * 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 #include @@ -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, diff --git a/src/lib/server/trigger.c b/src/lib/server/trigger.c index b48ac44ab4e..0f2330877d4 100644 --- a/src/lib/server/trigger.c +++ b/src/lib/server/trigger.c @@ -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; diff --git a/src/lib/server/virtual_servers.c b/src/lib/server/virtual_servers.c index 7b468396348..ee5003d37d6 100644 --- a/src/lib/server/virtual_servers.c +++ b/src/lib/server/virtual_servers.c @@ -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: diff --git a/src/lib/server/virtual_servers.h b/src/lib/server/virtual_servers.h index 15564bdd400..8cea0e7f606 100644 --- a/src/lib/server/virtual_servers.h +++ b/src/lib/server/virtual_servers.h @@ -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); diff --git a/src/lib/sim/id.h b/src/lib/sim/id.h index 0060592befe..33143382c1e 100644 --- a/src/lib/sim/id.h +++ b/src/lib/sim/id.h @@ -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]); diff --git a/src/lib/soh/soh.c b/src/lib/soh/soh.c index ef3e7662293..b5e3a07ddab 100644 --- a/src/lib/soh/soh.c +++ b/src/lib/soh/soh.c @@ -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; diff --git a/src/lib/tls/cache.c b/src/lib/tls/cache.c index 93ad0febae7..368baa18be3 100644 --- a/src/lib/tls/cache.c +++ b/src/lib/tls/cache.c @@ -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: diff --git a/src/lib/tls/ocsp.c b/src/lib/tls/ocsp.c index b39845eb68e..c97ca2a87e2 100644 --- a/src/lib/tls/ocsp.c +++ b/src/lib/tls/ocsp.c @@ -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; diff --git a/src/lib/tls/session.c b/src/lib/tls/session.c index 58903e36c38..cd00fa7cc05 100644 --- a/src/lib/tls/session.c +++ b/src/lib/tls/session.c @@ -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; diff --git a/src/lib/unlang/base.c b/src/lib/unlang/base.c index bc295c61e5f..27529834218 100644 --- a/src/lib/unlang/base.c +++ b/src/lib/unlang/base.c @@ -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; } diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index c455145e576..d6403d4cb74 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -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, "")); 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; diff --git a/src/lib/unlang/foreach.c b/src/lib/unlang/foreach.c index e84ef80cd20..d393552a6b2 100644 --- a/src/lib/unlang/foreach.c +++ b/src/lib/unlang/foreach.c @@ -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) diff --git a/src/lib/unlang/map.c b/src/lib/unlang/map.c index 69828501e5e..bd2c2335971 100644 --- a/src/lib/unlang/map.c +++ b/src/lib/unlang/map.c @@ -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; diff --git a/src/lib/unlang/subrequest.c b/src/lib/unlang/subrequest.c index 267732ab9e4..23be11fa34f 100644 --- a/src/lib/unlang/subrequest.c +++ b/src/lib/unlang/subrequest.c @@ -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; diff --git a/src/lib/unlang/switch.c b/src/lib/unlang/switch.c index df5839ecbb8..85ff2c0c938 100644 --- a/src/lib/unlang/switch.c +++ b/src/lib/unlang/switch.c @@ -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 = ↦ @@ -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" diff --git a/src/lib/unlang/tmpl.c b/src/lib/unlang/tmpl.c index f72d89fae38..c98201f85f9 100644 --- a/src/lib/unlang/tmpl.c +++ b/src/lib/unlang/tmpl.c @@ -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){ diff --git a/src/lib/unlang/unlang_priv.h b/src/lib/unlang/unlang_priv.h index e76e5ebc08b..712bd73c916 100644 --- a/src/lib/unlang/unlang_priv.h +++ b/src/lib/unlang/unlang_priv.h @@ -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 diff --git a/src/lib/unlang/xlat.c b/src/lib/unlang/xlat.c index 55aa7ab171b..d29e712a611 100644 --- a/src/lib/unlang/xlat.c +++ b/src/lib/unlang/xlat.c @@ -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; diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 41b69b308db..2dfa6c045af 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -51,6 +51,7 @@ typedef struct xlat_exp xlat_exp_t; #include #include #include +#include /** 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); diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index b8ae4ecb735..cbd4a42dca3 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -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), ""), - vp->da->name, - vp->tag, - fr_table_str_by_value(fr_tokens_table, vp->op, ""), - &vp->data); - } else { - RIDEBUG2("&%s:%s %s %pV", - fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""), - vp->da->name, - fr_table_str_by_value(fr_tokens_table, vp->op, ""), - &vp->data); - } + RIDEBUG2("&%s:%s %s %pV", + fr_table_str_by_value(pair_list_table, tmpl_list(vpt), ""), + vp->da->name, + fr_table_str_by_value(fr_tokens_table, vp->op, ""), + &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, "")); + 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, "")); 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:[*]}" == ", , , ..." +"%{concat:, %{request[*]}" == ", , , ..." @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); diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index 9c3befa376a..950f5aa6871 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -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; diff --git a/src/lib/unlang/xlat_inst.c b/src/lib/unlang/xlat_inst.c index 18a69e1c2a2..48e4ba20ddc 100644 --- a/src/lib/unlang/xlat_inst.c +++ b/src/lib/unlang/xlat_inst.c @@ -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. diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 24b545576c5..6dd1c59402f 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -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 { diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index ad5e768f5d5..6760e08542c 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -20,13 +20,15 @@ * @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 +#include #include #include #include @@ -34,161 +36,231 @@ RCSID("$Id$") #include #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 %{} @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 %{:= 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[]}' */ + L("}") /* First special token is a '}' i.e. '%{}' */ + ); + + 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 %{} 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[]}' */ - - if (*q == '}') break; /* First special token is a '}' i.e. '%{}' */ + /* + * 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 %{::} + * Hint token is a ':' it's an xlat function %{: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, "e, 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; +} diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 2ac95274577..96d26168953 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -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. diff --git a/src/lib/util/dict_unknown.c b/src/lib/util/dict_unknown.c index 330a509cc2a..c7d87370c0a 100644 --- a/src/lib/util/dict_unknown.c +++ b/src/lib/util/dict_unknown.c @@ -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__); diff --git a/src/lib/util/dict_util.c b/src/lib/util/dict_util.c index 33036518034..da919d22c4d 100644 --- a/src/lib/util/dict_util.c +++ b/src/lib/util/dict_util.c @@ -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 diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index 3fb7e4746d6..c557b6d2392 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -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 \ diff --git a/src/lib/util/pair.c b/src/lib/util/pair.c index 5d5aa8d1bde..71dc37cb75c 100644 --- a/src/lib/util/pair.c +++ b/src/lib/util/pair.c @@ -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, ""); -} - -/** Print one attribute and value to a string - * - * Print a VALUE_PAIR in the format: -@verbatim - [:tag] -@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 = ""; - } - - 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 - [:tag] -@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 = ""; - } - - 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 diff --git a/src/lib/util/pair.h b/src/lib/util/pair.h index 7592f2d0026..87206e14c2f 100644 --- a/src/lib/util/pair.h +++ b/src/lib/util/pair.h @@ -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 { diff --git a/src/lib/util/pair_cursor.c b/src/lib/util/pair_cursor.c index 1304dcc5310..0f0d066745f 100644 --- a/src/lib/util/pair_cursor.c +++ b/src/lib/util/pair_cursor.c @@ -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); diff --git a/src/lib/util/pair_cursor.h b/src/lib/util/pair_cursor.h index 133495d9307..1f928ca4ca8 100644 --- a/src/lib/util/pair_cursor.h +++ b/src/lib/util/pair_cursor.h @@ -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); diff --git a/src/lib/util/pair_legacy.c b/src/lib/util/pair_legacy.c index aa73737c2ca..f3c3e83d4cb 100644 --- a/src/lib/util/pair_legacy.c +++ b/src/lib/util/pair_legacy.c @@ -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; } diff --git a/src/lib/util/pair_print.c b/src/lib/util/pair_print.c index 88fb5702e2a..4538ea743d2 100644 --- a/src/lib/util/pair_print.c +++ b/src/lib/util/pair_print.c @@ -20,76 +20,95 @@ * * @copyright 2020 The FreeRADIUS server project */ +#include +#include -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 + * = ["]["] + */ + 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, ""); + 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 - [:tag] + [:tag] [q][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 = ""; } - 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 - [:tag] -@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 = ""; - } - - 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. - * - * @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 = ""; - } - - fr_value_box_snprint(out, ) -} - - diff --git a/src/lib/util/print.c b/src/lib/util/print.c index 2e7eb251ec9..93f7ce17d5c 100644 --- a/src/lib/util/print.c +++ b/src/lib/util/print.c @@ -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; diff --git a/src/lib/util/regex.c b/src/lib/util/regex.c index 910f33f8655..dd9f024519b 100644 --- a/src/lib/util/regex.c +++ b/src/lib/util/regex.c @@ -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 diff --git a/src/lib/util/regex.h b/src/lib/util/regex.h index ca3f9dbdbe5..73e3b40ed81 100644 --- a/src/lib/util/regex.h +++ b/src/lib/util/regex.h @@ -29,6 +29,7 @@ extern "C" { #include #include +#include #include #include @@ -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); diff --git a/src/lib/util/sbuff.c b/src/lib/util/sbuff.c index cf01fd73dea..4a92231a0c7 100644 --- a/src/lib/util/sbuff.c +++ b/src/lib/util/sbuff.c @@ -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); diff --git a/src/lib/util/sbuff.h b/src/lib/util/sbuff.h index f18453db530..340ade1b908 100644 --- a/src/lib/util/sbuff.h +++ b/src/lib/util/sbuff.h @@ -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, ...) \ diff --git a/src/lib/util/token.c b/src/lib/util/token.c index 2b0a7a9fab4..1687c174edb 100644 --- a/src/lib/util/token.c +++ b/src/lib/util/token.c @@ -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 } }; diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 8acb43e7c3f..d12fd46bc87 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -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) */ -} diff --git a/src/lib/util/value.h b/src/lib/util/value.h index fb40b5ade17..5152138896b 100644 --- a/src/lib/util/value.h +++ b/src/lib/util/value.h @@ -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 * diff --git a/src/modules/proto_arp/proto_arp_process.c b/src/modules/proto_arp/proto_arp_process.c index 9eafaab08fd..d1f331bc0cc 100644 --- a/src/modules/proto_arp/proto_arp_process.c +++ b/src/modules/proto_arp/proto_arp_process.c @@ -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) { diff --git a/src/modules/proto_detail/proto_detail_process.c b/src/modules/proto_detail/proto_detail_process.c index 8bfd33b88ee..39d9b4ad837 100644 --- a/src/modules/proto_detail/proto_detail_process.c +++ b/src/modules/proto_detail/proto_detail_process.c @@ -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) { diff --git a/src/modules/proto_dhcpv4/dhcpclient.c b/src/modules/proto_dhcpv4/dhcpclient.c index f12f603a145..104d4b77516 100644 --- a/src/modules/proto_dhcpv4/dhcpclient.c +++ b/src/modules/proto_dhcpv4/dhcpclient.c @@ -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); } } diff --git a/src/modules/proto_dhcpv4/proto_dhcpv4_process.c b/src/modules/proto_dhcpv4/proto_dhcpv4_process.c index d216efcaf86..cb13b7241a4 100644 --- a/src/modules/proto_dhcpv4/proto_dhcpv4_process.c +++ b/src/modules/proto_dhcpv4/proto_dhcpv4_process.c @@ -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]); diff --git a/src/modules/proto_dhcpv6/proto_dhcpv6_process.c b/src/modules/proto_dhcpv6/proto_dhcpv6_process.c index f354b22cf93..93f6897b2c3 100644 --- a/src/modules/proto_dhcpv6/proto_dhcpv6_process.c +++ b/src/modules/proto_dhcpv6/proto_dhcpv6_process.c @@ -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) { diff --git a/src/modules/proto_ldap_sync/proto_ldap_sync.c b/src/modules/proto_ldap_sync/proto_ldap_sync.c index bbdaa3dd248..12c6900afb4 100644 --- a/src/modules/proto_ldap_sync/proto_ldap_sync.c +++ b/src/modules/proto_ldap_sync/proto_ldap_sync.c @@ -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"); diff --git a/src/modules/proto_radius/proto_radius_acct.c b/src/modules/proto_radius/proto_radius_acct.c index 58eea42abd3..f1b5a24f151 100644 --- a/src/modules/proto_radius/proto_radius_acct.c +++ b/src/modules/proto_radius/proto_radius_acct.c @@ -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)); diff --git a/src/modules/proto_radius/proto_radius_auth.c b/src/modules/proto_radius/proto_radius_auth.c index 45d7e949078..34827486b57 100644 --- a/src/modules/proto_radius/proto_radius_auth.c +++ b/src/modules/proto_radius/proto_radius_auth.c @@ -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), "", 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 = ""; } - } 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 = ""; } } @@ -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 { diff --git a/src/modules/proto_radius/proto_radius_coa.c b/src/modules/proto_radius/proto_radius_coa.c index 32e4eb31ee1..7b65c4b11ad 100644 --- a/src/modules/proto_radius/proto_radius_coa.c +++ b/src/modules/proto_radius/proto_radius_coa.c @@ -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: diff --git a/src/modules/proto_radius/proto_radius_dynamic_client.c b/src/modules/proto_radius/proto_radius_dynamic_client.c index 60a60e23a39..51bcf1a4186 100644 --- a/src/modules/proto_radius/proto_radius_dynamic_client.c +++ b/src/modules/proto_radius/proto_radius_dynamic_client.c @@ -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; diff --git a/src/modules/proto_radius/proto_radius_load.c b/src/modules/proto_radius/proto_radius_load.c index 5fac929bde3..83723a568bc 100644 --- a/src/modules/proto_radius/proto_radius_load.c +++ b/src/modules/proto_radius/proto_radius_load.c @@ -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; /* diff --git a/src/modules/proto_radius/proto_radius_status.c b/src/modules/proto_radius/proto_radius_status.c index c7d66fb2533..7396cfe38bd 100644 --- a/src/modules/proto_radius/proto_radius_status.c +++ b/src/modules/proto_radius/proto_radius_status.c @@ -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)); diff --git a/src/modules/proto_tacacs/proto_tacacs.c b/src/modules/proto_tacacs/proto_tacacs.c index d4493f5fd2e..f85a896d8a2 100644 --- a/src/modules/proto_tacacs/proto_tacacs.c +++ b/src/modules/proto_tacacs/proto_tacacs.c @@ -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), diff --git a/src/modules/proto_tacacs/proto_tacacs_acct.c b/src/modules/proto_tacacs/proto_tacacs_acct.c index 1b5a4c83fdd..f95b45b41ae 100644 --- a/src/modules/proto_tacacs/proto_tacacs_acct.c +++ b/src/modules/proto_tacacs/proto_tacacs_acct.c @@ -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); diff --git a/src/modules/proto_tacacs/proto_tacacs_auth.c b/src/modules/proto_tacacs/proto_tacacs_auth.c index 0e14e2b534e..d5856c9af12 100644 --- a/src/modules/proto_tacacs/proto_tacacs_auth.c +++ b/src/modules/proto_tacacs/proto_tacacs_auth.c @@ -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; } diff --git a/src/modules/proto_tacacs/proto_tacacs_autz.c b/src/modules/proto_tacacs/proto_tacacs_autz.c index 5c91dda2966..db0ff5c429b 100644 --- a/src/modules/proto_tacacs/proto_tacacs_autz.c +++ b/src/modules/proto_tacacs/proto_tacacs_autz.c @@ -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"); } diff --git a/src/modules/proto_vmps/proto_vmps_dynamic_client.c b/src/modules/proto_vmps/proto_vmps_dynamic_client.c index 84ee342873d..09d4c23805f 100644 --- a/src/modules/proto_vmps/proto_vmps_dynamic_client.c +++ b/src/modules/proto_vmps/proto_vmps_dynamic_client.c @@ -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; diff --git a/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c b/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c index 4680a7dfc45..96bc8db6ddf 100644 --- a/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c +++ b/src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c @@ -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, "", 6, T_BARE_WORD); + tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "", 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, "", 6, T_BARE_WORD); + tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "", 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 */ diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index 604223675e0..39784f3019e 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -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; } diff --git a/src/modules/rlm_cache/serialize.c b/src/modules/rlm_cache/serialize.c index 9d841d25d45..5acefb54eaf 100644 --- a/src/modules/rlm_cache/serialize.c +++ b/src/modules/rlm_cache/serialize.c @@ -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'); diff --git a/src/modules/rlm_chap/rlm_chap.c b/src/modules/rlm_chap/rlm_chap.c index db69e5a8264..c0c2ea7e4e8 100644 --- a/src/modules/rlm_chap/rlm_chap.c +++ b/src/modules/rlm_chap/rlm_chap.c @@ -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; diff --git a/src/modules/rlm_couchbase/mod.c b/src/modules/rlm_couchbase/mod.c index 4bcafa50d75..ecbb628e8e2 100644 --- a/src/modules/rlm_couchbase/mod.c +++ b/src/modules/rlm_couchbase/mod.c @@ -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 */ diff --git a/src/modules/rlm_couchbase/rlm_couchbase.c b/src/modules/rlm_couchbase/rlm_couchbase.c index 00e5d191cd7..e6c41d1cf39 100644 --- a/src/modules/rlm_couchbase/rlm_couchbase.c +++ b/src/modules/rlm_couchbase/rlm_couchbase.c @@ -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)); diff --git a/src/modules/rlm_csv/rlm_csv.c b/src/modules/rlm_csv/rlm_csv.c index dd334a03f99..6e6592bd6b0 100644 --- a/src/modules/rlm_csv/rlm_csv.c +++ b/src/modules/rlm_csv/rlm_csv.c @@ -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; diff --git a/src/modules/rlm_digest/rlm_digest.c b/src/modules/rlm_digest/rlm_digest.c index 1b078c46013..c86fb42a4ae 100644 --- a/src/modules/rlm_digest/rlm_digest.c +++ b/src/modules/rlm_digest/rlm_digest.c @@ -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; diff --git a/src/modules/rlm_eap/rlm_eap.c b/src/modules/rlm_eap/rlm_eap.c index c5d9dc61826..34d2a27b2e4 100644 --- a/src/modules/rlm_eap/rlm_eap.c +++ b/src/modules/rlm_eap/rlm_eap.c @@ -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; } diff --git a/src/modules/rlm_eap/types/rlm_eap_fast/eap_fast.c b/src/modules/rlm_eap/types/rlm_eap_fast/eap_fast.c index 2e046fc09f7..d63be6487c0 100644 --- a/src/modules/rlm_eap/types/rlm_eap_fast/eap_fast.c +++ b/src/modules/rlm_eap/types/rlm_eap_fast/eap_fast.c @@ -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; diff --git a/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c b/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c index c2c016cf169..d35e0998623 100644 --- a/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c +++ b/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c @@ -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 { diff --git a/src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c b/src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c index 4f94ebb2c4c..837538108fb 100644 --- a/src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c +++ b/src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c @@ -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; diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c index 17b1cf293a2..2b55ccc099a 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c +++ b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c @@ -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 { diff --git a/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c b/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c index 6803b8bf936..048c5e67993 100644 --- a/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c +++ b/src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c @@ -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 diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 6f691f9afc4..9253448971a 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -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 { diff --git a/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c b/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c index b6edc9a1c2a..009fcf87f4d 100644 --- a/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c +++ b/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c @@ -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 { diff --git a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c index 4607e7585c5..66bef2d01b2 100644 --- a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c +++ b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c @@ -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) && diff --git a/src/modules/rlm_exec/rlm_exec.c b/src/modules/rlm_exec/rlm_exec.c index 1fdbe756ba7..6341b8f5d4e 100644 --- a/src/modules/rlm_exec/rlm_exec.c +++ b/src/modules/rlm_exec/rlm_exec.c @@ -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; diff --git a/src/modules/rlm_expiration/rlm_expiration.c b/src/modules/rlm_expiration/rlm_expiration.c index 22afa48259f..a780c0ed631 100644 --- a/src/modules/rlm_expiration/rlm_expiration.c +++ b/src/modules/rlm_expiration/rlm_expiration.c @@ -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; diff --git a/src/modules/rlm_expr/rlm_expr.c b/src/modules/rlm_expr/rlm_expr.c index 146858701be..e372e6a6ae9 100644 --- a/src/modules/rlm_expr/rlm_expr.c +++ b/src/modules/rlm_expr/rlm_expr.c @@ -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; diff --git a/src/modules/rlm_files/rlm_files.c b/src/modules/rlm_files/rlm_files.c index 01bfebf01ea..c0c114767c7 100644 --- a/src/modules/rlm_files/rlm_files.c +++ b/src/modules/rlm_files/rlm_files.c @@ -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; } diff --git a/src/modules/rlm_imap/rlm_imap.c b/src/modules/rlm_imap/rlm_imap.c index 0a6f06d463e..cafdac06689 100644 --- a/src/modules/rlm_imap/rlm_imap.c +++ b/src/modules/rlm_imap/rlm_imap.c @@ -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"); diff --git a/src/modules/rlm_isc_dhcp/rlm_isc_dhcp.c b/src/modules/rlm_isc_dhcp/rlm_isc_dhcp.c index c77b524db77..270fcf1051b 100644 --- a/src/modules/rlm_isc_dhcp/rlm_isc_dhcp.c +++ b/src/modules/rlm_isc_dhcp/rlm_isc_dhcp.c @@ -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; /* diff --git a/src/modules/rlm_json/rlm_json.c b/src/modules/rlm_json/rlm_json.c index 5b6137fc662..1fa233be97b 100644 --- a/src/modules/rlm_json/rlm_json.c +++ b/src/modules/rlm_json/rlm_json.c @@ -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"); diff --git a/src/modules/rlm_krb5/rlm_krb5.c b/src/modules/rlm_krb5/rlm_krb5.c index 0069c309b22..82107034158 100644 --- a/src/modules/rlm_krb5/rlm_krb5.c +++ b/src/modules/rlm_krb5/rlm_krb5.c @@ -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"); diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index e568b1b3f9d..5840da6fb83 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -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 diff --git a/src/modules/rlm_ldap/user.c b/src/modules/rlm_ldap/user.c index d4f0cac0135..dedd9b0cbb4 100644 --- a/src/modules/rlm_ldap/user.c +++ b/src/modules/rlm_ldap/user.c @@ -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"); diff --git a/src/modules/rlm_linelog/rlm_linelog.c b/src/modules/rlm_linelog/rlm_linelog.c index c545860dd14..2651e844e2f 100644 --- a/src/modules/rlm_linelog/rlm_linelog.c +++ b/src/modules/rlm_linelog/rlm_linelog.c @@ -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; } diff --git a/src/modules/rlm_logintime/rlm_logintime.c b/src/modules/rlm_logintime/rlm_logintime.c index 8656bddf0f1..b84d759daf9 100644 --- a/src/modules/rlm_logintime/rlm_logintime.c +++ b/src/modules/rlm_logintime/rlm_logintime.c @@ -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; /* diff --git a/src/modules/rlm_lua/lua.c b/src/modules/rlm_lua/lua.c index 2430fd53d0f..8fb2d1cc6a3 100644 --- a/src/modules/rlm_lua/lua.c +++ b/src/modules/rlm_lua/lua.c @@ -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, "")); 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); /* diff --git a/src/modules/rlm_mruby/rlm_mruby.c b/src/modules/rlm_mruby/rlm_mruby.c index a0312709b63..a32ae01fbce 100644 --- a/src/modules/rlm_mruby/rlm_mruby.c +++ b/src/modules/rlm_mruby/rlm_mruby.c @@ -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; diff --git a/src/modules/rlm_mschap/auth_wbclient.c b/src/modules/rlm_mschap/auth_wbclient.c index d77c8074f5c..fe417a948d9 100644 --- a/src/modules/rlm_mschap/auth_wbclient.c +++ b/src/modules/rlm_mschap/auth_wbclient.c @@ -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; diff --git a/src/modules/rlm_mschap/opendir.c b/src/modules/rlm_mschap/opendir.c index 42d37f714a6..db56bb72897 100644 --- a/src/modules/rlm_mschap/opendir.c +++ b/src/modules/rlm_mschap/opendir.c @@ -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; diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index dc54a995d48..aaf35eca6e7 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -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, diff --git a/src/modules/rlm_opendirectory/rlm_opendirectory.c b/src/modules/rlm_opendirectory/rlm_opendirectory.c index 6ed725d76c1..7db09e85e18 100644 --- a/src/modules/rlm_opendirectory/rlm_opendirectory.c +++ b/src/modules/rlm_opendirectory/rlm_opendirectory.c @@ -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; diff --git a/src/modules/rlm_pam/rlm_pam.c b/src/modules/rlm_pam/rlm_pam.c index c019c36ed4e..f9bd0efc672 100644 --- a/src/modules/rlm_pam/rlm_pam.c +++ b/src/modules/rlm_pam/rlm_pam.c @@ -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); diff --git a/src/modules/rlm_pap/rlm_pap.c b/src/modules/rlm_pap/rlm_pap.c index 67fdbb48a26..b2665bbe1bd 100644 --- a/src/modules/rlm_pap/rlm_pap.c +++ b/src/modules/rlm_pap/rlm_pap.c @@ -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; diff --git a/src/modules/rlm_passwd/rlm_passwd.c b/src/modules/rlm_passwd/rlm_passwd.c index ab15b6dcb4b..ee9013e5d64 100644 --- a/src/modules/rlm_passwd/rlm_passwd.c +++ b/src/modules/rlm_passwd/rlm_passwd.c @@ -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; diff --git a/src/modules/rlm_perl/rlm_perl.c b/src/modules/rlm_perl/rlm_perl.c index 24b15694f51..2a26bd1106d 100644 --- a/src/modules/rlm_perl/rlm_perl.c +++ b/src/modules/rlm_perl/rlm_perl.c @@ -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 - * :, 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 { diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c index 8685dbcb206..0695398a38b 100644 --- a/src/modules/rlm_python/rlm_python.c +++ b/src/modules/rlm_python/rlm_python.c @@ -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); diff --git a/src/modules/rlm_radius/rlm_radius.c b/src/modules/rlm_radius/rlm_radius.c index 8d9ad0e891a..59281e7ce54 100644 --- a/src/modules/rlm_radius/rlm_radius.c +++ b/src/modules/rlm_radius/rlm_radius.c @@ -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); } diff --git a/src/modules/rlm_radius/rlm_radius_udp.c b/src/modules/rlm_radius/rlm_radius_udp.c index 52825c14170..0bc820e8f65 100644 --- a/src/modules/rlm_radius/rlm_radius_udp.c +++ b/src/modules/rlm_radius/rlm_radius_udp.c @@ -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); } diff --git a/src/modules/rlm_radutmp/rlm_radutmp.c b/src/modules/rlm_radutmp/rlm_radutmp.c index 23dc6ccaeb3..40909c5ebee 100644 --- a/src/modules/rlm_radutmp/rlm_radutmp.c +++ b/src/modules/rlm_radutmp/rlm_radutmp.c @@ -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; diff --git a/src/modules/rlm_redis_ippool/redis_ippool.h b/src/modules/rlm_redis_ippool/redis_ippool.h index ea0e043eabd..33750339913 100644 --- a/src/modules/rlm_redis_ippool/redis_ippool.h +++ b/src/modules/rlm_redis_ippool/redis_ippool.h @@ -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; \ diff --git a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c index ffbffc0de76..4a135f5f956 100644 --- a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c +++ b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c @@ -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, "")); @@ -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); } diff --git a/src/modules/rlm_rediswho/rlm_rediswho.c b/src/modules/rlm_rediswho/rlm_rediswho.c index f557a78838c..db4f7b304e1 100644 --- a/src/modules/rlm_rediswho/rlm_rediswho.c +++ b/src/modules/rlm_rediswho/rlm_rediswho.c @@ -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; diff --git a/src/modules/rlm_rest/rest.c b/src/modules/rlm_rest/rest.c index 965b313866a..f922e31c53a 100644 --- a/src/modules/rlm_rest/rest.c +++ b/src/modules/rlm_rest/rest.c @@ -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(¤t, tmpl_request(dst)) < 0) { RWDEBUG("Attribute name refers to outer request but not in a tunnel (skipping)"); continue; diff --git a/src/modules/rlm_rest/rlm_rest.c b/src/modules/rlm_rest/rlm_rest.c index e44ce517c4b..ea14cd72b54 100644 --- a/src/modules/rlm_rest/rlm_rest.c +++ b/src/modules/rlm_rest/rlm_rest.c @@ -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 diff --git a/src/modules/rlm_securid/mem.c b/src/modules/rlm_securid/mem.c index 44b3dafe626..e0567dfab4c 100644 --- a/src/modules/rlm_securid/mem.c +++ b/src/modules/rlm_securid/mem.c @@ -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; } diff --git a/src/modules/rlm_securid/rlm_securid.c b/src/modules/rlm_securid/rlm_securid.c index 5ec19ed3f93..e9263216a27 100644 --- a/src/modules/rlm_securid/rlm_securid.c +++ b/src/modules/rlm_securid/rlm_securid.c @@ -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 diff --git a/src/modules/rlm_soh/rlm_soh.c b/src/modules/rlm_soh/rlm_soh.c index 26302f97cfb..1dd0c8586ab 100644 --- a/src/modules/rlm_soh/rlm_soh.c +++ b/src/modules/rlm_soh/rlm_soh.c @@ -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; diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index 2deda0112f7..2b726cccdf3 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -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 : diff --git a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c index e86a06fcdfd..3306b76dc37 100644 --- a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c +++ b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c @@ -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; } diff --git a/src/modules/rlm_sqlippool/rlm_sqlippool.c b/src/modules/rlm_sqlippool/rlm_sqlippool.c index e46812840cd..817b695c90a 100644 --- a/src/modules/rlm_sqlippool/rlm_sqlippool.c +++ b/src/modules/rlm_sqlippool/rlm_sqlippool.c @@ -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; diff --git a/src/modules/rlm_stats/rlm_stats.c b/src/modules/rlm_stats/rlm_stats.c index 6ddaa3627f6..6bb620f9e49 100644 --- a/src/modules/rlm_stats/rlm_stats.c +++ b/src/modules/rlm_stats/rlm_stats.c @@ -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; diff --git a/src/modules/rlm_unix/rlm_unix.c b/src/modules/rlm_unix/rlm_unix.c index 2c3775fd761..90b2dd5c54e 100644 --- a/src/modules/rlm_unix/rlm_unix.c +++ b/src/modules/rlm_unix/rlm_unix.c @@ -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); diff --git a/src/modules/rlm_unpack/rlm_unpack.c b/src/modules/rlm_unpack/rlm_unpack.c index 50254b2ffac..d99369986d1 100644 --- a/src/modules/rlm_unpack/rlm_unpack.c +++ b/src/modules/rlm_unpack/rlm_unpack.c @@ -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; } diff --git a/src/modules/rlm_wimax/rlm_wimax.c b/src/modules/rlm_wimax/rlm_wimax.c index bcdfa15d788..45dfb4ae21f 100644 --- a/src/modules/rlm_wimax/rlm_wimax.c +++ b/src/modules/rlm_wimax/rlm_wimax.c @@ -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"); } diff --git a/src/modules/rlm_winbind/rlm_winbind.c b/src/modules/rlm_winbind/rlm_winbind.c index 7f8cbc547cc..4f2d56db657 100644 --- a/src/modules/rlm_winbind/rlm_winbind.c +++ b/src/modules/rlm_winbind/rlm_winbind.c @@ -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 diff --git a/src/modules/rlm_yubikey/decrypt.c b/src/modules/rlm_yubikey/decrypt.c index 6e2218dd381..3a8e8bb32d1 100644 --- a/src/modules/rlm_yubikey/decrypt.c +++ b/src/modules/rlm_yubikey/decrypt.c @@ -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; diff --git a/src/modules/rlm_yubikey/rlm_yubikey.c b/src/modules/rlm_yubikey/rlm_yubikey.c index 660e24f1d19..c45ff06c1b3 100644 --- a/src/modules/rlm_yubikey/rlm_yubikey.c +++ b/src/modules/rlm_yubikey/rlm_yubikey.c @@ -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; diff --git a/src/protocols/dhcpv4/base.c b/src/protocols/dhcpv4/base.c index eefd9026268..e1e7b7d56b1 100644 --- a/src/protocols/dhcpv4/base.c +++ b/src/protocols/dhcpv4/base.c @@ -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. diff --git a/src/protocols/dhcpv4/packet.c b/src/protocols/dhcpv4/packet.c index 8d2348ce5cc..d8dd63ff6cc 100644 --- a/src/protocols/dhcpv4/packet.c +++ b/src/protocols/dhcpv4/packet.c @@ -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(); diff --git a/src/protocols/dhcpv4/raw.c b/src/protocols/dhcpv4/raw.c index e7dd9d3151c..b993ab7723f 100644 --- a/src/protocols/dhcpv4/raw.c +++ b/src/protocols/dhcpv4/raw.c @@ -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(ð_bcast, ð_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, ð_hdr->dst_addr, ETH_ADDR_LEN) != 0))) { /* No match. */ diff --git a/src/protocols/dhcpv6/base.c b/src/protocols/dhcpv6/base.c index 5068f648f9f..0238822e754 100644 --- a/src/protocols/dhcpv6/base.c +++ b/src/protocols/dhcpv6/base.c @@ -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. diff --git a/src/protocols/radius/decode.c b/src/protocols/radius/decode.c index d48e1886cb6..dea3aca6323 100644 --- a/src/protocols/radius/decode.c +++ b/src/protocols/radius/decode.c @@ -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: diff --git a/src/protocols/radius/encode.c b/src/protocols/radius/encode.c index 5f9a2fddfe8..c0f34836904 100644 --- a/src/protocols/radius/encode.c +++ b/src/protocols/radius/encode.c @@ -32,6 +32,9 @@ RCSID("$Id$") #include #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)) { diff --git a/src/protocols/tacacs/base.c b/src/protocols/tacacs/base.c index 6c5b5c58fa1..38f45dc7124 100644 --- a/src/protocols/tacacs/base.c +++ b/src/protocols/tacacs/base.c @@ -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", diff --git a/src/protocols/tacacs/encode.c b/src/protocols/tacacs/encode.c index 1bf3e77e748..805c69dcd1d 100644 --- a/src/protocols/tacacs/encode.c +++ b/src/protocols/tacacs/encode.c @@ -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(); } diff --git a/src/protocols/vmps/vmps.c b/src/protocols/vmps/vmps.c index a8b210a2e3b..77073aeb102 100644 --- a/src/protocols/vmps/vmps.c +++ b/src/protocols/vmps/vmps.c @@ -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 +}; diff --git a/src/tests/auth/chap b/src/tests/auth/chap index 302f394e22c..dcf36a7a43c 100644 --- a/src/tests/auth/chap +++ b/src/tests/auth/chap @@ -6,5 +6,5 @@ # Set CHAP-Password # update request { - &CHAP-Password := "%{chap_password:%{request:CHAP-Password}}" + &CHAP-Password := "%{chap_password:%{request.CHAP-Password}}" } diff --git a/src/tests/auth/chap_header b/src/tests/auth/chap_header index e70ee15ff16..3321c16e332 100644 --- a/src/tests/auth/chap_header +++ b/src/tests/auth/chap_header @@ -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}}" } diff --git a/src/tests/keywords/concat b/src/tests/keywords/concat index 8481a55aa62..d1cd000cc32 100644 --- a/src/tests/keywords/concat +++ b/src/tests/keywords/concat @@ -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[*]}}" } diff --git a/src/tests/keywords/escape b/src/tests/keywords/escape index 6eb186cec48..b11fd23ef18 100644 --- a/src/tests/keywords/escape +++ b/src/tests/keywords/escape @@ -14,35 +14,35 @@ update request { &Tmp-String-7 := 'Ã…Â’=C4=8Cÿ' } -if ("%{escape:%{request:Tmp-String-0}}" != &Tmp-String-0) { +if ("%{escape:%{Tmp-String-0}}" != &Tmp-String-0) { test_fail } -if ("%{escape:%{request:Tmp-String-1}}" != &Tmp-String-3) { +if ("%{escape:%{Tmp-String-1}}" != &Tmp-String-3) { test_fail } -if ("%{escape:%{request:Tmp-String-2}}" != &Tmp-String-4) { +if ("%{escape:%{Tmp-String-2}}" != &Tmp-String-4) { test_fail } -if ("%{unescape:%{request:Tmp-String-0}}" != &Tmp-String-0) { +if ("%{unescape:%{Tmp-String-0}}" != &Tmp-String-0) { test_fail } -if ("%{unescape:%{request:Tmp-String-3}}" != "%{Tmp-String-1}") { +if ("%{unescape:%{Tmp-String-3}}" != "%{Tmp-String-1}") { test_fail } -if ("%{unescape:%{request:Tmp-String-4}}" != &Tmp-String-2) { +if ("%{unescape:%{Tmp-String-4}}" != &Tmp-String-2) { test_fail } -if ("%{escape:%{request:Tmp-String-6}}" != &Tmp-String-7) { +if ("%{escape:%{Tmp-String-6}}" != &Tmp-String-7) { test_fail } -if ("%{unescape:%{request:Tmp-String-7}}" != &Tmp-String-6) { +if ("%{unescape:%{Tmp-String-7}}" != &Tmp-String-6) { test_fail } diff --git a/src/tests/keywords/if-multivalue b/src/tests/keywords/if-multivalue index 362a2242670..dfffe7b17e9 100644 --- a/src/tests/keywords/if-multivalue +++ b/src/tests/keywords/if-multivalue @@ -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 } diff --git a/src/tests/keywords/ipaddr.raw b/src/tests/keywords/ipaddr.raw index 996512a0e09..cee2384dd5c 100644 --- a/src/tests/keywords/ipaddr.raw +++ b/src/tests/keywords/ipaddr.raw @@ -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" } } } diff --git a/src/tests/keywords/ipprefix b/src/tests/keywords/ipprefix index 19e0a7d66ab..5b851abf71e 100644 --- a/src/tests/keywords/ipprefix +++ b/src/tests/keywords/ipprefix @@ -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 } diff --git a/src/tests/keywords/md4 b/src/tests/keywords/md4 index 170cf74b5cd..1793b896930 100644 --- a/src/tests/keywords/md4 +++ b/src/tests/keywords/md4 @@ -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}}" } diff --git a/src/tests/keywords/md5 b/src/tests/keywords/md5 index dc7eb545816..039f278be1f 100644 --- a/src/tests/keywords/md5 +++ b/src/tests/keywords/md5 @@ -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}}" diff --git a/src/tests/keywords/pairs b/src/tests/keywords/pairs index f7f3cc4f492..c28de33ad8f 100644 --- a/src/tests/keywords/pairs +++ b/src/tests/keywords/pairs @@ -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 } diff --git a/src/tests/keywords/pap b/src/tests/keywords/pap index c13ae96cfed..5aa003f3aa3 100644 --- a/src/tests/keywords/pap +++ b/src/tests/keywords/pap @@ -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 diff --git a/src/tests/keywords/pap-ssha2 b/src/tests/keywords/pap-ssha2 index eb2651bdcb8..23301247aac 100644 --- a/src/tests/keywords/pap-ssha2 +++ b/src/tests/keywords/pap-ssha2 @@ -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 diff --git a/src/tests/keywords/parallel b/src/tests/keywords/parallel index 6d1bb6348d9..fe424019f87 100644 --- a/src/tests/keywords/parallel +++ b/src/tests/keywords/parallel @@ -16,7 +16,7 @@ parallel { } } -if ("%{control:Tmp-Integer-0[#]}" != 4) { +if ("%{control.Tmp-Integer-0[#]}" != 4) { test_fail return diff --git a/src/tests/keywords/sha1 b/src/tests/keywords/sha1 index ccbfaf3c1ea..e4968f4174a 100644 --- a/src/tests/keywords/sha1 +++ b/src/tests/keywords/sha1 @@ -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} }" diff --git a/src/tests/keywords/sha2 b/src/tests/keywords/sha2 index b73a7aac616..aef16ffb8f9 100644 --- a/src/tests/keywords/sha2 +++ b/src/tests/keywords/sha2 @@ -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) { diff --git a/src/tests/keywords/switch-escape b/src/tests/keywords/switch-escape index 4aca8817611..8e2b1d0fed4 100644 --- a/src/tests/keywords/switch-escape +++ b/src/tests/keywords/switch-escape @@ -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 index a45de776672..00000000000 --- a/src/tests/keywords/switch-value-error2 +++ /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 - } - -} diff --git a/src/tests/keywords/unknown b/src/tests/keywords/unknown index 46a21ff7514..1eafcadaebd 100644 --- a/src/tests/keywords/unknown +++ b/src/tests/keywords/unknown @@ -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 index 14250101722..00000000000 --- a/src/tests/keywords/update-add-ref-index +++ /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 index 282635232ac..00000000000 --- a/src/tests/keywords/update-add-ref-tag +++ /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 diff --git a/src/tests/keywords/update-index b/src/tests/keywords/update-index index 9684c21da1c..d86edd49112 100644 --- a/src/tests/keywords/update-index +++ b/src/tests/keywords/update-index @@ -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 } diff --git a/src/tests/keywords/update-remove-any b/src/tests/keywords/update-remove-any index ff0a97cc1e0..f026bfecf50 100644 --- a/src/tests/keywords/update-remove-any +++ b/src/tests/keywords/update-remove-any @@ -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 index 31ee13f22c3..00000000000 --- a/src/tests/keywords/update-remove-tag +++ /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 diff --git a/src/tests/keywords/update-remove-value b/src/tests/keywords/update-remove-value index 7c203d65713..882cd57684d 100644 --- a/src/tests/keywords/update-remove-value +++ b/src/tests/keywords/update-remove-value @@ -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 index 60a96bf6573..00000000000 --- a/src/tests/keywords/update-tag +++ /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 diff --git a/src/tests/keywords/update-unknown b/src/tests/keywords/update-unknown index 4d372b28f9c..ad6cf329447 100644 --- a/src/tests/keywords/update-unknown +++ b/src/tests/keywords/update-unknown @@ -10,20 +10,20 @@ # 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 } diff --git a/src/tests/keywords/update-xlat b/src/tests/keywords/update-xlat index 0077e700d2d..4ae02fda4ff 100644 --- a/src/tests/keywords/update-xlat +++ b/src/tests/keywords/update-xlat @@ -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') { diff --git a/src/tests/keywords/urlquote b/src/tests/keywords/urlquote index 461dacffdef..3dfebec2963 100644 --- a/src/tests/keywords/urlquote +++ b/src/tests/keywords/urlquote @@ -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}" } diff --git a/src/tests/keywords/vendor_specific.raw b/src/tests/keywords/vendor_specific.raw index 6491cdb2039..db596222e38 100644 --- a/src/tests/keywords/vendor_specific.raw +++ b/src/tests/keywords/vendor_specific.raw @@ -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 { diff --git a/src/tests/keywords/virtual b/src/tests/keywords/virtual index 4e785118ac2..bd421b14f71 100644 --- a/src/tests/keywords/virtual +++ b/src/tests/keywords/virtual @@ -1,6 +1,6 @@ # # PRE: update if # -if (&request:Packet-Type == Access-Request) { +if (&Packet-Type == Access-Request) { success } diff --git a/src/tests/keywords/wimax b/src/tests/keywords/wimax index f8fc1903181..f424c1c1334 100644 --- a/src/tests/keywords/wimax +++ b/src/tests/keywords/wimax @@ -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 index db76a137681..00000000000 --- a/src/tests/keywords/xlat-attr-tag +++ /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 diff --git a/src/tests/keywords/xlat-error b/src/tests/keywords/xlat-error index c7dc6d75700..b9d97ae5029 100644 --- a/src/tests/keywords/xlat-error +++ b/src/tests/keywords/xlat-error @@ -1,5 +1,5 @@ # -# PRE: if xlat-attr-tag +# PRE: if # # diff --git a/src/tests/keywords/xlat-exec b/src/tests/keywords/xlat-exec index 42c06f66b17..8fc6e7be11b 100644 --- a/src/tests/keywords/xlat-exec +++ b/src/tests/keywords/xlat-exec @@ -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 } diff --git a/src/tests/keywords/xlat-explode b/src/tests/keywords/xlat-explode index 3f2e7612d6d..b450b50e1c0 100644 --- a/src/tests/keywords/xlat-explode +++ b/src/tests/keywords/xlat-explode @@ -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 } diff --git a/src/tests/keywords/xlat-inline b/src/tests/keywords/xlat-inline index 1b25cee85b4..fd725c14532 100644 --- a/src/tests/keywords/xlat-inline +++ b/src/tests/keywords/xlat-inline @@ -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}" diff --git a/src/tests/keywords/xlat-list b/src/tests/keywords/xlat-list index c7d03cfb2ce..b0b2d55a866 100644 --- a/src/tests/keywords/xlat-list +++ b/src/tests/keywords/xlat-list @@ -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') { diff --git a/src/tests/keywords/xlat-virtual-attr b/src/tests/keywords/xlat-virtual-attr index 259e03c01dc..1e781e51810 100644 --- a/src/tests/keywords/xlat-virtual-attr +++ b/src/tests/keywords/xlat-virtual-attr @@ -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 } diff --git a/src/tests/modules/cache_rbtree/cache-update.unlang b/src/tests/modules/cache_rbtree/cache-update.unlang index 253501fcf7a..bbe8c931a7d 100644 --- a/src/tests/modules/cache_rbtree/cache-update.unlang +++ b/src/tests/modules/cache_rbtree/cache-update.unlang @@ -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 diff --git a/src/tests/modules/cache_rbtree/module.conf b/src/tests/modules/cache_rbtree/module.conf index 3da68b88980..c0501252c1d 100644 --- a/src/tests/modules/cache_rbtree/module.conf +++ b/src/tests/modules/cache_rbtree/module.conf @@ -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' diff --git a/src/tests/modules/chap/chap_password.unlang b/src/tests/modules/chap/chap_password.unlang index f60fdb14ae0..91581b91904 100644 --- a/src/tests/modules/chap/chap_password.unlang +++ b/src/tests/modules/chap/chap_password.unlang @@ -4,7 +4,7 @@ update request { } update request { - &CHAP-Password := "%{chap_password:%{control:Cleartext-Password}}" + &CHAP-Password := "%{chap_password:%{control.Cleartext-Password}}" } chap.authenticate diff --git a/src/tests/modules/ldap/module.conf b/src/tests/modules/ldap/module.conf index 72754653520..2e6b26d7aec 100644 --- a/src/tests/modules/ldap/module.conf +++ b/src/tests/modules/ldap/module.conf @@ -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. diff --git a/src/tests/modules/linelog/module.conf b/src/tests/modules/linelog/module.conf index fabf5f11d42..ca0db6de6d9 100644 --- a/src/tests/modules/linelog/module.conf +++ b/src/tests/modules/linelog/module.conf @@ -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[*] } } diff --git a/src/tests/modules/redis/cluster_key.unlang b/src/tests/modules/redis/cluster_key.unlang index af4a2db1278..8ee3682990e 100644 --- a/src/tests/modules/redis/cluster_key.unlang +++ b/src/tests/modules/redis/cluster_key.unlang @@ -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 diff --git a/src/tests/modules/redis/cluster_reset.inc b/src/tests/modules/redis/cluster_reset.inc index d00be31686a..50dc148715e 100644 --- a/src/tests/modules/redis/cluster_reset.inc +++ b/src/tests/modules/redis/cluster_reset.inc @@ -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 } } diff --git a/src/tests/modules/redis_ippool/alloc.unlang b/src/tests/modules/redis_ippool/alloc.unlang index fc3444e2c94..8032bb16a72 100644 --- a/src/tests/modules/redis_ippool/alloc.unlang +++ b/src/tests/modules/redis_ippool/alloc.unlang @@ -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` } # diff --git a/src/tests/modules/redis_ippool/pool_tool_delete.unlang b/src/tests/modules/redis_ippool/pool_tool_delete.unlang index 651bd217b19..52547591f91 100644 --- a/src/tests/modules/redis_ippool/pool_tool_delete.unlang +++ b/src/tests/modules/redis_ippool/pool_tool_delete.unlang @@ -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 diff --git a/src/tests/modules/redis_ippool/pool_tool_modify.unlang b/src/tests/modules/redis_ippool/pool_tool_modify.unlang index ffafa739a7b..ecd533883b9 100644 --- a/src/tests/modules/redis_ippool/pool_tool_modify.unlang +++ b/src/tests/modules/redis_ippool/pool_tool_modify.unlang @@ -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 diff --git a/src/tests/modules/redis_ippool/pool_tool_release.unlang b/src/tests/modules/redis_ippool/pool_tool_release.unlang index e2b6731e359..8ee61dc888d 100644 --- a/src/tests/modules/redis_ippool/pool_tool_release.unlang +++ b/src/tests/modules/redis_ippool/pool_tool_release.unlang @@ -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 diff --git a/src/tests/modules/redis_ippool/release.unlang b/src/tests/modules/redis_ippool/release.unlang index 1c421da3af0..069a309cbb6 100644 --- a/src/tests/modules/redis_ippool/release.unlang +++ b/src/tests/modules/redis_ippool/release.unlang @@ -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 diff --git a/src/tests/modules/redis_ippool/update.unlang b/src/tests/modules/redis_ippool/update.unlang index 93c30edf01d..c3b9f79fcc5 100644 --- a/src/tests/modules/redis_ippool/update.unlang +++ b/src/tests/modules/redis_ippool/update.unlang @@ -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 diff --git a/src/tests/modules/redis_ippool/update_alloc.unlang b/src/tests/modules/redis_ippool/update_alloc.unlang index efb0e6ffee2..4aed0045ac8 100644 --- a/src/tests/modules/redis_ippool/update_alloc.unlang +++ b/src/tests/modules/redis_ippool/update_alloc.unlang @@ -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 diff --git a/src/tests/modules/sql/map.unlang b/src/tests/modules/sql/map.unlang index d6169018ddc..e112cba4890 100644 --- a/src/tests/modules/sql/map.unlang +++ b/src/tests/modules/sql/map.unlang @@ -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 { diff --git a/src/tests/radclient/config/radiusd.conf b/src/tests/radclient/config/radiusd.conf index 008da2a9b69..fd7635bc00f 100644 --- a/src/tests/radclient/config/radiusd.conf +++ b/src/tests/radclient/config/radiusd.conf @@ -117,8 +117,8 @@ server test { } update reply { - &raw.Attr-26 := &reply:Class - &raw.Attr-26 += 0x483d342c493d43 + &raw.26 := &reply:Class + &raw.26 += 0x483d342c493d43 } } diff --git a/src/tests/unit/condition/base.txt b/src/tests/unit/condition/base.txt index 16b9a849cd4..a988760c749 100644 --- a/src/tests/unit/condition/base.txt +++ b/src/tests/unit/condition/base.txt @@ -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 &Filter-Id == &Framed-IP-Address match ERROR offset 22 Unexpected cast condition &Filter-Id == &Framed-IP-Address -match ERROR offset 22 Unexpected cast +match ERROR offset 23 Unknown data type condition &Filter-Id == "foo" match ERROR offset 1 Unknown data type @@ -153,9 +153,6 @@ match ERROR offset 1 Unknown data type # # Normalize things # -condition &Filter-Id == "127.0.0.1" -match &Filter-Id == '127.0.0.1' - condition 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 127.0.0.1 == "127.0.0.1" match true -condition 127.0.0.1 == "%{sql: 127.0.0.1}" -match 127.0.0.1 == "%{sql: 127.0.0.1}" +condition 127.0.0.1 == "%{md4: 127.0.0.1}" +match 127.0.0.1 == "%{md4: 127.0.0.1}" # # Bare %{...} is allowed. # -condition 127.0.0.1 == %{sql:127.0.0.1} -match 127.0.0.1 == "%{sql:127.0.0.1}" +condition 127.0.0.1 == %{md4:127.0.0.1} +match 127.0.0.1 == %{md4:127.0.0.1} -condition 127.0.0.1 == %{sql: SELECT user FROM table WHERE user='%{User-Name}'} -match 127.0.0.1 == "%{sql: SELECT user FROM table WHERE user='%{User-Name}'}" +condition 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'} +match 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'} condition 00:11:22:33:44:55 == "00:11:22:33:44:55" match true -condition 00:11:22:33:44:55 == "ff:11:22:33:44:55" -match false -condition 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}" -match 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}" +condition 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}" +match 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}" condition 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 127.0.0.1 == 2130706433 match true @@ -412,14 +408,15 @@ match ERROR offset 2 Forbidden data type in cast condition "foo" == &User-Name match &User-Name == "foo" -condition "%{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 "%{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 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 &Filter-Id == &Framed-IP-Address condition &Class == &Framed-IP-Address match &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 ¬-a-list:User-Name == ¬-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 ¬-a-packet.User-Name == ¬-a-packet.User-Name match ¬-a-packet.User-Name == ¬-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 192.168.0.0/16 > 192.168.1.2 match true @@ -645,8 +602,8 @@ match &NAS-IP-Address < &PMIP6-Home-IPv4-HoA condition &NAS-IP-Address < 192.168/16 match &NAS-IP-Address < 192.168.0.0/16 -condition &NAS-IP-Address < "%{echo: 192.168/16}" -match &NAS-IP-Address < "%{echo: 192.168/16}" +condition &NAS-IP-Address < "%{string: 192.168/16}" +match &NAS-IP-Address < "%{string: 192.168/16}" condition &NAS-IP-Address < `/bin/echo 192.168/16` match &NAS-IP-Address < `/bin/echo 192.168/16` @@ -665,4 +622,4 @@ condition ("$ENV{SOMETHING_OR_OTHER}" == '') match true count -match 334 +match 306 diff --git a/src/tests/unit/condition/escape.txt b/src/tests/unit/condition/escape.txt index ccf1f24171c..bf75375d6d0 100644 --- a/src/tests/unit/condition/escape.txt +++ b/src/tests/unit/condition/escape.txt @@ -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 diff --git a/src/tests/unit/condition/regex.txt b/src/tests/unit/condition/regex.txt index a69c858fbe5..0e1988acf43 100644 --- a/src/tests/unit/condition/regex.txt +++ b/src/tests/unit/condition/regex.txt @@ -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 diff --git a/src/tests/unit/protocols/radius/packet_radius.txt b/src/tests/unit/protocols/radius/packet_radius.txt index 0e86e578f47..e56da934d2f 100644 --- a/src/tests/unit/protocols/radius/packet_radius.txt +++ b/src/tests/unit/protocols/radius/packet_radius.txt @@ -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. diff --git a/src/tests/unit/protocols/radius/packet_rfc3162.txt b/src/tests/unit/protocols/radius/packet_rfc3162.txt index ed55a69f398..81051c122c3 100644 --- a/src/tests/unit/protocols/radius/packet_rfc3162.txt +++ b/src/tests/unit/protocols/radius/packet_rfc3162.txt @@ -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 diff --git a/src/tests/unit/protocols/radius/packet_wireshark01.txt b/src/tests/unit/protocols/radius/packet_wireshark01.txt index 8927e305b9e..f47933e5288 100644 --- a/src/tests/unit/protocols/radius/packet_wireshark01.txt +++ b/src/tests/unit/protocols/radius/packet_wireshark01.txt @@ -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. diff --git a/src/tests/unit/protocols/radius/rfc2868.txt b/src/tests/unit/protocols/radius/rfc2868.txt index 864b0eb09cf..5de503ab29b 100644 --- a/src/tests/unit/protocols/radius/rfc2868.txt +++ b/src/tests/unit/protocols/radius/rfc2868.txt @@ -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 diff --git a/src/tests/unit/protocols/radius/wimax.txt b/src/tests/unit/protocols/radius/wimax.txt index e29c012e22b..99cbe4467c3 100644 --- a/src/tests/unit/protocols/radius/wimax.txt +++ b/src/tests/unit/protocols/radius/wimax.txt @@ -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 diff --git a/src/tests/unit/unknown.txt.broken b/src/tests/unit/unknown.txt.broken index ccade8f4718..9fabb1fcdd7 100644 --- a/src/tests/unit/unknown.txt.broken +++ b/src/tests/unit/unknown.txt.broken @@ -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 diff --git a/src/tests/unit/xlat/base.txt b/src/tests/unit/xlat/base.txt index b86a3c0c19e..62c8dc95880 100644 --- a/src/tests/unit/xlat/base.txt +++ b/src/tests/unit/xlat/base.txt @@ -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