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
# example - 'cn'
name_attribute = '<group_name_attribute>' <3>
- # example - "(member=%{control:Ldap-UserDn})"
- membership_filter = "(<group_membership_dn_attribute>=%{control:Ldap-UserDn})" <4>
+ # example - "(member=%{control.Ldap-UserDn})"
+ membership_filter = "(<group_membership_dn_attribute>=%{control.Ldap-UserDn})" <4>
}
}
----
# example - 'cn'
name_attribute = '<group_name_attribute>' <3>
- # example - "(memberUid=%{control:Ldap-UserDn})"
- membership_filter = "(<group_membership_uid_attribute>=%{control:Ldap-UserDn})" <4>
+ # example - "(memberUid=%{control.Ldap-UserDn})"
+ membership_filter = "(<group_membership_uid_attribute>=%{control.Ldap-UserDn})" <4>
}
}
----
[width="100%",cols="30%,70%",options="header",]
|===
| Purpose | `ldap { group { ... } }` config item
-| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(<group_membership_dn_attribute>=%{control:Ldap-UserDn})"```
+| Specify how to find group objects referencing a user by DN. | ```membership_filter = "(<group_membership_dn_attribute>=%{control.Ldap-UserDn})"```
|===
== Groups - variant 4
[source,config]
----
-membership_filter = "(|(<group_membership_filter_by_uid>=%{control:Ldap-UserDn})(<group_membership_filter_by_name>=%{%{Stripped-User-Name}:-%{User-Name}}))"
+membership_filter = "(|(<group_membership_filter_by_uid>=%{control.Ldap-UserDn})(<group_membership_filter_by_name>=%{%{Stripped-User-Name}:-%{User-Name}}))"
----
****
alloc_find = "\
CALL fr_allocate_previous_or_new_framedipaddress( \
- '%{control:${pool_name}}', \
+ '%{control.${pool_name}}', \
'%{User-Name}', \
'%{Calling-Station-Id}', \
'%{NAS-IP-Address}', \
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}', \
```
cache cache_eap {
- key = "%{%{control:State}:-%{%{reply:State}:-%{State}}}"
+ key = "%{%{control.State}:-%{%{reply.State}:-%{State}}}"
ttl = 15
update reply {
&reply: += &reply:
# relative = no
}
delay delay_reject {
- delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}"
+ delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}"
relative = yes
}
```
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})"
}
```
&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]}"
}
----
&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."
}
----
&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}}"
}
----
&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}}"
}
----
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'
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}"
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
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
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
# }
# &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
# }
}
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 {
# }
# 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) {
----
call inner-tunnel {
update parent.reply {
- &Packet-Type := "%{reply:Packet-Type}"
+ &Packet-Type := "%{reply.Packet-Type}"
}
}
----
`"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.
.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:
}
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}"
}
----
}
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]}"
}
----
&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}}"
}
----
====
}
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[*]}}"
}
----
"%{explode:&control:Tmp-String-0 @}"
update reply {
- &Reply-Message := "Welcome %{control:Tmp-String-0[0]}"
+ &Reply-Message := "Welcome %{control.Tmp-String-0[0]}"
}
----
&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}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}}"
}
----
&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}"
}
----
&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}"
}
----
&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}}}"
}
----
&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}}}"
}
----
[source,unlang]
----
update reply {
- &Class := "%{pack:%{reply:Framed-IP-Address}%{NAS-IP-Address}}"
+ &Class := "%{pack:%{reply.Framed-IP-Address}%{NAS-IP-Address}}"
}
----
if (&control:Tmp-String-0 =~ /^(?<login>(.*))@(?<domain>(.*))$/) {
update reply {
- &Reply-Message := "The %{control:Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }"
+ &Reply-Message := "The %{control.Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }"
}
}
----
&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}}"
}
----
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
#
# key:: Cache key.
#
- key = "%{%{control:State}:-%{%{reply:State}:-%{State}}}"
+ key = "%{%{control.State}:-%{%{reply.State}:-%{State}}}"
#
# ttl:: TTL for cache entries.
# received.
#
delay delay_reject {
- delay = "%{%{reply:FreeRADIUS-Response-Delay}:-1}"
+ delay = "%{%{reply.FreeRADIUS-Response-Delay}:-1}"
relative = yes
}
#
$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})"
}
# &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]}"
# }
# ----
#
# &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."
# }
# ----
#
# 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
# &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}}"
# }
# ----
#
# &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}}"
# }
# ----
#
#
# 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 { ... }::
# 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})"
}
(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}'"
}
(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"
(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)"
}
-- 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}, \
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 \
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 \
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 \
# 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) \
# 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 \
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.
#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}, \
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 \
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}}'"
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}'"
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}'"
-- 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}, \
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 \
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() \
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 \
#
#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} \
#
#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 \
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:${pool-name}}' \
+ WHERE pool_name='%{control.${pool-name}}' \
LIMIT 1"
#
#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}, \
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() \
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}}'"
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}'"
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}'"
-- 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}, \
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}', \
# 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 \
# 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 \
# 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 \
FROM (\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name = '%{control:Pool-Name}'\
+ WHERE pool_name = '%{control.Pool-Name}'\
) WHERE ROWNUM = 1"
#
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"
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"
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"
-- 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}, \
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 \
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' \
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 \
#
#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 \
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:${pool_name}}' \
+ WHERE pool_name='%{control.${pool_name}}' \
LIMIT 1"
#
#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}', \
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) \
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}}'"
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}'"
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}'"
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 \
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'"
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"
# 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() \
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' \
+ WHERE pool_name='%{control.Pool-Name}' \
LIMIT 1"
#
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') \
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}}'"
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 \
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}'"
-- 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}', \
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 \
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 \
) \
#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() \
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.
#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}', \
-- 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}', \
-- 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}', \
#
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 \
#
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 \
#
#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() \
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' \
+ WHERE pool_name='%{control.Pool-Name}' \
LIMIT 1"
#
#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}', \
-- 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}', \
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}' ) \
# 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}' ) \
# 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 \
# 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 \
FROM (\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}'\
+ WHERE pool_name='%{control.Pool-Name}'\
) \
WHERE ROWNUM = 1"
#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}', \
-- 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}', \
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 \
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 \
#
#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}"
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' \
+ WHERE pool_name='%{control.Pool-Name}' \
LIMIT 1"
#
#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}', \
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 \
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"
#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"
pool_check = "\
SELECT id \
FROM ${ippool_table} \
- WHERE pool_name='%{control:${pool_name}}' \
+ WHERE pool_name='%{control.${pool_name}}' \
LIMIT 1"
#
VALUES ( \
'%{SQL-User-Name}', \
'%{%{User-Password}:-%{Chap-Password}}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
'%{expr:%l * 1000 + %M / 1000}')"
}
VALUES(\
'%{User-Name}', \
'%{%{User-Password}:-CHAP-PASSWORD}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
'%S.%{expr:%M / 1000}')"
}
(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}' )"
VALUES ( \
'%{SQL-User-Name}', \
'%{%{User-Password}:-%{Chap-Password}}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
'%S.%M')"
}
VALUES (\
'%{User-Name}', \
'%{%{User-Password}:-%{Chap-Password}}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
TO_TIMESTAMP('%S.%M','YYYY-MM-DDHH24:MI:SS.FF'))"
}
VALUES(\
'%{User-Name}', \
'%{%{User-Password}:-Chap-Password}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
'%S.%M')"
}
VALUES ( \
'%{SQL-User-Name}', \
'%{%{User-Password}:-%{Chap-Password}}', \
- '%{reply:Packet-Type}', \
+ '%{reply.Packet-Type}', \
'%S.%M')"
}
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}:-}}}"
}
}
}
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
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
# @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
}
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
# @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
}
# 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
# }
#
# 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
# }
# 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
#
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 {
#
# 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
# 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}"
# }
#
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}}"
}
}
/*
* 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);
}
/*
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,
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 {
} 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++;
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);
}
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);
}
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;
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;
* 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;
}
return;
}
- fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
+ fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da);
}
/*
/*
* 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;
}
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
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;
{
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));
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;
return (_len); \
} while (0)
-#define RETURN_OK_WITH_ERROR(_len) \
+#define RETURN_OK_WITH_ERROR() \
do { \
result->rcode = RESULT_OK; \
result->file = __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
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;
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);
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);
}
}
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);
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);
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);
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);
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();
}
/*
* 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);
}
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;
/*
* Delete the trailing ", ".
*/
- if (p > data) {
- p -= 2;
- }
+ if (p > data) p -= 2;
*p = 0;
RETURN_OK(p - data);
fr_value_box_t *box2;
fr_type_t type;
size_t match_len;
- size_t len;
+ ssize_t slen;
char *p;
/*
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();
}
* 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
* 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();
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,
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);
}
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);
}
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;
* 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;
}
}
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");
#include <freeradius-devel/server/module.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/rand.h>
+#include <freeradius-devel/util/value.h>
#include <freeradius-devel/tls/base.h>
* 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;
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;
}
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;
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")) {
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) {
* 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 {
* 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 };
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;
* 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;
* 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
* 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;
/*
* 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
{
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 {
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
* 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++;
/*
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
* - 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
* 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);
*
* 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;
/*
* 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);
* 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,
*
* 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;
/*
* 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);
* 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);
*
* 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;
/*
* 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;
}
* 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);
}
* 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;
}
* 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;
}
* 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;
}
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:
* 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)",
* 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);
* 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");
}
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:
* 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 "
}
}
- 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;
* 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");
}
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:
* 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");
}
* 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);
/*
* 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;
/*
* 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;
* 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:
/*
* 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);
}
* 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);
}
* 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;
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;
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:
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
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
* 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
* 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);
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",
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",
/*
* 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;
* 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;
/*
* 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;
/*
/*
* 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;
/*
* 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",
/*
* 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;
/*
* 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;
/*
* 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;
/*
* 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;
/*
* 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;
/*
* 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",
/*
* 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",
* 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);
}
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);
* 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);
}
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);
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;
*/
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
*/
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
*/
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
*/
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
}
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
*/
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
}
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
} 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");
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) {
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, "");
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);
*/
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) {
/* 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 */
*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;
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:
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());
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;
* 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;
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);
}
/*
* 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;
#include <freeradius-devel/server/cf_file.h>
#include <freeradius-devel/server/cf_priv.h>
-#include <freeradius-devel/server/log.h>
#include <freeradius-devel/server/cond.h>
-#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/server/log.h>
#include <freeradius-devel/server/util.h>
-
+#include <freeradius-devel/server/virtual_servers.h>
#include <freeradius-devel/util/cursor.h>
+#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/syserror.h>
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;
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
* 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;
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);
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 */
#include <freeradius-devel/server/cf_parse.h>
#include <freeradius-devel/server/cf_priv.h>
#include <freeradius-devel/server/log.h>
-#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/server/tmpl.h>
-
+#include <freeradius-devel/server/virtual_servers.h>
+#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/inet.h>
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/types.h>
}
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);
* 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) {
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);
}
* 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
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;
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);
CONF_PARSER *rule;
void *data;
int type;
+ fr_dict_t const *dict = NULL;
rule = cf_data_value(rule_cd);
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).
*
/*
* 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;
{
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);
}
}
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);
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;
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);
/** 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);
*/
void cond_debug(fr_cond_t const *cond)
{
- fr_cond_t const *next;
+ fr_cond_t const *c;
- for (next = cond; next; next = next->next) {
- INFO("cond %s (%p)", fr_table_str_by_value(cond_type_table, cond->type, "<INVALID>"), cond);
- INFO("\tnegate : %s", cond->negate ? "true" : "false");
- INFO("\tcast : %s", cond->cast ? fr_table_str_by_value(fr_value_box_type_table,
- cond->cast->type, "<INVALID>") : "none");
- INFO("\top : %s", fr_table_str_by_value(cond_op_table, cond->next_op, "<INVALID>"));
- INFO("\tfixup : %s", fr_table_str_by_value(cond_pass2_table, cond->pass2_fixup, "<INVALID>"));
+ for (c = cond; c; c =c->next) {
+ INFO("cond %s (%p)", fr_table_str_by_value(cond_type_table, c->type, "<INVALID>"), cond);
+ INFO("\tnegate : %s", c->negate ? "true" : "false");
+ INFO("\tcast : %s", c->cast ? fr_table_str_by_value(fr_value_box_type_table,
+ c->cast->type, "<INVALID>") : "none");
+ INFO("\top : %s", fr_table_str_by_value(cond_logical_op_table, c->next_op, "<INVALID>"));
+ INFO("\tfixup : %s", fr_table_str_by_value(cond_pass2_table, c->pass2_fixup, "<INVALID>"));
- switch (cond->type) {
+ switch (c->type) {
case COND_TYPE_MAP:
- tmpl_attr_debug(cond->data.map->lhs);
- tmpl_attr_debug(cond->data.map->rhs);
+ INFO("lhs (");
+ tmpl_debug(c->data.map->lhs);
+ INFO(")");
+ INFO("rhs (");
+ tmpl_debug(c->data.map->rhs);
+ INFO(")");
+ break;
+
+ case COND_TYPE_RCODE:
+ INFO("\trcode : %s", fr_table_str_by_value(rcode_table, c->data.rcode, ""));
break;
case COND_TYPE_EXISTS:
- tmpl_attr_debug(cond->data.vpt);
+ tmpl_debug(c->data.vpt);
break;
case COND_TYPE_CHILD:
- cond_debug(cond->next);
+ INFO("child (");
+ cond_debug(c->data.child);
+ INFO(")");
break;
default:
* 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
* - 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:
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__);
/*
* 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;
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__);
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 {\
fr_table_str_by_value(fr_value_box_type_table, _s->type, "<INVALID>"),\
fr_table_str_by_value(fr_value_box_type_table, cast_type, "<INVALID>"));\
if (fr_value_box_cast(request, &_s ## _cast, cast_type, cast, _s) < 0) {\
- RPEDEBUG("Failed casting " #_s " operand");\
+ if (request) RPEDEBUG("Failed casting " #_s " operand");\
rcode = -1;\
goto finish;\
}\
*/
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;
}
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;
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;
/** 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
* - 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;
case TMPL_TYPE_UNRESOLVED:
case TMPL_TYPE_EXEC:
- case TMPL_TYPE_XLAT_UNRESOLVED:
case TMPL_TYPE_XLAT:
{
char *p = NULL;
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);
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;
* - 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:
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
}
#include <ctype.h>
static fr_table_num_sorted_t const allowed_return_codes[] = {
- { L("fail"), 1 },
- { L("handled"), 1 },
- { L("invalid"), 1 },
- { L("noop"), 1 },
- { L("notfound"), 1 },
- { L("ok"), 1 },
- { L("reject"), 1 },
- { L("updated"), 1 },
- { L("disallow"), 1 }
+ { L("fail"), 1 },
+ { L("handled"), 1 },
+ { L("invalid"), 1 },
+ { L("noop"), 1 },
+ { L("notfound"), 1 },
+ { L("ok"), 1 },
+ { L("reject"), 1 },
+ { L("updated"), 1 },
+ { L("disallow"), 1 }
};
static size_t allowed_return_codes_len = NUM_ELEMENTS(allowed_return_codes);
+fr_table_num_sorted_t const cond_quote_table[] = {
+ { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
+ { L("'"), T_SINGLE_QUOTED_STRING },
+ { L("/"), T_SOLIDUS_QUOTED_STRING },
+ { L("`"), T_BACK_QUOTED_STRING }
+};
+size_t cond_quote_table_len = NUM_ELEMENTS(cond_quote_table);
+
+fr_table_num_sorted_t const cond_logical_op_table[] = {
+ { L("&&"), COND_AND },
+ { L("||"), COND_OR }
+};
+size_t cond_logical_op_table_len = NUM_ELEMENTS(cond_logical_op_table);
+
+fr_table_num_sorted_t const cond_cmp_op_table[] = {
+ { L("!*"), T_OP_CMP_FALSE },
+ { L("!="), T_OP_NE },
+ { L("!~"), T_OP_REG_NE },
+ { L(":="), T_OP_SET },
+ { L("<"), T_OP_LT },
+ { L("<="), T_OP_LE },
+ { L("="), T_OP_EQ },
+ { L("=*"), T_OP_CMP_TRUE },
+ { L("=="), T_OP_CMP_EQ },
+ { L("=~"), T_OP_REG_EQ },
+ { L(">"), T_OP_GT },
+ { L(">="), T_OP_GE }
+};
+size_t cond_cmp_op_table_len = NUM_ELEMENTS(cond_cmp_op_table);
+
/*
* This file shouldn't use any functions from the server core.
*/
-
-size_t cond_snprint(size_t *need, char *out, size_t outlen, fr_cond_t const *in)
+ssize_t cond_print(fr_sbuff_t *out, fr_cond_t const *in)
{
- size_t len;
- char *p = out;
- char *end = out + outlen - 1;
- fr_cond_t const *c = in;
- size_t our_need;
-
- if (!need) need = &our_need;
-
- RETURN_IF_NO_SPACE_INIT(need, 1, p, out, end);
+ fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
+ fr_cond_t const *c = in;
-next:
- if (!c) {
- p[0] = '\0';
- return 0;
- }
-
- if (c->negate) {
- *(p++) = '!'; /* FIXME: only allow for child? */
- }
-
- switch (c->type) {
- case COND_TYPE_EXISTS:
- fr_assert(c->data.vpt != NULL);
- if (c->cast) {
- len = snprintf(p, end - p, "<%s>", fr_table_str_by_value(fr_value_box_type_table,
- c->cast->type, "??"));
- RETURN_IF_TRUNCATED(need, len, p, out, end);
- }
+ while (c) {
+ if (c->negate) FR_SBUFF_IN_CHAR_RETURN(&our_out, '!');
- len = tmpl_snprint(need, p, end - p, c->data.vpt);
- if (*need) return len;
- p += len;
- break;
+ switch (c->type) {
+ case COND_TYPE_EXISTS:
+ fr_assert(c->data.vpt != NULL);
+ if (c->cast) {
+ FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
+ fr_table_str_by_value(fr_value_box_type_table,
+ c->cast->type, "??"));
+ }
+ FR_SBUFF_RETURN(tmpl_print_quoted, &our_out, c->data.vpt, TMPL_ATTR_REF_PREFIX_YES);
+ break;
- case COND_TYPE_MAP:
- fr_assert(c->data.map != NULL);
-#if 0
- *(p++) = '['; /* for extra-clear debugging */
-#endif
- if (c->cast) {
- len = snprintf(p, end - p, "<%s>", fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "??"));
- RETURN_IF_TRUNCATED(need, len, p, out, end);
- }
+ case COND_TYPE_RCODE:
+ fr_assert(c->data.rcode != RLM_MODULE_UNKNOWN);
+ FR_SBUFF_IN_STRCPY_RETURN(&our_out, fr_table_str_by_value(rcode_table, c->data.rcode, ""));
+ break;
- len = map_snprint(need, p, end - p, c->data.map);
- if (*need) return len;
- p += len;
-#if 0
- *(p++) = ']';
-#endif
- break;
+ case COND_TYPE_MAP:
+ if (c->cast) {
+ FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "<%s>",
+ fr_table_str_by_value(fr_value_box_type_table,
+ c->cast->type, "??"));
+ }
+ FR_SBUFF_RETURN(map_print, &our_out, c->data.map);
+ break;
- case COND_TYPE_CHILD:
- fr_assert(c->data.child != NULL);
- *(p++) = '(';
- len = cond_snprint(need, p, (end - p) - 1, c->data.child); /* -1 for proceeding ')' */
- if (*need) return len;
- if (len >= (outlen - 1)) return len;
- p += len;
- *(p++) = ')';
- break;
+ case COND_TYPE_CHILD:
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, '(');
+ FR_SBUFF_RETURN(cond_print, &our_out, c->data.child);
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, ')');
+ break;
- case COND_TYPE_TRUE:
- len = strlcpy(out, "true", outlen);
- RETURN_IF_TRUNCATED(need, len, p, out, end);
- return p - out;
+ case COND_TYPE_TRUE:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "true");
+ break;
- case COND_TYPE_FALSE:
- len = strlcpy(out, "false", outlen);
- RETURN_IF_TRUNCATED(need, len, p, out, end);
- return p - out;
+ case COND_TYPE_FALSE:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "false");
+ break;
- default:
- *out = '\0';
- return 0;
- }
+ default:
+ break;
+ }
- if (c->next_op == COND_NONE) {
- fr_assert(c->next == NULL);
- *p = '\0';
- return p - out;
- }
+ if (c->next_op == COND_NONE) {
+ fr_assert(c->next == NULL);
+ goto done;
+ }
- if (c->next_op == COND_AND) {
- len = strlcpy(p, " && ", end - p);
- RETURN_IF_TRUNCATED(need, len, p, out, end);
+ switch (c->next_op) {
+ case COND_AND:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " && ");
+ break;
- } else if (c->next_op == COND_OR) {
- len = strlcpy(p, " || ", end - p);
- RETURN_IF_TRUNCATED(need, len, p, out, end);
+ case COND_OR:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, " || ");
+ break;
- } else {
- fr_assert(0 == 1);
+ default:
+ fr_assert(0);
+ }
+ c = c->next;
}
- c = c->next;
- goto next;
+done:
+ fr_sbuff_terminate(&our_out);
+ return fr_sbuff_set(out, &our_out);
}
#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)) {
return 1;
}
- *error = "Attribute comparisons must be of the same data type";
+ fr_strerror_printf("Cannot compare types '%s' (cast) and '%s' (attr)",
+ fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "<INVALID>"),
+ fr_table_str_by_value(fr_value_box_type_table, tmpl_da(c->data.map->rhs)->type, "<INVALID>"));
return 0;
}
#ifdef HAVE_REGEX
- if (tmpl_is_regex_unresolved(c->data.map->rhs)) {
- *error = "Cannot use cast with regex comparison";
+ if (tmpl_contains_regex(c->data.map->rhs)) {
+ fr_strerror_printf("Cannot use cast with regex comparison");
return -(rhs - start);
}
#endif
*/
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);
}
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);
}
*/
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;
}
}
/*
* See if two attribute comparisons are OK.
*/
-static ssize_t cond_check_attrs(fr_cond_t *c, char const *start,
- char const *lhs, fr_token_t lhs_type,
- char const *rhs, fr_token_t rhs_type,
- char const **error)
+static ssize_t cond_check_attrs(fr_cond_t *c, fr_sbuff_marker_t *m_lhs, fr_sbuff_marker_t *m_rhs)
{
- tmpl_t *vpt;
+ tmpl_t *attr, *data, *xlat, *unresolved, *xlat_unresolved, *exec, *vpt;
+ tmpl_t *lhs = c->data.map->lhs, *rhs = c->data.map->rhs;
+ fr_token_t op = c->data.map->op;
+
+/** True if one operand is of _type_a and the other of _type_b
+ */
+#define TMPL_OF_TYPE_A_B(_type_a, _type_b) \
+ ((((tmpl_is_##_type_a(lhs) && (_type_a = lhs)) && (tmpl_is_##_type_b(rhs) && (_type_b = rhs)))) || \
+ (((tmpl_is_##_type_a(rhs) && (_type_a = rhs)) && (tmpl_is_##_type_b(lhs) && (_type_b = lhs)))))
+
+/** True if one operand is of _type_a and the other is not of _type_a
+ *
+ */
+#define TMPL_OF_TYPE_A_NOT_A(_type_a, _out_b) \
+ ((((tmpl_is_##_type_a(lhs) && (_type_a = lhs)) && (!tmpl_is_##_type_a(rhs) && (_out_b = rhs)))) || \
+ (((tmpl_is_##_type_a(rhs) && (_type_a = rhs)) && (!tmpl_is_##_type_a(lhs) && (_out_b = lhs)))))
+
+/** True if both operands are of _type_a
+ *
+ */
+#define TMPL_OF_TYPE_A_A(_type_a) \
+ ((tmpl_is_##_type_a(lhs)) && (tmpl_is_##_type_a(rhs)))
+
+#define TMPL_RETURN(_vpt) return -((_vpt) == lhs ? fr_sbuff_used(m_lhs) : fr_sbuff_used(m_rhs))
/*
- * Two attributes? They must be of the same type
+ * Attribute comparison with a box
*/
- if (tmpl_is_attr(c->data.map->rhs) &&
- tmpl_is_attr(c->data.map->lhs) &&
- (tmpl_da(c->data.map->lhs)->type != tmpl_da(c->data.map->rhs)->type)) {
- if (cond_type_check(c, tmpl_da(c->data.map->lhs)->type)) {
- return 1;
+ if (TMPL_OF_TYPE_A_B(attr, data)) {
+ fr_type_t type = tmpl_da(attr)->type;
+
+ /*
+ * Most of the time the box type takes
+ * precedence, except in the case of a few types
+ * like IP prefixes.
+ */
+ switch (tmpl_value_type(data)) {
+ case FR_TYPE_IPV4_PREFIX:
+ case FR_TYPE_IPV6_PREFIX:
+ type = tmpl_value_type(data);
+ break;
+
+ default:
+ break;
}
- *error = "Attribute comparisons must be of the same data type";
- return 0;
+ if (tmpl_cast_in_place(data, type, tmpl_da(attr)) < 0) {
+ fr_strerror_printf_push("Failed casting data to match attribute");
+ return -(data == lhs ? fr_sbuff_used(m_lhs) : fr_sbuff_used(m_rhs));
+ }
}
/*
- * Invalid: User-Name == bob
- * Valid: User-Name == "bob"
- *
- * There's no real reason for
- * this, other than consistency.
+ * Two attributes? They must be of the same type
*/
- if (tmpl_is_attr(c->data.map->lhs) &&
- !tmpl_is_attr(c->data.map->rhs) &&
- (tmpl_da(c->data.map->lhs)->type == FR_TYPE_STRING) &&
- (c->data.map->op != T_OP_CMP_TRUE) &&
- (c->data.map->op != T_OP_CMP_FALSE) &&
- (c->data.map->rhs->quote == T_BARE_WORD)) {
- return_rhs("Comparison value must be a quoted string");
- }
+ if (TMPL_OF_TYPE_A_A(attr) && (tmpl_da(lhs)->type != tmpl_da(rhs)->type)) {
+ if (cond_type_check(c, tmpl_da(lhs)->type)) return 1; /* Or be mungeable to the same type */
- /*
- * Quotes around non-string
- * attributes mean that it's
- * either xlat, or an exec.
- */
- if (tmpl_is_attr(c->data.map->lhs) &&
- !tmpl_is_attr(c->data.map->rhs) &&
- (tmpl_da(c->data.map->lhs)->type != FR_TYPE_STRING) &&
- (tmpl_da(c->data.map->lhs)->type != FR_TYPE_OCTETS) &&
- (tmpl_da(c->data.map->lhs)->type != FR_TYPE_DATE) &&
- (rhs_type == T_SINGLE_QUOTED_STRING)) {
- *error = "Comparison value must be an unquoted string";
- return_rhs:
- return -(rhs - start);
+ fr_strerror_printf("Cannot compare attributes of type '%s' and '%s'",
+ fr_table_str_by_value(fr_value_box_type_table, tmpl_da(lhs)->type, "<INVALID>"),
+ fr_table_str_by_value(fr_value_box_type_table, tmpl_da(rhs)->type, "<INVALID>"));
+ return 0;
}
/*
* The LHS has been cast to a data type, and the RHS is a
* literal. Cast the RHS to the type of the cast.
+ *
+ * FIXME - We should revisit this when RHS casting is supported.
*/
- if (c->cast && tmpl_is_unresolved(c->data.map->rhs) &&
- (tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast) < 0)) {
- return_rhs("Failed to parse field");
+ if (c->cast && tmpl_is_unresolved(rhs) &&
+ (tmpl_cast_in_place(rhs, c->cast->type, c->cast) < 0)) {
+ fr_strerror_printf("Failed to parse field");
+ return -fr_sbuff_used(m_rhs);
}
/*
*
* 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);
}
/*
* 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:
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;
}
* 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);
}
* 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;
* 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++;
}
}
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;
}
}
/*
* 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,
* conditions don't use filenames.
*/
if (!require_regex && (*in == '/')) {
- *error = "Unexpected regular expression";
+ fr_strerror_printf("Unexpected regular expression");
return 0;
}
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;
}
*/
expanded = talloc_strndup(ctx, *out, *outlen);
if (!expanded) {
- *error = "Failed allocating memory";
+ fr_strerror_printf("Failed allocating memory");
return -1;
}
*out = expanded;
return slen; /* NOT my_slen */
}
+#endif
-
-/** Tokenize a conditional check
- *
- * @param[in] ctx talloc ctx
- * @param[in] cs our configuration section
- * @param[out] pcond pointer to the returned condition structure
- * @param[out] error the parse error (if any)
- * @param[in] in the start of the string to process. Should be "(..."
- * @param[in] inlen the length of the string to process
- * @param[in] brace look for a closing brace (how many deep we are)
- * @param[in] rules for attribute parsing
- * @return
- * - Length of the string skipped.
- * - < 0 (the offset to the offending error) on error.
- */
-static ssize_t cond_tokenize(TALLOC_CTX *ctx, CONF_SECTION *cs,
- fr_cond_t **pcond, char const **error,
- char const *in, size_t inlen, int brace,
- tmpl_rules_t const *rules)
+static int cond_normalise(TALLOC_CTX *ctx, fr_token_t lhs_type, fr_cond_t **c_out)
{
- ssize_t slen, tlen;
- char const *p = in, *end = in + inlen;
- char const *lhs, *rhs;
- fr_cond_t *c;
- size_t lhs_len, rhs_len;
- fr_token_t op, lhs_type, rhs_type;
- bool regex = false;
- tmpl_rules_t parse_rules;
+ fr_cond_t *c = *c_out;
/*
- * We allow unknown and undefined attributes here
+ * Normalize the condition before returning.
+ *
+ * We collapse multiple levels of braces to one. Then
+ * convert maps to literals. Then literals to true/false
+ * statements. Then true/false ||/&& followed by other
+ * conditions to just conditions.
+ *
+ * Order is important. The more complex cases are
+ * converted to simpler ones, from the most complex cases
+ * to the simplest ones.
*/
- parse_rules = *rules;
- parse_rules.allow_unknown = true;
- parse_rules.allow_unresolved = true;
-
- c = talloc_zero(ctx, fr_cond_t);
-
- fr_assert(c != NULL);
- lhs_type = rhs_type = T_INVALID;
-
- fr_skip_whitespace(p);
-
- if (!*p) {
- return_P("Empty condition is invalid");
- }
/*
- * !COND
+ * (FOO) --> FOO
+ * (FOO) ... --> FOO ...
*/
- if (*p == '!') {
- p++;
- c->negate = true;
- fr_skip_whitespace(p);
+ if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
+ fr_cond_t *child;
- /*
- * Just for stupidity
- */
- if (*p == '!') {
- return_P("Double negation is invalid");
- }
- }
+ child = talloc_steal(ctx, c->data.child);
+ c->data.child = NULL;
- /*
- * (COND)
- */
- if (*p == '(') {
- p++;
+ child->next = talloc_steal(child, c->next);
+ c->next = NULL;
+
+ child->next_op = c->next_op;
/*
- * We've already eaten one layer of
- * brackets. Go recurse to get more.
+ * Set the negation properly
*/
- c->type = COND_TYPE_CHILD;
- c->ci = cf_section_to_item(cs);
- slen = cond_tokenize(c, cs, &c->data.child, error, p, end - p, brace + 1, rules);
- if (slen <= 0) return_SLEN;
-
- if (!c->data.child) {
- return_P("Empty condition is invalid");
+ if ((c->negate && !child->negate) ||
+ (!c->negate && child->negate)) {
+ child->negate = true;
+ } else {
+ child->negate = false;
}
- p += slen;
- fr_skip_whitespace(p);
- goto closing_brace;
+ talloc_free(c);
+ c = child;
}
/*
- * We didn't see anything special. The condition must be one of
+ * (FOO ...) --> FOO ...
*
- * FOO
- * FOO OP BAR
- */
-
- /*
- * Grab the LHS
- */
- slen = cond_preparse(c, &lhs, &lhs_len, p, end - p, &lhs_type, error, &c->cast, false, cs);
- if (slen <= 0) {
- return_SLEN;
- }
-
- /*
- * We may (or not) have an operator
+ * But don't do !(FOO || BAR) --> !FOO || BAR
+ * Because that's different.
*/
+ if ((c->type == COND_TYPE_CHILD) &&
+ !c->next && !c->negate) {
+ fr_cond_t *child;
- /*
- * If the LHS is 0xabcdef, then automatically case it to octets.
- */
- if (!c->cast && (lhs_type == T_BARE_WORD) &&
- (lhs[0] == '0') && (lhs[1] == 'x') &&
- ((slen & 0x01) == 0)) {
- if (slen == 2) {
- return_P("Empty octet string is invalid");
- }
+ child = talloc_steal(ctx, c->data.child);
+ c->data.child = NULL;
- c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
- FR_CAST_BASE + FR_TYPE_OCTETS);
+ talloc_free(c);
+ c = child;
}
- p += slen;
- fr_skip_whitespace(p);
-
/*
- * (FOO)
+ * Convert maps to literals. Convert one form of map to
+ * a standardized form. This doesn't make any
+ * theoretical difference, but it does mean that the
+ * run-time evaluation has fewer cases to check.
*/
- if (*p == ')') {
- if (slen == 0) {
- return_P("Empty string is invalid");
+ if (c->type == COND_TYPE_MAP) do {
+ /*
+ * !FOO !~ BAR --> FOO =~ BAR
+ */
+ if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
+ c->negate = false;
+ c->data.map->op = T_OP_REG_EQ;
}
/*
- * don't skip the brace. We'll look for it later.
+ * FOO !~ BAR --> !FOO =~ BAR
*/
- goto exists;
+ if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
+ c->negate = true;
+ c->data.map->op = T_OP_REG_EQ;
+ }
/*
- * FOO
+ * !FOO != BAR --> FOO == BAR
*/
- } else if (!*p) {
- if (brace) {
- return_P("No closing brace at end of string");
+ if (c->negate && (c->data.map->op == T_OP_NE)) {
+ c->negate = false;
+ c->data.map->op = T_OP_CMP_EQ;
}
- goto exists;
-
/*
- * FOO && ...
+ * This next one catches "LDAP-Group != foo",
+ * which doesn't work as-is, but this hack fixes
+ * it.
+ *
+ * FOO != BAR --> !FOO == BAR
*/
- } else if (((p[0] == '&') && (p[1] == '&')) ||
- ((p[0] == '|') && (p[1] == '|'))) {
-
- exists:
- if (c->cast) {
- *error = "Cannot do cast for existence check";
- return_0:
- talloc_free(c);
- return 0;
+ if (!c->negate && (c->data.map->op == T_OP_NE)) {
+ c->negate = true;
+ c->data.map->op = T_OP_CMP_EQ;
}
- c->type = COND_TYPE_EXISTS;
- c->ci = cf_section_to_item(cs);
+ /*
+ * FOO =* BAR --> FOO
+ * FOO !* BAR --> !FOO
+ *
+ * FOO may be a string, or a delayed attribute
+ * reference.
+ */
+ if ((c->data.map->op == T_OP_CMP_TRUE) ||
+ (c->data.map->op == T_OP_CMP_FALSE)) {
+ tmpl_t *vpt;
- tlen = tmpl_afrom_str(c, &c->data.vpt,
- lhs, lhs_len, lhs_type, &parse_rules, true);
- if (tlen < 0) {
- p = lhs - tlen;
- return_P(fr_strerror());
- }
+ vpt = talloc_steal(c, c->data.map->lhs);
+ c->data.map->lhs = NULL;
- fr_assert(!tmpl_is_regex_unresolved(c->data.vpt));
+ /*
+ * Invert the negation bit.
+ */
+ if (c->data.map->op == T_OP_CMP_FALSE) {
+ c->negate = !c->negate;
+ }
- if (tmpl_unknown_attr_add(c->data.vpt) < 0) {
- p = lhs - tlen;
- return_P("Failed defining attribute");
- }
+ TALLOC_FREE(c->data.map);
- if (tmpl_is_attr_unresolved(c->data.vpt)) {
- c->pass2_fixup = PASS2_FIXUP_ATTR;
+ c->type = COND_TYPE_EXISTS;
+ c->data.vpt = vpt;
+ break; /* it's no longer a map */
}
- } else { /* it's an operator */
- vp_map_t *map;
-
/*
- * The next thing should now be a comparison operator.
+ * Both are data (IP address, integer, etc.)
+ *
+ * We can do the evaluation here, so that it
+ * doesn't need to be done at run time
*/
- c->type = COND_TYPE_MAP;
- c->ci = cf_section_to_item(cs);
-
- switch (*p) {
- default:
- return_P("Invalid text. Expected comparison operator");
-
- case '!':
- if (p[1] == '=') {
- op = T_OP_NE;
- p += 2;
-
-#ifdef HAVE_REGEX
- } else if (p[1] == '~') {
- regex = true;
-
- op = T_OP_REG_NE;
- p += 2;
-#endif
-
- } else if (p[1] == '*') {
- if (lhs_type != T_BARE_WORD) {
- return_P("Cannot use !* on a string");
- }
-
- op = T_OP_CMP_FALSE;
- p += 2;
+ if (tmpl_is_data(c->data.map->lhs) &&
+ tmpl_is_data(c->data.map->rhs)) {
+ int rcode;
+ rcode = cond_eval_map(NULL, 0, c);
+ TALLOC_FREE(c->data.map);
+ c->cast = NULL;
+ if (rcode) {
+ c->type = COND_TYPE_TRUE;
} else {
- goto invalid_operator;
+ c->type = COND_TYPE_FALSE;
}
- break;
-
- case '=':
- if (p[1] == '=') {
- op = T_OP_CMP_EQ;
- p += 2;
-
-#ifdef HAVE_REGEX
- } else if (p[1] == '~') {
- regex = true;
-
- op = T_OP_REG_EQ;
- p += 2;
-#endif
- } else if (p[1] == '*') {
- if (lhs_type != T_BARE_WORD) {
- return_P("Cannot use =* on a string");
- }
-
- op = T_OP_CMP_TRUE;
- p += 2;
-
- } else {
- invalid_operator:
- return_P("Invalid operator");
- }
+ break; /* it's no longer a map */
+ }
- break;
+ /*
+ * Both are literal strings. They're not parsed
+ * as TMPL_TYPE_DATA because there's no cast to an
+ * attribute.
+ *
+ * We can do the evaluation here, so that it
+ * doesn't need to be done at run time
+ */
+ if (tmpl_is_unresolved(c->data.map->rhs) &&
+ tmpl_is_unresolved(c->data.map->lhs) &&
+ !c->pass2_fixup) {
+ int rcode;
- case '<':
- if (p[1] == '=') {
- op = T_OP_LE;
- p += 2;
+ fr_assert(c->cast == NULL);
+ rcode = cond_eval_map(NULL, 0, c);
+ if (rcode) {
+ c->type = COND_TYPE_TRUE;
} else {
- op = T_OP_LT;
- p++;
+ DEBUG4("OPTIMIZING (%s %s %s) --> FALSE",
+ c->data.map->lhs->name,
+ fr_table_str_by_value(fr_tokens_table, c->data.map->op, "??"),
+ c->data.map->rhs->name);
+ c->type = COND_TYPE_FALSE;
}
- break;
- case '>':
- if (p[1] == '=') {
- op = T_OP_GE;
- p += 2;
-
- } else {
- op = T_OP_GT;
- p++;
- }
+ /*
+ * Free map after using it above.
+ */
+ TALLOC_FREE(c->data.map);
break;
}
- fr_skip_whitespace(p);
-
- if (!*p) {
- return_P("Expected text after operator");
- }
-
- slen = cond_preparse(c, &rhs, &rhs_len, p, end - p, &rhs_type, error, NULL, regex, cs);
- if (slen <= 0) {
- return_SLEN;
- }
-
/*
- * Duplicate map_from_fields here, as we
- * want to separate parse errors in the
- * LHS from ones in the RHS.
+ * <ipaddr>"foo" CMP &Attribute-Name The cast may
+ * not be necessary, and we can re-write it so
+ * that the attribute reference is on the LHS.
*/
- c->data.map = map = talloc_zero(c, vp_map_t);
+ if (c->cast &&
+ tmpl_is_attr(c->data.map->rhs) &&
+ (c->cast->type == tmpl_da(c->data.map->rhs)->type) &&
+ !tmpl_is_attr(c->data.map->lhs)) {
+ tmpl_t *tmp;
- tlen = tmpl_afrom_str(map, &map->lhs, lhs, lhs_len,
- lhs_type, &parse_rules, true);
- if (tlen < 0) {
- p = lhs - tlen;
- return_P(fr_strerror());
- }
+ tmp = c->data.map->rhs;
+ c->data.map->rhs = c->data.map->lhs;
+ c->data.map->lhs = tmp;
- if (tmpl_unknown_attr_add(map->lhs) < 0) {
- *error = "Failed defining attribute";
- return_lhs:
- talloc_free(c);
- return -(lhs - in);
- }
+ c->cast = NULL;
- map->op = op;
+ switch (c->data.map->op) {
+ case T_OP_CMP_EQ:
+ /* do nothing */
+ break;
- /*
- * This code converts '&Attr-1.2.3.4 == 0xabcdef'
- * into "&Known-Attribure == value"
- *
- * So that the debug output makes more sense, AND
- * so that the comparisons are done to known attributes.
- */
- if (tmpl_is_attr(map->lhs) &&
- (tmpl_da(map->lhs)->flags.is_raw || map->lhs->data.attribute.was_oid) &&
- map_cast_from_hex(map, rhs_type, rhs)) {
- /* do nothing */
+ case T_OP_LE:
+ c->data.map->op = T_OP_GE;
+ break;
- } else {
- /*
- * If the RHS is 0xabcdef... automatically cast it to octets
- * unless the LHS is an attribute of type octets, or an
- * integer type.
- */
- if (!c->cast && (rhs_type == T_BARE_WORD) &&
- (rhs[0] == '0') && (rhs[1] == 'x') &&
- ((slen & 0x01) == 0)) {
- if (slen == 2) {
- return_P("Empty octet string is invalid");
- }
+ case T_OP_LT:
+ c->data.map->op = T_OP_GT;
+ break;
- if ((map->lhs->type != TMPL_TYPE_ATTR) ||
- !((tmpl_da(map->lhs)->type == FR_TYPE_OCTETS) ||
- (tmpl_da(map->lhs)->type == FR_TYPE_UINT8) ||
- (tmpl_da(map->lhs)->type == FR_TYPE_UINT16) ||
- (tmpl_da(map->lhs)->type == FR_TYPE_UINT32) ||
- (tmpl_da(map->lhs)->type == FR_TYPE_UINT64))) {
- c->cast = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()),
- FR_CAST_BASE + FR_TYPE_OCTETS);
- }
- }
+ case T_OP_GE:
+ c->data.map->op = T_OP_LE;
+ break;
- tlen = tmpl_afrom_str(map, &map->rhs, rhs, rhs_len, rhs_type,
- &parse_rules, true);
- if (tlen < 0) {
- p = rhs - tlen;
- return_P(fr_strerror());
+ case T_OP_GT:
+ c->data.map->op = T_OP_LT;
+ break;
+
+ default:
+ fr_strerror_printf("Internal sanity check failed 1");
+ return -1;
}
- }
- if (tmpl_unknown_attr_add(map->rhs) < 0) {
- *error = "Failed defining attribute";
- return_rhs:
- talloc_free(c);
- return -(rhs - in);
+ /*
+ * This must have been parsed into TMPL_TYPE_DATA.
+ */
+ fr_assert(!tmpl_is_unresolved(c->data.map->rhs));
}
- /*
- * Unknown attributes get marked up for pass2.
- */
- if (tmpl_is_attr_unresolved(c->data.map->lhs) ||
- tmpl_is_attr_unresolved(c->data.map->rhs)) {
- c->pass2_fixup = PASS2_FIXUP_ATTR;
- }
+ } while (0);
+
+ /*
+ * Existence checks. We short-circuit static strings,
+ * too.
+ *
+ * FIXME: the data types should be in the template, too.
+ * So that we know where a literal came from.
+ *
+ * "foo" is NOT the same as 'foo' or a bare foo.
+ */
+ if (c->type == COND_TYPE_EXISTS) {
+ switch (c->data.vpt->type) {
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_XLAT_UNRESOLVED:
+ case TMPL_TYPE_ATTR:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_EXEC:
+ break;
-#ifdef HAVE_REGEX
/*
- * Parse the regex flags
+ * 'true' and 'false' are special strings
+ * which mean themselves.
+ *
+ * For integers, 0 is false, all other
+ * integers are true.
+ *
+ * For strings, '' and "" are false.
+ * 'foo' and "foo" are true.
+ *
+ * The str2tmpl function takes care of
+ * marking "%{foo}" as TMPL_TYPE_XLAT_UNRESOLVED, so
+ * the strings here are fixed at compile
+ * time.
+ *
+ * `exec` and "%{...}" are left alone.
+ *
+ * Bare words must be module return
+ * codes.
*/
- if (regex) {
- int err;
- ssize_t flen;
- fr_regex_flags_t regex_flags;
+ case TMPL_TYPE_UNRESOLVED:
+ if (!*c->data.vpt->name) {
+ c->type = COND_TYPE_FALSE;
+ TALLOC_FREE(c->data.vpt);
- memset(®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;
}
/*
- * <ipaddr>"foo" CMP &Attribute-Name The cast may
- * not be necessary, and we can re-write it so
- * that the attribute reference is on the LHS.
+ * don't skip the brace. We'll look for it later.
*/
- if (c->cast &&
- tmpl_is_attr(c->data.map->rhs) &&
- (c->cast->type == tmpl_da(c->data.map->rhs)->type) &&
- !tmpl_is_attr(c->data.map->lhs)) {
- tmpl_t *tmp;
-
- tmp = c->data.map->rhs;
- c->data.map->rhs = c->data.map->lhs;
- c->data.map->lhs = tmp;
-
- c->cast = NULL;
-
- switch (c->data.map->op) {
- case T_OP_CMP_EQ:
- /* do nothing */
- break;
+ goto unary;
- case T_OP_LE:
- c->data.map->op = T_OP_GE;
- break;
+ /*
+ * FOO - Existence check
+ */
+ } else if (!fr_sbuff_extend(&our_in)) {
+ if (brace) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
+ }
- case T_OP_LT:
- c->data.map->op = T_OP_GT;
- break;
+ goto unary;
+ }
- case T_OP_GE:
- c->data.map->op = T_OP_LE;
- break;
+ /*
+ * FOO && ... - Logical operator (existence check)
+ */
+ fr_sbuff_out_by_longest_prefix(&slen, &cond_op, cond_logical_op_table, &FR_SBUFF_NO_ADVANCE(&our_in), COND_NONE);
+ if ((cond_op == COND_AND) || (cond_op == COND_OR)) {
+ unary:
+ if (c->cast) {
+ fr_strerror_printf("Cannot do cast for existence check");
+ fr_sbuff_set(&our_in, &m_lhs_cast);
+ goto error;
+ }
- case T_OP_GT:
- c->data.map->op = T_OP_LT;
- break;
+ if (tmpl_contains_regex(lhs)) {
+ fr_strerror_printf("Unexpected regular expression");
+ fr_sbuff_set(&our_in, &m_lhs);
+ goto error;
+ }
- default:
- return_0("Internal sanity check failed 1");
+ /*
+ * Check to see if this is an rcode operand.
+ * These are common enough and specific enough
+ * to conditions that we handle them in the
+ * condition code specifically.
+ *
+ * Unary barewords can only be rcodes, so
+ * anything that's not a rcode an rcode
+ * is an error.
+ */
+ if (tmpl_is_unresolved(lhs) && (lhs->quote == T_BARE_WORD)) {
+ rlm_rcode_t rcode;
+
+ rcode = fr_table_value_by_str(rcode_table, lhs->data.unescaped, RLM_MODULE_UNKNOWN);
+ if (rcode == RLM_MODULE_UNKNOWN) {
+ fr_strerror_printf("Expected a module return code");
+ fr_sbuff_set(&our_in, &m_lhs);
+ goto error;
}
+ TALLOC_FREE(lhs);
- /*
- * This must have been parsed into TMPL_TYPE_DATA.
- */
- fr_assert(!tmpl_is_unresolved(c->data.map->rhs));
+ c->type = COND_TYPE_RCODE;
+ c->ci = cf_section_to_item(cs);
+ c->data.rcode = rcode;
+
+ goto closing_brace;
}
- } while (0);
+ c->type = COND_TYPE_EXISTS;
+ c->ci = cf_section_to_item(cs);
+ c->data.vpt = lhs;
+
+ goto closing_brace;
+ }
/*
- * Existence checks. We short-circuit static strings,
- * too.
- *
- * FIXME: the data types should be in the template, too.
- * So that we know where a literal came from.
- *
- * "foo" is NOT the same as 'foo' or a bare foo.
+ * Check for any other operator
*/
- if (c->type == COND_TYPE_EXISTS) {
- switch (c->data.vpt->type) {
- case TMPL_TYPE_XLAT_UNRESOLVED:
- case TMPL_TYPE_ATTR:
- case TMPL_TYPE_ATTR_UNRESOLVED:
- case TMPL_TYPE_LIST:
- case TMPL_TYPE_EXEC:
- break;
+ fr_sbuff_marker(&m_op, &our_in);
+ fr_sbuff_out_by_longest_prefix(&slen, &op, cond_cmp_op_table, &our_in, 0);
+ if (slen == 0) {
+ fr_strerror_printf("Invalid operator");
+ goto error;
+ }
+ fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
- /*
- * 'true' and 'false' are special strings
- * which mean themselves.
- *
- * For integers, 0 is false, all other
- * integers are true.
- *
- * For strings, '' and "" are false.
- * 'foo' and "foo" are true.
- *
- * The str2tmpl function takes care of
- * marking "%{foo}" as TMPL_TYPE_XLAT_UNRESOLVED, so
- * the strings here are fixed at compile
- * time.
- *
- * `exec` and "%{...}" are left alone.
- *
- * Bare words must be module return
- * codes.
- */
- case TMPL_TYPE_UNRESOLVED:
- if ((strcmp(c->data.vpt->name, "true") == 0) ||
- (strcmp(c->data.vpt->name, "1") == 0)) {
- c->type = COND_TYPE_TRUE;
- TALLOC_FREE(c->data.vpt);
+ {
+ vp_map_t *map;
+ tmpl_t *rhs;
- } else if ((strcmp(c->data.vpt->name, "false") == 0) ||
- (strcmp(c->data.vpt->name, "0") == 0)) {
- c->type = COND_TYPE_FALSE;
- TALLOC_FREE(c->data.vpt);
+ /*
+ * The next thing should now be a comparison operator.
+ */
+ c->type = COND_TYPE_MAP;
+ c->ci = cf_section_to_item(cs);
- } else if (!*c->data.vpt->name) {
- c->type = COND_TYPE_FALSE;
- TALLOC_FREE(c->data.vpt);
+ switch (op) {
+#ifdef HAVE_REGEX
+ case T_OP_REG_NE:
+ case T_OP_REG_EQ:
+ break;
+#endif
- } else if ((lhs_type == T_SINGLE_QUOTED_STRING) ||
- (lhs_type == T_DOUBLE_QUOTED_STRING)) {
- c->type = COND_TYPE_TRUE;
- TALLOC_FREE(c->data.vpt);
+ case T_OP_CMP_FALSE:
+ case T_OP_CMP_TRUE:
+ if (lhs->quote != T_BARE_WORD) {
+ fr_strerror_printf("Cannot use %s on a string",
+ fr_table_str_by_value(cond_cmp_op_table, op, "<INVALID>"));
+ fr_sbuff_set(&our_in, &m_op);
+ goto error;
+ }
+ break;
+ default:
+ break;
+ }
- } else if (lhs_type == T_BARE_WORD) {
- int rcode;
- bool zeros = true;
- char const *q;
+ if (!fr_sbuff_extend(&our_in)) {
+ fr_strerror_printf("Expected text after operator");
+ goto error;
+ }
- for (q = c->data.vpt->name;
- *q != '\0';
- q++) {
- if (!isdigit((int) *q)) {
- break;
- }
- if (*q != '0') zeros = false;
- }
+ MEM(c->data.map = map = talloc(c, vp_map_t));
- /*
- * It's all digits, and therefore
- * 'false' if zero, and 'true' otherwise.
- */
- if (!*q) {
- if (zeros) {
- c->type = COND_TYPE_FALSE;
- } else {
- c->type = COND_TYPE_TRUE;
- }
- TALLOC_FREE(c->data.vpt);
- break;
- }
+ /*
+ * Grab the RHS
+ */
+ fr_sbuff_marker(&m_rhs_cast, &our_in);
+ slen = cond_tokenize_operand(c, &rhs, &m_rhs, &our_in, &our_t_rules);
+ if (!rhs) {
+ fr_sbuff_advance(&our_in, slen * -1);
+ goto error;
+ }
+ if (tmpl_is_attr_unresolved(rhs)) c->pass2_fixup = PASS2_FIXUP_ATTR;
+
+ *map = (vp_map_t) {
+ .ci = cf_section_to_item(cs),
+ .lhs = lhs,
+ .op = op,
+ .rhs = rhs
+ };
+
+ if (rhs->cast != FR_TYPE_INVALID) {
+ fr_strerror_printf("Unexpected cast");
+ fr_sbuff_set(&our_in, &m_rhs_cast);
+ goto error;
+ }
- /*
- * Allow &Foo-Bar where Foo-Bar is an attribute
- * defined by a module.
- */
- if (c->pass2_fixup == PASS2_FIXUP_ATTR) {
- break;
- }
+ /*
+ * Binary expressions can't contain lists
+ */
+ if (tmpl_is_list(lhs)) {
+ fr_strerror_printf("Cannot use list references in condition");
+ fr_sbuff_set(&our_in, &m_lhs);
+ goto error;
+ }
- rcode = fr_table_value_by_str(allowed_return_codes,
- c->data.vpt->name, 0);
- if (!rcode) {
- return_0("Expected a module return code");
- }
- }
+ if (tmpl_is_list(rhs)) {
+ fr_strerror_printf("Cannot use list references in condition");
+ fr_sbuff_set(&our_in, &m_rhs);
+ goto error;
+ }
- /*
- * Else lhs_type==T_INVALID, and this
- * node was made by promoting a child
- * which had already been normalized.
- */
- break;
+ if (((op == T_OP_REG_EQ) || (op == T_OP_REG_NE)) &&
+ (!tmpl_contains_regex(lhs) && !tmpl_contains_regex(rhs))) {
+ fr_strerror_printf("Expected regular expression");
+ fr_sbuff_set(&our_in, &m_rhs);
+ goto error;
+ }
- case TMPL_TYPE_DATA:
- return_0("Cannot use data here");
+ if (((op != T_OP_REG_EQ) && (op != T_OP_REG_NE)) &&
+ (tmpl_contains_regex(lhs) || tmpl_contains_regex(rhs))) {
+ fr_strerror_printf("Unexpected regular expression"); /* Fixme should point to correct operand */
+ fr_sbuff_set(&our_in, &m_rhs);
+ goto error;
+ }
- default:
- return_0("Internal sanity check failed 2");
+ fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+
+ /*
+ * Check cast type. We can have the RHS
+ * a string if the LHS has a cast. But
+ * if the RHS is an attr, it MUST be the
+ * same type as the LHS.
+ */
+ if (c->cast) {
+ slen = cond_check_cast(c, fr_sbuff_start(&our_in), m_lhs.p, m_rhs.p);
+ if (slen <= 0) {
+ fr_sbuff_set(&our_in, our_in.start + (slen * -1));
+ goto error;
+ }
+ } else {
+ slen = cond_check_attrs(c, &m_lhs, &m_rhs);
+ if (slen <= 0) {
+ fr_sbuff_set(&our_in, our_in.start + (slen * -1));
+ goto error;
+ }
}
- }
+ } /* parse OP RHS */
+closing_brace:
/*
- * !TRUE -> FALSE
+ * ...COND)
*/
- if (c->type == COND_TYPE_TRUE) {
- if (c->negate) {
- c->negate = false;
- c->type = COND_TYPE_FALSE;
+ if (fr_sbuff_is_char(&our_in, ')')) {
+ if (!brace) {
+ fr_strerror_printf("Unexpected closing brace");
+ goto error;
}
+ fr_sbuff_advance(&our_in, 1);
+ fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX);
+ goto done;
}
/*
- * !FALSE -> TRUE
+ * End of string is allowed, unless we're still looking
+ * for closing braces.
*/
- if (c->type == COND_TYPE_FALSE) {
- if (c->negate) {
- c->negate = false;
- c->type = COND_TYPE_TRUE;
+ if (!fr_sbuff_extend(&our_in)) {
+ if (brace) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
}
+ goto done;
}
/*
- * true && FOO --> FOO
+ * We've parsed all of the condition, stop.
*/
- if ((c->type == COND_TYPE_TRUE) &&
- (c->next_op == COND_AND)) {
- fr_cond_t *next;
-
- next = talloc_steal(ctx, c->next);
- c->next = NULL;
+ if (brace == 0) {
+ if (fr_sbuff_is_space(&our_in)) goto done;
- talloc_free(c);
- c = next;
+ /*
+ * Open a section, it's OK to be done.
+ */
+ if (fr_sbuff_is_char(&our_in, '{')) goto done;
}
/*
- * false && FOO --> false
+ * Allow ((a == b) && (b == c))
*/
- if ((c->type == COND_TYPE_FALSE) &&
- (c->next_op == COND_AND)) {
- talloc_free(c->next);
- c->next = NULL;
- c->next_op = COND_NONE;
+ fr_sbuff_out_by_longest_prefix(&slen, &cond_op, cond_logical_op_table,
+ &our_in, COND_NONE);
+ if (slen == 0) {
+ fr_strerror_printf("Unexpected text after condition");
+ goto error;
}
/*
- * false || FOO --> FOO
+ * Recurse to parse the next condition.
*/
- if ((c->type == COND_TYPE_FALSE) &&
- (c->next_op == COND_OR)) {
- fr_cond_t *next;
+ c->next_op = cond_op;
- next = talloc_steal(ctx, c->next);
- c->next = NULL;
+ /*
+ * May still be looking for a closing brace.
+ */
+ slen = cond_tokenize(c, &c->next, cs, &our_in, brace, &our_t_rules);
+ if (slen <= 0) {
+ fr_sbuff_advance(&our_in, slen * -1);
+ goto error;
+ }
+done:
+ if (cond_normalise(ctx, lhs ? lhs->quote : T_INVALID, &c) < 0) {
talloc_free(c);
- c = next;
+ return 0;
}
- /*
- * true || FOO --> true
- */
- if ((c->type == COND_TYPE_TRUE) &&
- (c->next_op == COND_OR)) {
- talloc_free(c->next);
- c->next = NULL;
- c->next_op = COND_NONE;
- }
+ *out = c;
- *pcond = c;
- return p - in;
+ return fr_sbuff_set(in, &our_in);
}
/** Tokenize a conditional check
*
* @param[in] cs current CONF_SECTION and talloc ctx
* @param[out] head the parsed condition structure
- * @param[out] error the parse error (if any)
* @param[in] dict dictionary to resolve attributes in.
- * @param[in] in the start of the string to process. Should be "(..."
- * @param[in] inlen the length of the string to process.
+ * @param[in] in the start of the string to process.
* @return
* - Length of the string skipped.
* - < 0 (the offset to the offending error) on error.
*/
-ssize_t fr_cond_tokenize(CONF_SECTION *cs,
- fr_cond_t **head, char const **error,
- fr_dict_t const *dict, char const *in, size_t inlen)
+ssize_t fr_cond_tokenize(CONF_SECTION *cs, fr_cond_t **head, fr_dict_t const *dict, fr_sbuff_t *in)
{
- return cond_tokenize(cs, cs, head, error, in, inlen, 0, &(tmpl_rules_t){ .dict_def = dict });
+ char buffer[8192];
+ ssize_t diff, slen;
+
+ if (!cf_expand_variables(cf_filename(cs), cf_lineno(cs), cf_item_to_section(cf_parent(cs)),
+ buffer, sizeof(buffer),
+ fr_sbuff_current(in), fr_sbuff_remaining(in), NULL)) {
+ fr_strerror_printf("Failed expanding configuration variable");
+ return 0;
+ }
+
+ diff = fr_sbuff_remaining(in) - strlen(buffer); /* Hack so that we appear to consume more of the string */
+ slen = cond_tokenize(cs, head, cs, &FR_SBUFF_IN(buffer, strlen(buffer)), 0,
+ &(tmpl_rules_t){
+ .dict_def = dict,
+ .allow_unresolved = true,
+ .allow_unknown = true,
+ .allow_foreign = (dict == NULL) /* Allow foreign attributes if we have no dict */
+ });
+ if (slen < 0) return slen;
+
+ return slen + diff;
}
/*
case COND_TYPE_INVALID:
return false;
+ case COND_TYPE_RCODE:
case COND_TYPE_EXISTS:
case COND_TYPE_MAP:
case COND_TYPE_TRUE:
}
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);
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);
}
*
*
* @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;
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);
* 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;
* 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);
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;
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;
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);
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);
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;
}
*/
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.
/*
* 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)) {
* @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);
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);
* @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
* - 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)
{
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);
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;
}
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);
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;
goto error;
}
n->op = map->op;
- n->tag = tmpl_tag(map->lhs);
*out = n;
break;
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);
}
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;
}
}
}
n->op = map->op;
- n->tag = tmpl_tag(map->lhs);
*out = n;
MAP_VERIFY(map);
#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.
* This allows the syntax like:
* - "Attr-%{number}" := "value"
*/
- case TMPL_TYPE_XLAT_UNRESOLVED:
case TMPL_TYPE_XLAT:
case TMPL_TYPE_EXEC:
{
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));
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.
*/
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)) {
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
{
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);
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;
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);
}
/*
*/
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);
* 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);
}
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;
* map name.
*/
if (vp) {
- tmpl_snprint(NULL, buffer, sizeof(buffer), map->lhs);
+ tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL); /* Fixme - bad escaping */
RDEBUG2("%s%s %s %s", buffer, vp->da->name, fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"), rhs);
break;
}
FALL_THROUGH;
case TMPL_TYPE_ATTR:
- tmpl_snprint(NULL, buffer, sizeof(buffer), map->lhs);
+ tmpl_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map->lhs, TMPL_ATTR_REF_PREFIX_YES, NULL);
RDEBUG2("%s %s %s", buffer, fr_table_str_by_value(fr_tokens_table, vp ? vp->op : map->op, "<INVALID>"), rhs);
break;
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
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));
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;
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;
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;
* 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;
/*
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;
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;
* Just print the value being assigned
*/
default:
- case TMPL_TYPE_XLAT_UNRESOLVED:
case TMPL_TYPE_XLAT:
case TMPL_TYPE_UNRESOLVED:
case TMPL_TYPE_DATA:
/*
* 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;
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) {
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 */
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);
/*
* 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
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) {
*/
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.
*/
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;
}
/*
#include <freeradius-devel/server/rcode.h>
fr_table_num_sorted_t const rcode_table[] = {
- { L("disallow"), RLM_MODULE_DISALLOW },
+ { L("disallow"), RLM_MODULE_DISALLOW },
{ L("fail"), RLM_MODULE_FAIL },
{ L("handled"), RLM_MODULE_HANDLED },
{ L("invalid"), RLM_MODULE_INVALID },
{ L("noop"), RLM_MODULE_NOOP },
- { L("notfound"), RLM_MODULE_NOTFOUND },
- { L("ok"), RLM_MODULE_OK },
+ { L("notfound"), RLM_MODULE_NOTFOUND },
+ { L("ok"), RLM_MODULE_OK },
{ L("reject"), RLM_MODULE_REJECT },
{ L("updated"), RLM_MODULE_UPDATED },
};
* 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",
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);
/*
* 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 */
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);
#include <freeradius-devel/util/hex.h>
#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/sbuff.h>
+
#include <ctype.h>
/** Map #tmpl_type_t values to descriptive strings
*/
-fr_table_num_sorted_t const tmpl_type_table[] = {
- { L("uninitialised"), TMPL_TYPE_UNINITIALISED },
+fr_table_num_ordered_t const tmpl_type_table[] = {
+ { L("uninitialised"), TMPL_TYPE_UNINITIALISED },
- { L("null"), TMPL_TYPE_NULL },
- { L("data"), TMPL_TYPE_DATA },
+ { L("null"), TMPL_TYPE_NULL },
+ { L("data"), TMPL_TYPE_DATA },
- { L("attr"), TMPL_TYPE_ATTR },
- { L("list"), TMPL_TYPE_LIST },
+ { L("attr"), TMPL_TYPE_ATTR },
+ { L("list"), TMPL_TYPE_LIST },
- { L("exec"), TMPL_TYPE_EXEC },
- { L("xlat"), TMPL_TYPE_XLAT },
+ { L("exec"), TMPL_TYPE_EXEC },
+ { L("xlat"), TMPL_TYPE_XLAT },
- { L("regex"), TMPL_TYPE_REGEX },
+ { L("regex"), TMPL_TYPE_REGEX },
+ { L("regex-uncompiled"), TMPL_TYPE_REGEX_UNCOMPILED },
+ { L("regex-xlat"), TMPL_TYPE_REGEX_XLAT },
- { L("literal"), TMPL_TYPE_UNRESOLVED },
- { L("attr-unparsed"), TMPL_TYPE_ATTR_UNRESOLVED },
- { L("xlat-unparsed"), TMPL_TYPE_XLAT_UNRESOLVED },
- { L("regex-unparsed"), TMPL_TYPE_REGEX_UNRESOLVED }
+ { L("unresolved"), TMPL_TYPE_UNRESOLVED },
+ { L("attr-unresolved"), TMPL_TYPE_ATTR_UNRESOLVED },
+ { L("exec-unresolved"), TMPL_TYPE_EXEC_UNRESOLVED },
+ { L("xlat-unresolved"), TMPL_TYPE_XLAT_UNRESOLVED },
+ { L("regex-unresolved"), TMPL_TYPE_REGEX_XLAT_UNRESOLVED }
};
size_t tmpl_type_table_len = NUM_ELEMENTS(tmpl_type_table);
+/** Attr ref types
+ */
+fr_table_num_ordered_t const attr_ref_table[] = {
+ { L("normal"), TMPL_ATTR_TYPE_NORMAL },
+ { L("unknown"), TMPL_ATTR_TYPE_UNKNOWN },
+ { L("unresolved"), TMPL_ATTR_TYPE_UNRESOLVED }
+};
+size_t attr_ref_table_len = NUM_ELEMENTS(attr_ref_table);
+
/** Map keywords to #pair_list_t values
*/
fr_table_num_ordered_t const pair_list_table[] = {
/** Special attribute reference indexes
*/
static fr_table_num_sorted_t const attr_num_table[] = {
- { L("*"), NUM_ALL },
- { L("#"), NUM_COUNT },
+ { L("*"), NUM_ALL },
+ { L("#"), NUM_COUNT },
{ L("any"), NUM_ANY },
- { L("n"), NUM_LAST }
+ { L("n"), NUM_LAST }
};
static size_t attr_num_table_len = NUM_ELEMENTS(attr_num_table);
+static void tmpl_attr_ref_to_raw(tmpl_t *vpt, tmpl_attr_t *ref);
+
+void tmpl_attr_debug(tmpl_t const *vpt)
+{
+ tmpl_attr_t *ar = NULL;
+ tmpl_request_t *rr = NULL;
+ unsigned int i = 0;
+ char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
+
+ switch (vpt->type) {
+ case TMPL_TYPE_ATTR:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ case TMPL_TYPE_LIST:
+ break;
+
+ default:
+ INFO("%s can't print tmpls of type %s", __FUNCTION__,
+ fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
+ return;
+ }
+
+ INFO("tmpl_t %s (%.8x) \"%pV\" (%p)",
+ fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"),
+ vpt->type,
+ fr_box_strvalue_len(vpt->name, vpt->len), vpt);
+
+ INFO("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
+
+ INFO("request references:");
+
+ /*
+ * Print all the request references
+ */
+ while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+ INFO("\t[%u] %s (%u)", i,
+ fr_table_str_by_value(request_ref_table, rr->request, "<INVALID>"), rr->request);
+ i++;
+ }
+ i = 0;
+
+ INFO("list: %s", fr_table_str_by_value(pair_list_table, vpt->data.attribute.list, "<INVALID>"));
+
+ INFO("attribute references:");
+ /*
+ * Print all the attribute references
+ */
+ while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+ snprintf(buffer, sizeof(buffer), "%i", ar->num);
+
+ switch (ar->type) {
+ case TMPL_ATTR_TYPE_NORMAL:
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ if (!ar->da) {
+ INFO("\t[%u] %s null%s%s%s",
+ i,
+ fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+ ar->num != NUM_ANY ? "[" : "",
+ ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+ ar->num != NUM_ANY ? "]" : "");
+ goto next;
+ }
+
+ INFO("\t[%u] %s %s %s%s%s%s (%u)",
+ i,
+ fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+ fr_table_str_by_value(fr_value_box_type_table, ar->da->type, "<INVALID>"),
+ ar->da->name,
+ ar->num != NUM_ANY ? "[" : "",
+ ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+ ar->num != NUM_ANY ? "]" : "",
+ ar->da->attr
+ );
+ if (ar->da->parent) INFO("\t parent : %s", ar->da->parent->name);
+ INFO("\t is_raw : %pV", fr_box_bool(ar->da->flags.is_raw));
+ INFO("\t is_unknown : %pV", fr_box_bool(ar->da->flags.is_unknown));
+ break;
+
+
+ case TMPL_ATTR_TYPE_UNRESOLVED:
+ INFO("\t[%u] %s %s%s%s%s - unresolved",
+ i,
+ fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"),
+ ar->ar_unresolved,
+ ar->num != NUM_ANY ? "[" : "",
+ ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
+ ar->num != NUM_ANY ? "]" : "");
+ break;
+
+ default:
+ INFO("\t[%u] Bad type %s(%u)",
+ i, fr_table_str_by_value(attr_ref_table, ar->type, "<INVALID>"), ar->type);
+ break;
+ }
+
+ next:
+ i++;
+ }
+}
+
+void tmpl_debug(tmpl_t const *vpt)
+{
+ switch (vpt->type) {
+ case TMPL_TYPE_ATTR:
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ tmpl_attr_debug(vpt);
+ return;
+
+ default:
+ break;
+ }
+
+ INFO("tmpl_t %s (%.8x) \"%pR\" (%p)",
+ fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"),
+ vpt->type,
+ fr_box_strvalue_len(vpt->name, vpt->len), vpt);
+
+ INFO("\tquote : %s", fr_table_str_by_value(fr_token_quotes_table, vpt->quote, "<INVALID>"));
+ switch (vpt->type) {
+ case TMPL_TYPE_NULL:
+ return;
+
+ case TMPL_TYPE_DATA:
+ INFO("\ttype : %s", fr_table_str_by_value(fr_value_box_type_table,
+ tmpl_value_type(vpt), "<INVALID>"));
+ INFO("\tlen : %zu", tmpl_value_length(vpt));
+ INFO("\tvalue : %pV", tmpl_value(vpt));
+
+ if (tmpl_value_enumv(vpt)) INFO("\tenumv : %s (%p)",
+ tmpl_value_enumv(vpt)->name, tmpl_value_enumv(vpt));
+ return;
+
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_EXEC:
+ case TMPL_TYPE_REGEX_XLAT:
+ {
+ char *str;
+
+ xlat_aprint(NULL, &str, tmpl_xlat(vpt), NULL);
+
+ INFO("\texpansion : %pR", fr_box_strvalue_buffer(str));
+
+ talloc_free(str);
+ }
+ break;
+
+ case TMPL_TYPE_REGEX:
+ {
+ INFO("\tpattern : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
+ }
+ break;
+
+ default:
+ if (tmpl_needs_resolving(vpt)) {
+ if (tmpl_is_unresolved(vpt)) {
+ INFO("\tunescaped : %pR", fr_box_strvalue_buffer(vpt->data.unescaped));
+ INFO("\tlen : %zu", talloc_array_length(vpt->data.unescaped) - 1);
+ } else {
+ INFO("\tunresolved : %pR", fr_box_strvalue_len(vpt->name, vpt->len));
+ INFO("\tlen : %zu", vpt->len);
+ }
+ } else {
+ INFO("debug nyi");
+ }
+ break;
+ }
+}
+
/** @name Parse list and request qualifiers to #pair_list_t and #request_ref_t values
*
* These functions also resolve #pair_list_t and #request_ref_t values to #REQUEST
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 ':':
{
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;
* @{
*/
-/** 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);
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.
*/
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) {
return 0;
}
-void tmpl_attr_debug(tmpl_t const *vpt)
-{
- tmpl_attr_t *ar = NULL;
- tmpl_request_t *rr = NULL;
- unsigned int i = 0;
- char buffer[sizeof(STRINGIFY(INT16_MAX)) + 1];
-
- switch (vpt->type) {
- case TMPL_TYPE_ATTR:
- case TMPL_TYPE_ATTR_UNRESOLVED:
- case TMPL_TYPE_LIST:
- break;
-
- default:
- INFO("%s can't print tmpls of type %s", __FUNCTION__,
- fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
- return;
- }
-
- INFO("tmpl_t %s %s (%p)", fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"), vpt->name, vpt);
- INFO("request references:");
-
- /*
- * Print all the request references
- */
- while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
- INFO("\t[%u] %s (%u)", i,
- fr_table_str_by_value(request_ref_table, rr->request, "<INVALID>"), rr->request);
- i++;
- }
- i = 0;
-
- INFO("attribute references:");
- /*
- * Print all the attribute references
- */
- while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
- snprintf(buffer, sizeof(buffer), "%i", ar->num);
-
- switch (ar->type) {
- case TMPL_ATTR_TYPE_NORMAL:
- case TMPL_ATTR_TYPE_UNKNOWN:
- if (!ar->da) {
- INFO("\t[%u] null%s%s%s",
- i,
- ar->num != NUM_ANY ? "[" : "",
- ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
- ar->num != NUM_ANY ? "]" : "");
- goto next;
- }
-
-
- INFO("\t[%u] %s %s(%u)%s%s%s%s",
- i,
- fr_table_str_by_value(fr_value_box_type_table, ar->da->type, "<INVALID>"),
- ar->da->name,
- ar->da->attr,
- ar->num != NUM_ANY ? "[" : "",
- ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
- ar->num != NUM_ANY ? "]" : "",
- ar->da->flags.is_unknown ? " - is_unknown" : ""
- );
- break;
-
-
- case TMPL_ATTR_TYPE_UNRESOLVED:
- INFO("\t[%u] %s%s%s%s - unparsed",
- i, ar->unknown.name,
- ar->num != NUM_ANY ? "[" : "",
- ar->num != NUM_ANY ? fr_table_str_by_value(attr_num_table, ar->num, buffer) : "",
- ar->num != NUM_ANY ? "]" : "");
- break;
-
- default:
- INFO("\t[%u] Bad type %u", i, ar->type);
- break;
- }
-
- next:
- i++;
- }
-}
-
/** Copy a list of attribute and request references from one tmpl to another
*
*/
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:
default:
if (!fr_cond_assert(0)) return -1;
}
-
- dst_ar->ar_tag = src_ar->ar_tag;
dst_ar->ar_num = src_ar->ar_num;
}
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));
}
/*
return 0;
}
-/** Convert an abstract da into a concrete one
- *
- * Usually used to fixup combo ip addresses
- */
-int tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type)
-{
- fr_dict_attr_t const *abstract;
- fr_dict_attr_t const *concrete;
- tmpl_attr_t *ref;
-
- tmpl_assert_type(tmpl_is_attr(vpt));
-
- abstract = tmpl_da(vpt);
- if (abstract->type != FR_TYPE_COMBO_IP_ADDR) {
- fr_strerror_printf("Abstract attribute \"%s\" is of incorrect type '%s'", abstract->name,
- fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"));
- return -1;
- }
-
- concrete = fr_dict_attr_by_type(abstract, type);
- if (!concrete) {
- fr_strerror_printf("Can't convert abstract type '%s' to concrete type '%s'",
- fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"),
- fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"));
- return -1;
- }
-
- ref = fr_dlist_tail(&vpt->data.attribute.ar);
- ref->da = concrete;
-
- TMPL_ATTR_VERIFY(vpt);
-
- return 0;
-}
-
-/** Covert the leaf attribute of a tmpl to a unknown/raw type
- *
- */
-void tmpl_attr_to_raw(tmpl_t *vpt)
-{
- tmpl_attr_t *ref;
-
- ref = fr_dlist_tail(&vpt->data.attribute.ar);
- switch (ref->type) {
- case TMPL_ATTR_TYPE_NORMAL:
- {
- char buffer[256] = "Attr-";
- char *p = buffer + strlen(buffer);
- char *end = buffer + sizeof(buffer);
- size_t len;
- fr_dict_attr_t *da;
-
- len = fr_dict_print_attr_oid(NULL, p, end - p, NULL, ref->da);
- p += len;
-
- ref->da = ref->ar_unknown = da = fr_dict_unknown_acopy(vpt, ref->da);
- ref->ar_unknown->type = FR_TYPE_OCTETS;
- ref->ar_unknown->flags.is_raw = 1;
-
- talloc_const_free(da->name);
- MEM(da->name = talloc_bstrndup(da, buffer, p - buffer));
-
- ref->type = TMPL_ATTR_TYPE_UNKNOWN;
- }
- break;
-
- case TMPL_ATTR_TYPE_UNKNOWN:
- ref->ar_unknown->type = FR_TYPE_OCTETS;
- ref->ar_unknown->flags.is_raw = 1;
- break;
-
- case TMPL_ATTR_TYPE_UNRESOLVED:
- fr_assert(0);
- break;
- }
-
- TMPL_ATTR_VERIFY(vpt);
-}
-
/** Replace the current attribute reference
*
*/
* 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;
}
}
/*
- * 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;
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);
}
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;
fr_dlist_talloc_reverse_free(&vpt->data.attribute.ar);
}
- ref = tmpl_ar_add(vpt, TMPL_ATTR_TYPE_UNRESOLVED);
+ ref = tmpl_attr_ref_add(vpt, TMPL_ATTR_TYPE_UNRESOLVED);
ref->ar_unresolved = talloc_strndup(vpt, name, len);
TMPL_ATTR_VERIFY(vpt);
}
-/** Resolve an undefined attribute using the specified rules
- *
- */
-int tmpl_attr_resolve_unresolved(tmpl_t *vpt, tmpl_rules_t const *rules)
-{
- tmpl_attr_t *ar = NULL;
- fr_dict_attr_t const *parent = NULL;
- fr_dict_attr_t const *da;
-
- fr_assert_msg(tmpl_is_attr_unresolved(vpt), "Expected tmpl type 'undefined-attr', got '%s'",
- fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
-
- /*
- * First ref is special as it can resolve in the
- * internal dictionary or the protocol specific
- * dictionary.
- */
- ar = fr_dlist_head(&vpt->data.attribute.ar);
- if (fr_dict_attr_by_qualified_name(&da, rules->dict_def,
- ar->unknown.name, true) != FR_DICT_ATTR_OK) {
- parent = fr_dict_root(rules->dict_def);
- goto unknown;
- } else {
- ar->da = da;
- ar->type = TMPL_ATTR_TYPE_NORMAL;
- parent = ar->da;
- }
-
- /*
- * Loop, resolving each unparsed attribute in turn
- */
- while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
- ssize_t slen;
-
- slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent,
- &FR_SBUFF_IN(ar->unknown.name, strlen(ar->unknown.name)),
- false);
- if (slen <= 0) {
- fr_dict_attr_t *unknown_da;
-
- unknown:
- /*
- * Can't find it under it's regular name. Try an unknown attribute.
- */
- slen = fr_dict_unknown_afrom_oid_str(vpt, &unknown_da, parent, ar->unknown.name);
- if ((slen <= 0) || (ar->unknown.name[slen] != '\0')) {
- fr_strerror_printf_push("Failed resolving undefined attribute");
- return -1;
- }
-
- ar->type = TMPL_ATTR_TYPE_UNKNOWN;
- ar->da = ar->unknown.da = unknown_da;
- parent = ar->da;
- continue;
- }
-
- /*
- * Known attribute, just rewrite.
- */
- ar->da = da;
- ar->type = TMPL_ATTR_TYPE_NORMAL;
- parent = ar->da;
- }
-
- vpt->type = TMPL_TYPE_ATTR;
- TMPL_ATTR_VERIFY(vpt);
-
- return 0;
-}
-
-/** Set the request for an attribute ref
+/** Set the request for an attribute ref
*
*/
void tmpl_attr_set_request(tmpl_t *vpt, request_ref_t request)
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);
}
/** 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
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;
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
*
* Defaults are used if a NULL rules pointer is passed to the parsing function.
*/
-static tmpl_rules_t const default_rules = {
+static tmpl_rules_t const default_attr_ref_rules = {
.request_def = REQUEST_CURRENT,
.list_def = PAIR_LIST_REQUEST
};
-/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+/** Default formatting rules
*
- * @param[in,out] ctx to allocate #tmpl_t in.
- * @param[out] err May be NULL. Provides the exact error that the parser hit
- * when processing the attribute ref.
- * @param[out] out Where to write pointer to new #tmpl_t.
- * @param[in] name of attribute including #request_ref_t and #pair_list_t qualifiers.
- * If only #request_ref_t #pair_list_t qualifiers are found,
- * a #TMPL_TYPE_LIST #tmpl_t will be produced.
- * @param[in] name_len Length of name, or -1 to do strlen()
- * @param[in] rules Rules which control parsing:
- * - dict_def The default dictionary to use if attributes
- * are unqualified.
- * - request_def The default #REQUEST to set if no
- * #request_ref_t qualifiers are found in name.
- * - list_def The default list to set if no #pair_list_t
- * qualifiers are found in the name.
- * - allow_unknown If true attributes in the format accepted by
- * #fr_dict_unknown_afrom_oid_substr will be allowed,
- * even if they're not in the main dictionaries.
- * If an unknown attribute is found a #TMPL_TYPE_ATTR
- * #tmpl_t will be produced.
- * If #tmpl_afrom_attr_substr is being called on
- * startup, the #tmpl_t may be passed to
- * #tmpl_unknown_attr_add to
- * add the unknown attribute to the main dictionary.
- * If the unknown attribute is not added to
- * the main dictionary the #tmpl_t cannot be used
- * to search for a #VALUE_PAIR in a #REQUEST.
- * - allow_unresolved If true, we don't generate a parse error on
- * unknown attributes. If an unknown attribute is
- * found a #TMPL_TYPE_ATTR_UNRESOLVED
- * #tmpl_t will be produced.
- * - allow_foreign If true, allow attribute names to be qualified
- * with a protocol outside of the passed dict_def.
- * - disallow_internal If true, don't allow fallback to internal
- * attributes.
+ * Control token termination, escaping and how the tmpl is printed.
+ */
+fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_unquoted = {
+
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_double_unquoted = {
+ .escapes = &fr_value_unescape_double
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_single_unquoted = {
+ .escapes = &fr_value_unescape_single
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_unquoted = {
+ .escapes = &fr_value_unescape_solidus
+};
+
+fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_unquoted = {
+ .escapes = &fr_value_unescape_backtick
+};
+
+/** Parse rules for non-quoted strings
*
- * @see REMARKER to produce pretty error markers from the return value.
+ * These parse rules should be used for processing escape sequences in
+ * data from external data sources like SQL databases and REST APIs.
*
- * @return
- * - <= 0 on error (offset as negative integer)
- * - > 0 on success (number of bytes parsed).
+ * They do not include terminals to stop parsing as it assumes the values
+ * are discreet, and not wrapped in quotes.
*/
-ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
- tmpl_t **out, char const *name, ssize_t name_len, tmpl_rules_t const *rules)
-{
- char const *p, *q;
- long num;
- ssize_t slen;
- tmpl_t *vpt;
- request_ref_t request_ref;
- pair_list_t list;
- fr_dict_attr_t const *da;
- bool is_raw = false;
+fr_sbuff_parse_rules_t const *tmpl_parse_rules_unquoted[T_TOKEN_LAST] = {
+ [T_BARE_WORD] = &tmpl_parse_rules_bareword_unquoted,
+ [T_DOUBLE_QUOTED_STRING] = &tmpl_parse_rules_double_unquoted,
+ [T_SINGLE_QUOTED_STRING] = &tmpl_parse_rules_single_unquoted,
+ [T_SOLIDUS_QUOTED_STRING] = &tmpl_parse_rules_solidus_unquoted,
+ [T_BACK_QUOTED_STRING] = &tmpl_parse_rules_backtick_unquoted
+};
- if (!rules) rules = &default_rules; /* Use the defaults */
+fr_sbuff_parse_rules_t const tmpl_parse_rules_bareword_quoted = {
+ .escapes = &(fr_sbuff_unescape_rules_t){
+ .chr = '\\',
+ /*
+ * Allow barewords to contain whitespace
+ * if they're escaped.
+ */
+ .subs = {
+ ['\t'] = '\t',
+ ['\n'] = '\n',
+ [' '] = ' '
+ },
+ .do_hex = false,
+ .do_oct = false
+ },
+ .terminals = &FR_SBUFF_TERMS(
+ L("\t"),
+ L("\n"),
+ L(" ")
+ )
+};
- if (err) *err = ATTR_REF_ERROR_NONE;
+fr_sbuff_parse_rules_t const tmpl_parse_rules_double_quoted = {
+ .escapes = &fr_value_unescape_double,
+ .terminals = &FR_SBUFF_TERM("\"")
+};
- if (name_len < 0) name_len = strlen(name);
+fr_sbuff_parse_rules_t const tmpl_parse_rules_single_quoted = {
+ .escapes = &fr_value_unescape_single,
+ .terminals = &FR_SBUFF_TERM("'")
+};
- p = name;
+fr_sbuff_parse_rules_t const tmpl_parse_rules_solidus_quoted = {
+ .escapes = &fr_value_unescape_solidus,
+ .terminals = &FR_SBUFF_TERM("/")
+};
- if (!*p) {
- fr_strerror_printf("Empty attribute reference");
- if (err) *err = ATTR_REF_ERROR_EMPTY;
- return 0;
- }
+fr_sbuff_parse_rules_t const tmpl_parse_rules_backtick_quoted = {
+ .escapes = &fr_value_unescape_backtick,
+ .terminals = &FR_SBUFF_TERM("`")
+};
+
+/** Parse rules for quoted strings
+ *
+ * These parse rules should be used for internal parsing functions that
+ * are working with configuration files.
+ *
+ * They include appropriate quote terminals to force functions parsing
+ * quoted strings to return when they reach a quote character.
+ */
+fr_sbuff_parse_rules_t const *tmpl_parse_rules_quoted[T_TOKEN_LAST] = {
+ [T_BARE_WORD] = &tmpl_parse_rules_bareword_quoted,
+ [T_DOUBLE_QUOTED_STRING] = &tmpl_parse_rules_double_quoted,
+ [T_SINGLE_QUOTED_STRING] = &tmpl_parse_rules_single_quoted,
+ [T_SOLIDUS_QUOTED_STRING] = &tmpl_parse_rules_solidus_quoted,
+ [T_BACK_QUOTED_STRING] = &tmpl_parse_rules_backtick_quoted
+};
+
+/** Verify, after skipping whitespace, that a substring ends in a terminal char, or ends without further chars
+ *
+ * @param[in] in the sbuff to check.
+ * @param[in] p_rules to use terminals from.
+ * @return
+ * - true if substr is terminated correctly.
+ * - false if subst is not terminated correctly.
+ */
+static inline bool CC_HINT(always_inline) tmpl_substr_terminal_check(fr_sbuff_t *in,
+ fr_sbuff_parse_rules_t const *p_rules)
+{
+ fr_sbuff_marker_t m;
+ bool ret;
+
+ if (!fr_sbuff_extend(in)) return true; /* we're at the end of the string */
+ if (!p_rules || !p_rules->terminals) return false; /* more stuff to parse but don't have a terminal set */
+
+ fr_sbuff_marker(&m, in);
+ ret = fr_sbuff_is_terminal(in, p_rules->terminals);
+ fr_sbuff_set(in, &m);
+ fr_sbuff_marker_release(&m);
+ return ret;
+}
+/** Parse array subscript and in future other filters
+ *
+ * @param[out] err Parse error code.
+ * @param[in] ar to populate filter for.
+ * @param[in] name containing more attribute ref data.
+ * @return
+ * - 1 on success with filter parsed.
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int tmpl_attr_ref_parse_filter(attr_ref_error_t *err, tmpl_attr_t *ar, fr_sbuff_t *name)
+{
/*
- * Check to see if we expect a reference prefix
+ * Parse array subscript (and eventually complex filters)
*/
- switch (rules->prefix) {
- case TMPL_ATTR_REF_PREFIX_YES:
- if (*p != '&') {
- fr_strerror_printf("Invalid attribute reference, missing '&' prefix");
- if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
- return 0;
- }
- p++;
+ if (!fr_sbuff_next_if_char(name, '[')) return 0;
+
+ switch (*fr_sbuff_current(name)) {
+ case '#':
+ ar->num = NUM_COUNT;
+ fr_sbuff_next(name);
break;
- case TMPL_ATTR_REF_PREFIX_NO:
- if (*p == '&') {
- fr_strerror_printf("Attribute references used here must not have a '&' prefix");
- if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
- return 0;
- }
+ case '*':
+ ar->num = NUM_ALL;
+ fr_sbuff_next(name);
break;
- case TMPL_ATTR_REF_PREFIX_AUTO:
- if (*p == '&') p++;
+ case 'n':
+ ar->num = NUM_LAST;
+ fr_sbuff_next(name);
break;
- }
- MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, NULL, 0, T_BARE_WORD));
+ default:
+ {
+ fr_sbuff_parse_error_t sberr = FR_SBUFF_PARSE_OK;
+ fr_sbuff_t tmp = FR_SBUFF_NO_ADVANCE(name);
+
+ if (fr_sbuff_out(&sberr, &ar->num, &tmp) == 0) {
+ if (sberr == FR_SBUFF_PARSE_ERROR_NOT_FOUND) {
+ fr_strerror_printf("Array index is not an integer");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+ error:
+ return -1;
+ }
+
+ fr_strerror_printf("Invalid array index");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+ goto error;
+ }
+
+ if ((ar->num > 1000) || (ar->num < 0)) {
+ fr_strerror_printf("Invalid array index '%hi' (should be between 0-1000)", ar->num);
+ ar->num = 0;
+ if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+ goto error;
+ }
+ fr_sbuff_set(name, &tmp); /* Advance name _AFTER_ doing checks */
+ }
+ break;
+ }
/*
- * Search for a recognised list
- *
- * There may be multiple '.' separated
- * components, so don't error out if
- * the first one doesn't match a known list.
- *
- * The next check for a list qualifier
+ * Always advance here, so the error
+ * marker points to the bad char.
*/
- q = p;
- p += radius_request_name(&request_ref, p, rules->request_def);
+ if (!fr_sbuff_next_if_char(name, ']')) {
+ fr_strerror_printf("No closing ']' for array index");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
+ goto error;
+ }
+
+ return 1;
+}
+
+/** Parse an attribute reference, either an OID or attribute name
+ *
+ * This function calls itself recursively to process additional OID
+ * components once we've failed to resolve one component.
+ *
+ * @note Do not call directly.
+ *
+ * @param[in] ctx to allocate new attribute reference in.
+ * @param[out] err Parse error.
+ * @param[in,out] vpt to append this reference to.
+ * @param[in] name to parse.
+ * @param[in] rules see tmpl_attr_ref_afrom_attr_substr.
+ * @param[in] depth How deep we are. Used to check for maximum nesting level.
+ * @return
+ * - <0 on error.
+ * - 0 on success.
+ */
+static inline int tmpl_attr_ref_afrom_attr_unresolved_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+ tmpl_t *vpt,
+ fr_sbuff_t *name, tmpl_rules_t const *rules,
+ unsigned int depth)
+{
+ tmpl_attr_t *ar = NULL;
+ fr_dlist_head_t *list = &vpt->data.attribute.ar;
+ int ret;
+ char *unresolved;
+ size_t len;
- if (rules->disallow_qualifiers && (p != q)) {
- fr_strerror_printf("It is not permitted to specify a request list here.");
- goto invalid_list;
+ if (depth > FR_DICT_MAX_TLV_STACK) {
+ fr_strerror_printf("Attribute nesting too deep");
+ if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+ return -1;
+ }
+
+ /*
+ * Input too short
+ */
+ if (!fr_sbuff_extend(name)) {
+ fr_strerror_printf("Missing attribute reference");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+ return -1;
}
- tmpl_attr_set_request(vpt, request_ref);
- if (tmpl_request(vpt) == REQUEST_UNKNOWN) tmpl_attr_set_request(vpt, rules->request_def);
+ /*
+ * Mark the tmpl up as an unresolved attribute reference
+ * the attribute reference will be resolved later.
+ */
+ vpt->type = TMPL_TYPE_ATTR_UNRESOLVED;
+ MEM(ar = talloc(ctx, tmpl_attr_t));
/*
- * Finding a list qualifier is optional
+ * Copy out a string of allowed dictionary chars to form
+ * the unresolved attribute name.
*
- * Because the list name parser can
- * determine if something was a tag
- * or a list, we trust that when it
- * returns PAIR_LIST_UNKOWN that
- * the input string is invalid.
- */
- q = p;
- p += radius_list_name(&list, p, rules->list_def);
-
- if (rules->disallow_qualifiers && (p != q)) {
- fr_strerror_printf("It is not permitted to specify a pair list here.");
- invalid_list:
- if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
- slen = -(q - name);
+ * This will be resolved later (outside of this function).
+ */
+ len = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved,
+ name, FR_DICT_ATTR_MAX_NAME_LEN + 1,
+ fr_dict_attr_allowed_chars);
+ if (len == 0) {
+ fr_strerror_printf("Invalid attribute name");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+ error:
+ talloc_free(ar);
+ return -1;
+ }
+ if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
+ fr_strerror_printf("Attribute name is too long");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
goto error;
}
- if (list == PAIR_LIST_UNKNOWN) {
- fr_strerror_printf("Invalid list qualifier");
- if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
- slen = -(p - name);
- error:
- talloc_free(vpt);
- return slen;
- }
+ *ar = (tmpl_attr_t){
+ .ar_num = NUM_ANY,
+ .ar_type = TMPL_ATTR_TYPE_UNRESOLVED,
+ .ar_unresolved = unresolved
+ };
- tmpl_attr_set_list(vpt, list);
- tmpl_attr_set_leaf_tag(vpt, TAG_NONE);
- tmpl_attr_set_leaf_num(vpt, NUM_ANY);
+ if (tmpl_attr_ref_parse_filter(err, ar, name) < 0) goto error;
+
+ fr_dlist_insert_tail(list, ar);
/*
- * No more input after parsing the list ref, we're done.
+ * Once one OID component is created as unresolved all
+ * future OID components are also unresolved.
*/
- if (p == (name + name_len)) {
- vpt->type = TMPL_TYPE_LIST;
- goto finish;
+ if (fr_sbuff_next_if_char(name, '.')) {
+ ret = tmpl_attr_ref_afrom_attr_unresolved_substr(ctx, err, vpt, name, rules, depth + 1);
+ if (ret < 0) {
+ fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+ return -1;
+ }
}
- /*
- * This may be just a bare list, but it can still
- * have instance selectors and tag selectors.
- */
- switch (*p) {
- case '\0':
- vpt->type = TMPL_TYPE_LIST;
- goto finish;
+ return 0;
+}
- case '[':
- vpt->type = TMPL_TYPE_LIST;
- goto do_num;
+/** Parse an attribute reference, either an OID or attribute name
+ *
+ * @note Do not call directly.
+ *
+ * @param[in] ctx to allocate new attribute reference in.
+ * @param[out] err Parse error.
+ * @param[in,out] vpt to append this reference to.
+ * @param[in] parent Result of parsing the previous attribute reference.
+ * @param[in] name to parse.
+ * @param[in] rules which places constraints on attribute reference parsing.
+ * Rules interpreted by this function is:
+ * - allow_unknown - If false unknown OID components
+ * result in a parse error.
+ * - allow_unresolved - If false unknown attribute names
+ * result in a parse error.
+ * - disallow_internal - If an attribute resolves in the
+ * internal dictionary then that results in a parse
+ * error.
+ * - allow_foreign - If an attribute resolves in a dictionary
+ * that does not match the parent
+ * (exception being FR_TYPE_GROUP) then that results
+ * in a parse error.
+ * @param[in] depth How deep we are. Used to check for maximum nesting level.
+ * @return
+ * - <0 on error.
+ * - 0 on success.
+ */
+static inline int tmpl_attr_ref_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+ tmpl_t *vpt,
+ fr_dict_attr_t const *parent,
+ fr_sbuff_t *name, tmpl_rules_t const *rules, unsigned int depth)
+{
+ uint32_t oid = 0;
+ ssize_t slen;
+ fr_dlist_head_t *list = &vpt->data.attribute.ar;
+ tmpl_attr_t *ar = NULL;
+ fr_dict_attr_t const *da;
+ fr_sbuff_marker_t m_s;
- default:
- break;
+ fr_sbuff_marker(&m_s, name);
+
+ if (depth > FR_DICT_MAX_TLV_STACK) {
+ fr_strerror_printf("Attribute nesting too deep");
+ if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+ error:
+ fr_sbuff_marker_release(&m_s);
+ return -1;
}
/*
- * Hack to markup attributes as raw
+ * Input too short
*/
- if (strncmp(p, "raw.", 4) == 0) {
- is_raw = true;
- p += 4;
+ if (!fr_sbuff_extend(name)) {
+ fr_strerror_printf("Missing attribute reference");
+ if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
+ goto error;
}
/*
- * Look up by name, *including* any Attr-1.2.3.4 which was created when
- * parsing the configuration files.
+ * No parent means we need to go hunting through all the dctionaries
+ */
+ if (!parent) {
+ slen = fr_dict_attr_by_qualified_name_substr(NULL, &da,
+ rules->dict_def, name, !rules->disallow_internal);
+ /*
+ * Otherwise we're resolving in the context of the last component,
+ * or its reference in the case of group attributes.
*/
- slen = fr_dict_attr_by_qualified_name_substr(NULL, &da,
- rules->dict_def, &FR_SBUFF_IN(p, strlen(p)),
- !rules->disallow_internal);
- if (slen <= 0) {
- fr_dict_attr_t *unknown_da;
+ } else {
+ slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent, name, false);
+ }
- fr_strerror(); /* Clear out any existing errors */
+ /*
+ * The named component was a known attribute
+ * so record it as a normal attribute
+ * reference.
+ */
+ if (slen > 0) {
+ MEM(ar = talloc(ctx, tmpl_attr_t));
+ *ar = (tmpl_attr_t){
+ .ar_num = NUM_ANY,
+ .ar_type = TMPL_ATTR_TYPE_NORMAL,
+ .ar_da = da
+ };
+
+ goto check_attr;
+ }
- /*
- * At this point, the OID *must* be unknown, and
- * not previously used.
+ /*
+ * See if the ref begins with an unsigned integer
+ * if it does it's probably an OID component
+ *
+ * .<oid>
+ */
+ slen = fr_sbuff_out(NULL, &oid, name);
+ if (slen > 0) {
+ fr_dict_attr_t *da_unknown;
+
+ fr_strerror(); /* Clear out any existing errors */
+
+ /*
+ * Locating OID attributes is different than
+ * locating named attributes because we have
+ * significantly more numberspace overlap
+ * between the protocols.
*/
- slen = fr_dict_unknown_afrom_oid_substr(vpt, &unknown_da,
- fr_dict_root(rules->dict_def), p);
+ if (!parent && rules->dict_def) parent = fr_dict_root(rules->dict_def);
+ if (!parent && !rules->disallow_internal) parent = fr_dict_root(fr_dict_internal());
+ if (!parent) {
+ fr_strerror_printf("OID references must be qualified with a protocol when used here");
+ if (err) *err = ATTR_REF_ERROR_UNQUALIFIED_ATTRIBUTE_NOT_ALLOWED;
+ fr_sbuff_set(name, &m_s);
+ goto error;
+ }
/*
- * Attr-1.2.3.4 is OK.
+ * If it's numeric and not a known attribute
+ * then we create an unknown attribute with
+ * the specified attribute number.
*/
- if (slen > 0) {
+ da = fr_dict_attr_child_by_num(parent, oid);
+ if (da) {
+ /*
+ * The OID component was a known attribute
+ * so record it as a normal attribute
+ * reference.
+ */
+ MEM(ar = talloc(ctx, tmpl_attr_t));
+ *ar = (tmpl_attr_t){
+ .ar_num = NUM_ANY,
+ .ar_type = TMPL_ATTR_TYPE_NORMAL,
+ .ar_da = da,
+ };
vpt->data.attribute.was_oid = true;
- if (!is_raw) {
- da = fr_dict_attr_known(fr_dict_by_da(unknown_da), unknown_da);
- if (da) {
- talloc_free(unknown_da);
- tmpl_attr_set_leaf_da(vpt, da);
+ goto check_attr;
+ }
- p += slen;
+ if (!rules->allow_unknown) {
+ fr_strerror_printf("Unknown attributes not allowed here");
+ if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;
+ fr_sbuff_set(name, &m_s);
+ goto error;
+ }
- goto do_num;
- }
+ MEM(ar = talloc(ctx, tmpl_attr_t));
+
+ switch (parent->type) {
+ case FR_TYPE_VSA:
+ if (fr_dict_unknown_vendor_afrom_num(ar, &da_unknown, parent, oid) < 0) {
+ if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED; /* strerror set by dict function */
+ goto error;
}
+ break;
- if (!rules->allow_unknown) {
- fr_strerror_printf("Unknown attribute");
- if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED;
- slen = -(p - name);
+ default:
+ if (fr_dict_unknown_attr_afrom_num(ar, &da_unknown, parent, oid) < 0) {
+ if (err) *err = ATTR_REF_ERROR_UNKNOWN_ATTRIBUTE_NOT_ALLOWED; /* strerror set by dict function */
goto error;
}
+ break;
+ }
+ da_unknown->flags.internal = 1;
+
+ *ar = (tmpl_attr_t){
+ .ar_num = NUM_ANY,
+ .ar_type = TMPL_ATTR_TYPE_UNKNOWN,
+ .ar_unknown = da_unknown,
+ .ar_da = da_unknown,
+ };
+ da = da_unknown;
+ vpt->data.attribute.was_oid = true;
+ goto do_suffix;
+ }
- /*
- * Unknown attributes can be encoded, BUT
- * they must be of type "octets".
- */
- fr_assert(unknown_da->type == FR_TYPE_OCTETS);
- tmpl_attr_set_leaf_da(vpt, unknown_da);
- talloc_free(unknown_da);
+ /*
+ * Can't parse it as an attribute, might be a literal string
+ * let the caller decide.
+ *
+ * Don't alter the fr_strerror buffer, may contain useful
+ * errors from the dictionary code.
+ */
+ if (!rules->allow_unresolved) {
+ fr_strerror_printf_push("Unparsed attributes not allowed here");
+ if (err) *err = ATTR_REF_ERROR_UNRESOLVED_ATTRIBUTE_NOT_ALLOWED;
+ fr_sbuff_set(name, &m_s);
+ goto error;
+ }
- p += slen;
+ fr_sbuff_marker_release(&m_s);
- goto do_num; /* unknown attributes can't have tags */
- }
+ /*
+ * Once we hit one unresolved attribute we have to treat
+ * the rest of the components are unresolved as well.
+ */
+ return tmpl_attr_ref_afrom_attr_unresolved_substr(ctx, err, vpt, name, rules, depth);
+
+check_attr:
+ /*
+ * Attribute location (dictionary) checks
+ */
+ if (!rules->allow_foreign || rules->disallow_internal) {
+ fr_dict_t const *found_in = fr_dict_by_da(da);
/*
- * Can't parse it as an attribute, might be a literal string
- * let the caller decide.
- *
- * Don't alter the fr_strerror buffer, should contain the parse
- * error from fr_dict_unknown_from_suboid.
+ * Parent is the dict root if this is the first ref in the
+ * chain.
*/
- if (!rules->allow_unresolved) {
- fr_strerror_printf_push("Undefined attributes not allowed here");
- if (err) *err = ATTR_REF_ERROR_UNDEFINED_ATTRIBUTE_NOT_ALLOWED;
- slen = -(p - name);
- goto error;
- }
+ if (!parent) parent = fr_dict_root(rules->dict_def);
/*
- * Copy the name to a field for later resolution
+ * Even if allow_foreign is false, if disallow_internal is not
+ * true, we still allow the resolution.
*/
- vpt->type = TMPL_TYPE_ATTR_UNRESOLVED;
- for (q = p; (q < (name + name_len)) && ((*q == '.') || fr_dict_attr_allowed_chars[(uint8_t) *q]); q++);
- if (q == p) {
- fr_strerror_printf("Invalid attribute name");
- if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
- slen = -(p - name);
+ if (rules->disallow_internal && (found_in == fr_dict_internal())) {
+ fr_strerror_printf("Internal attributes not allowed here");
+ if (err) *err = ATTR_REF_ERROR_INTERNAL_ATTRIBUTE_NOT_ALLOWED;
+ fr_sbuff_set(name, &m_s);
goto error;
}
-
- if ((q - p) > FR_DICT_ATTR_MAX_NAME_LEN) {
- fr_strerror_printf("Attribute name is too long");
- if (err) *err = ATTR_REF_ERROR_INVALID_ATTRIBUTE_NAME;
- slen = -(p - name);
+ /*
+ * Check that the attribute we resolved was from an allowed
+ * dictionary.
+ *
+ * We already checked if internal attributes were disallowed
+ * above, so we skip this check if the attribute is internal.
+ *
+ * The reason this checks works with foreign attributes is
+ * because when an attr ref resolves to a group parent is not
+ * set to that attribute, but the foreign dictionary attribute
+ * that it references.
+ *
+ * My-Dhcp-In-RADIUS-Attribute.My-DHCP-Attribute
+ * | ||_ DHCP attribute
+ * | |_ Lookup inside linking attribute triggers dictionary change
+ * |_ RADIUS attribute
+ */
+ if (found_in != fr_dict_internal() &&
+ !rules->allow_foreign && (found_in != fr_dict_by_da(parent))) {
+ fr_strerror_printf("Only attributes from the %s protocol are allowed here",
+ fr_dict_root(rules->dict_def)->name);
+ if (err) *err = ATTR_REF_ERROR_FOREIGN_ATTRIBUTES_NOT_ALLOWED;
+ fr_sbuff_set(name, &m_s);
goto error;
}
- tmpl_attr_set_unresolved(vpt, p, q - p);
- p = q;
-
- goto do_num;
- } else {
- tmpl_attr_set_leaf_da(vpt, da);
}
+do_suffix:
/*
- * Attribute location checks
+ * Parse the attribute reference filter
*/
- {
- fr_dict_t const *found_in = fr_dict_by_da(tmpl_da(vpt));
-
+ switch (tmpl_attr_ref_parse_filter(err, ar, name)) {
+ case 0: /* No filter */
/*
- * Even if allow_foreign is false, if disallow_internal is not
- * true, we still allow foreign
+ * Omit nesting types where the relationship is already
+ * described by the dictionaries and there's no filter.
+ *
+ * These attribute references would just use additional
+ * memory for no real purpose.
+ *
+ * Because we pre-allocate an attribute reference in
+ * each tmpl talloc pool, unless the attribute
+ * reference list contains a group, there's no performance
+ * penalty in repeatedly allocating and freeing this ar.
*/
- if (found_in == fr_dict_internal()) {
- if (rules->disallow_internal) {
- fr_strerror_printf("Internal attributes not allowed here");
- if (err) *err = ATTR_REF_ERROR_INTERNAL_ATTRIBUTE_NOT_ALLOWED;
- slen = -(p - name);
- goto error;
- }
+ switch (da->type) {
+ case FR_TYPE_STRUCT:
+ case FR_TYPE_TLV:
+ case FR_TYPE_VENDOR:
+ case FR_TYPE_VSA:
+ TALLOC_FREE(ar);
+ break;
+
/*
- * Check that the attribute we resolved was from an allowed dictionary
+ * Groups are fine, as are leaf values.
*/
+ case FR_TYPE_GROUP:
+ case FR_TYPE_VALUE:
+ break;
+
+ default:
+ goto error;
}
-#if 0
- else if ((rules->dict_def && (found_in != rules->dict_def))) {
+ FALL_THROUGH;
+
+ case 1: /* Found a filter */
+ fr_dlist_insert_tail(list, ar);
+ break;
+
+ default: /* Parse error */
+ goto error;
+ }
+
+ /*
+ * At the end of the attribute reference. If there's a
+ * trailing '.' then there's another attribute reference
+ * we need to parse, otherwise we're done.
+ */
+ if (fr_sbuff_next_if_char(name, '.')) {
+ switch (da->type) {
/*
- * @fixme - We can't enforce this until we support nested attributes
- * where the change of attribute context gives us a new dictionary.
- *
- * i.e.
- *
- * My-Dhcp-In-RADIUS-Attribute.My-DHCP-Attribute
- * | ||_ DHCP attribute
- * | |_ Lookup inside linking attribute triggers dictionary change
- * |_ RADIUS attribute
+ * If this is a group then the parent is the
+ * group ref.
*/
+ case FR_TYPE_GROUP:
+ parent = da->ref;
+ break;
- if (!rules->allow_foreign) {
- fr_strerror_printf("Only attributes from the %s protocol are allowed here",
- fr_dict_root(rules->dict_def)->name);
- if (err) *err = ATTR_REF_ERROR_FOREIGN_ATTRIBUTES_NOT_ALLOWED;
- slen = -(p - name);
- goto error;
- }
+ case FR_TYPE_STRUCT:
+ case FR_TYPE_TLV:
+ case FR_TYPE_VENDOR:
+ case FR_TYPE_VSA:
+ parent = da;
+ break;
+
+ default:
+ fr_strerror_printf("Parent type of nested attribute must be of "
+ "\"struct\", \"tlv\", \"vendor\", \"vsa\" or \"group\", got \"%s\"",
+ fr_table_str_by_value(fr_value_box_type_table,
+ da->type, "<INVALID>"));
+ fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+ fr_sbuff_set(name, &m_s);
+ goto error;
+ }
+
+ if (tmpl_attr_ref_afrom_attr_substr(ctx, err, vpt, parent, name, rules, depth + 1) < 0) {
+ fr_dlist_talloc_free_tail(list); /* Remove and free ar */
+ goto error;
}
-#endif
}
+ fr_sbuff_marker_release(&m_s);
+
+ return 0;
+}
+
+static inline int tmpl_request_ref_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+ tmpl_t *vpt,
+ fr_sbuff_t *name,
+ fr_sbuff_parse_rules_t const *p_rules,
+ tmpl_rules_t const *ar_rules,
+ unsigned int depth)
+{
+ request_ref_t ref;
+ size_t ref_len;
+ tmpl_request_t *rr;
+ fr_dlist_head_t *list = &vpt->data.attribute.rr;
+ fr_sbuff_marker_t s_m;
+
+ fr_sbuff_marker(&s_m, name);
+ fr_sbuff_out_by_longest_prefix(&ref_len, &ref, request_ref_table, name, ar_rules->request_def);
+
/*
- * Parsing was successful, so advance the pointer
+ * No match
*/
- p += slen;
+ if (ref_len == 0) {
+ /*
+ * If depth == 0, then just use
+ * the default request reference.
+ */
+ default_ref:
+ if (depth == 0) {
+ MEM(rr = talloc(ctx, tmpl_request_t));
+ *rr = (tmpl_request_t){
+ .request = ref
+ };
+ fr_dlist_insert_tail(list, rr);
+ }
+
+ return 0;
+ }
/*
- * If it's an attribute, look for a tag.
- *
- * Note that we check for tags even if the attribute
- * isn't tagged. This lets us print more useful error
- * messages.
+ * We don't want to misidentify the list
+ * as being part of an attribute.
*/
- if (*p == ':') {
- char *end;
+ if (!fr_sbuff_is_char(name, '.') &&
+ ((!p_rules || !p_rules->terminals) ||
+ !tmpl_substr_terminal_check(name, p_rules))) {
+ fr_sbuff_set(name, &s_m);
+ goto default_ref;
+ }
- if (!tmpl_da(vpt)->flags.has_tag) { /* Lists don't have a da */
- fr_strerror_printf("Attribute '%s' cannot have a tag", tmpl_da(vpt)->name);
- if (err) *err = ATTR_REF_ERROR_TAGGED_ATTRIBUTE_NOT_ALLOWED;
- slen = -(p - name);
- goto error;
- }
+ fr_sbuff_marker_release(&s_m);
- /*
- * Allow '*' as an explicit wildcard.
- */
- if (p[1] == '*') {
- tmpl_attr_set_leaf_tag(vpt, TAG_ANY);
- p += 2;
+ /*
+ * Nesting level too deep
+ */
+ if (depth > TMPL_MAX_REQUEST_REF_NESTING) {
+ fr_strerror_printf("Request ref nesting too deep");
+ if (err) *err = ATTR_REF_ERROR_NESTING_TOO_DEEP;
+ return -1;
+ }
- } else {
- num = strtol(p + 1, &end, 10);
- if (!TAG_VALID_ZERO(num)) {
- fr_strerror_printf("Invalid tag value '%li' (should be between 0-31)", num);
- if (err) *err = ATTR_REF_ERROR_INVALID_TAG;
- slen = -((p + 1) - name);
- goto error;
- }
+ if (ar_rules->disallow_qualifiers) {
+ fr_strerror_printf("It is not permitted to specify a request reference here");
+ if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
+ return -1;
+ }
- tmpl_attr_set_leaf_tag(vpt, num);
- p = end;
- }
+ /*
+ * Add a new entry to the dlist
+ */
+ MEM(rr = talloc(ctx, tmpl_request_t));
+ *rr = (tmpl_request_t){
+ .request = ref
+ };
+ fr_dlist_insert_tail(list, rr);
/*
- * The attribute is tagged, but the admin didn't
- * specify one. This means it's likely a
- * "search" thingy.. i.e. "find me ANY attribute,
- * no matter what the tag".
+ * Advance past the separator (if there is one)
*/
- } else if (tmpl_da(vpt)->flags.has_tag) {
- tmpl_attr_set_leaf_tag(vpt, TAG_ANY);
+ if (fr_sbuff_next_if_char(name, '.')) {
+ if (tmpl_request_ref_afrom_attr_substr(ctx, err, vpt, name, p_rules, ar_rules, depth + 1) < 0) {
+ fr_dlist_talloc_free_tail(list); /* Remove and free rr */
+ return -1;
+ }
}
-do_num:
- if (*p == '\0') goto finish;
+ return 0;
+}
- if (*p == '[') {
- p++;
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
+ *
+ * @param[in,out] ctx to allocate #tmpl_t in.
+ * @param[out] err May be NULL. Provides the exact error that the parser hit
+ * when processing the attribute ref.
+ * @param[out] out Where to write pointer to new #tmpl_t.
+ * @param[in] name of attribute including #request_ref_t and #pair_list_t qualifiers.
+ * If only #request_ref_t #pair_list_t qualifiers are found,
+ * a #TMPL_TYPE_LIST #tmpl_t will be produced.
+ * @param[in] p_rules Formatting rules used to check for trailing garbage.
+ * @param[in] ar_rules Rules which control parsing:
+ * - dict_def The default dictionary to use if attributes
+ * are unqualified.
+ * - request_def The default #REQUEST to set if no
+ * #request_ref_t qualifiers are found in name.
+ * - list_def The default list to set if no #pair_list_t
+ * qualifiers are found in the name.
+ * - allow_unknown If true attributes in the format accepted by
+ * #fr_dict_unknown_afrom_oid_substr will be allowed,
+ * even if they're not in the main dictionaries.
+ * If an unknown attribute is found a #TMPL_TYPE_ATTR
+ * #tmpl_t will be produced.
+ * If #tmpl_afrom_attr_substr is being called on
+ * startup, the #tmpl_t may be passed to
+ * #tmpl_attr_unknown_add to
+ * add the unknown attribute to the main dictionary.
+ * If the unknown attribute is not added to
+ * the main dictionary the #tmpl_t cannot be used
+ * to search for a #VALUE_PAIR in a #REQUEST.
+ * - allow_unresolved If true, we don't generate a parse error on
+ * unknown attributes. If an unknown attribute is
+ * found a #TMPL_TYPE_ATTR_UNRESOLVED
+ * #tmpl_t will be produced.
+ * - allow_foreign If true, allow attribute names to be qualified
+ * with a protocol outside of the passed dict_def.
+ * - disallow_internal If true, don't allow fallback to internal
+ * attributes.
+ *
+ * @see REMARKER to produce pretty error markers from the return value.
+ *
+ * @return
+ * - <= 0 on error (offset as negative integer)
+ * - > 0 on success (number of bytes parsed).
+ */
+ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, attr_ref_error_t *err,
+ tmpl_t **out, fr_sbuff_t *name,
+ fr_sbuff_parse_rules_t const *p_rules,
+ tmpl_rules_t const *ar_rules)
+{
+ int ret;
+ size_t list_len;
+ tmpl_t *vpt;
+ fr_sbuff_t our_name = FR_SBUFF_NO_ADVANCE(name); /* Take a local copy in case we need to back track */
+ bool ref_prefix = false;
+ bool is_raw = false;
- switch (*p) {
- case '#':
- tmpl_attr_set_leaf_num(vpt, NUM_COUNT);
- p++;
- break;
+ if (!ar_rules) ar_rules = &default_attr_ref_rules; /* Use the defaults */
- case '*':
- tmpl_attr_set_leaf_num(vpt, NUM_ALL);
- p++;
- break;
+ if (err) *err = ATTR_REF_ERROR_NONE;
- case 'n':
- tmpl_attr_set_leaf_num(vpt, NUM_LAST);
- p++;
- break;
+ if (!fr_sbuff_extend(&our_name)) {
+ fr_strerror_printf("Empty attribute reference");
+ if (err) *err = ATTR_REF_ERROR_EMPTY;
+ FR_SBUFF_ERROR_RETURN(&our_name);
+ }
- default:
- {
- char *end;
+ /*
+ * Check to see if we expect a reference prefix
+ */
+ switch (ar_rules->prefix) {
+ case TMPL_ATTR_REF_PREFIX_YES:
+ if (!fr_sbuff_next_if_char(&our_name, '&')) {
+ fr_strerror_printf("Invalid attribute reference, missing '&' prefix");
+ if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
+ FR_SBUFF_ERROR_RETURN(&our_name);
+ }
- num = strtol(p, &end, 10);
- if (p == end) {
- fr_strerror_printf("Array index is not an integer");
- if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
- slen = -(p - name);
- goto error;
- }
+ break;
- if ((num > 1000) || (num < 0)) {
- fr_strerror_printf("Invalid array reference '%li' (should be between 0-1000)", num);
- if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
- slen = -(p - name);
- goto error;
- }
- tmpl_attr_set_leaf_num(vpt, num);
- p = end;
+ case TMPL_ATTR_REF_PREFIX_NO:
+ if (fr_sbuff_is_char(&our_name, '&')) {
+ fr_strerror_printf("Attribute references used here must not have a '&' prefix");
+ if (err) *err = ATTR_REF_ERROR_BAD_PREFIX;
+ FR_SBUFF_ERROR_RETURN(&our_name);
}
+ break;
+
+ case TMPL_ATTR_REF_PREFIX_AUTO:
+ fr_sbuff_next_if_char(&our_name, '&');
+ break;
+ }
+
+ MEM(vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, T_BARE_WORD, NULL, 0));
+ vpt->data.attribute.ref_prefix = ref_prefix;
+
+ /*
+ * For backwards compatibility strip off "Attr-"
+ */
+ (void)fr_sbuff_adv_past_strcase_literal(&our_name, "Attr-");
+
+ /*
+ * The "raw." prefix marks up the leaf attribute
+ * as unknown if it wasn't already which allows
+ * users to stick whatever they want in there as
+ * a value.
+ */
+ if (fr_sbuff_adv_past_strcase_literal(&our_name, "raw.")) is_raw = true;
+
+ /*
+ * Parse one or more request references
+ */
+ ret = tmpl_request_ref_afrom_attr_substr(vpt, err, vpt, &our_name, p_rules, ar_rules, 0);
+ if (ret < 0) {
+ error:
+ talloc_free(vpt);
+ FR_SBUFF_ERROR_RETURN(&our_name);
+ }
+
+ /*
+ * Parse the list reference
+ *
+ * This code should be removed when lists
+ * are integrated into attribute references.
+ */
+ fr_sbuff_out_by_longest_prefix(&list_len, &vpt->data.attribute.list, pair_list_table,
+ &our_name, ar_rules->list_def);
+
+ if (ar_rules->disallow_qualifiers && (list_len > 0)) {
+ fr_strerror_printf("It is not permitted to specify a pair list here");
+ if (err) *err = ATTR_REF_ERROR_INVALID_LIST_QUALIFIER;
+ talloc_free(vpt);
+ FR_SBUFF_ERROR_RETURN(&our_name);
+ }
+
+ /*
+ * Parse the attribute reference
+ *
+ * This will either be after:
+ * - A zero length list, i.e. just after the prefix '&', in which case we require an attribue
+ * - A ':' or '.' and then an allowed char, so we're sure it's not just a bare list ref.
+ */
+ if ((list_len == 0) ||
+ (((fr_sbuff_next_if_char(&our_name, ':') && (vpt->data.attribute.old_list_sep = true)) ||
+ fr_sbuff_next_if_char(&our_name, '.')) && fr_sbuff_is_in_charset(&our_name, fr_dict_attr_allowed_chars))) {
+ ret = tmpl_attr_ref_afrom_attr_substr(vpt, err,
+ vpt, NULL, &our_name, ar_rules, 0);
+ if (ret < 0) goto error;
+
+ /*
+ * Check to see if the user wants the leaf
+ * attribute to be raw.
+ *
+ * We can only do the conversion now _if_
+ * the complete hierarchy has been resolved
+ * otherwise we'll need to do the conversion
+ * later.
+ */
+ if (tmpl_is_attr(vpt) && is_raw) tmpl_attr_to_raw(vpt);
+ }
+
+ /*
+ * If there's no attribute references
+ * treat this as a list reference.
+ *
+ * Eventually we'll remove TMPL_TYPE_LIST
+ */
+ if (fr_dlist_num_elements(&vpt->data.attribute.ar) == 0) {
+ tmpl_attr_t *ar;
+
+ MEM(ar = talloc_zero(vpt, tmpl_attr_t));
+ switch (tmpl_attr_ref_parse_filter(err, ar, &our_name)) {
+ case 0: /* No filter */
+ talloc_free(ar);
break;
- }
- if (*p != ']') {
- fr_strerror_printf("No closing ']' for array index");
- if (err) *err = ATTR_REF_ERROR_INVALID_ARRAY_INDEX;
- slen = -(p - name);
+ case 1: /* Found a filter */
+ fr_dlist_insert_tail(&vpt->data.attribute.ar, ar);
+ break;
+
+ default: /* Parse error */
goto error;
}
- p++;
+
+ vpt->type = TMPL_TYPE_LIST;
}
-finish:
- vpt->name = talloc_strndup(vpt, name, p - name);
- vpt->len = p - name;
- vpt->quote = T_BARE_WORD;
+ tmpl_set_name(vpt, T_BARE_WORD, fr_sbuff_start(&our_name), fr_sbuff_used(&our_name));
+ vpt->rules = *ar_rules; /* Record the rules */
+
+ if (!tmpl_substr_terminal_check(&our_name, p_rules)) {
+ fr_strerror_printf("Unexpected text after attribute reference");
+ if (err) *err = ATTR_REF_ERROR_MISSING_TERMINATOR;
+ talloc_free(vpt);
+ *out = NULL;
+ return -fr_sbuff_used(&our_name);
+ }
TMPL_VERIFY(vpt); /* Because we want to ensure we produced something sane */
*out = vpt;
-
- return vpt->len;
+ return fr_sbuff_set(name, &our_name);
}
/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #tmpl_t
{
ssize_t slen, name_len;
- if (!rules) rules = &default_rules; /* Use the defaults */
+ if (!rules) rules = &default_attr_ref_rules; /* Use the defaults */
name_len = strlen(name);
- slen = tmpl_afrom_attr_substr(ctx, err, out, name, name_len, rules);
+ slen = tmpl_afrom_attr_substr(ctx, err, out, &FR_SBUFF_IN(name, name_len), NULL, rules);
if (slen <= 0) return slen;
if (!fr_cond_assert(*out)) return -1;
if (slen != name_len) {
- /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
- fr_strerror_printf("Unexpected text after %s", fr_table_str_by_value(tmpl_type_table, (*out)->type, "<INVALID>"));
+ /* This looks wrong, but it produces meaningful errors for unknown attrs */
+ fr_strerror_printf("Unexpected text after %s",
+ fr_table_str_by_value(tmpl_type_table, (*out)->type, "<INVALID>"));
return -slen;
}
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
*
* @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).
*
* @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.
* @{
*/
{
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:
}
break;
+ case TMPL_TYPE_ATTR: /* FIXME - We should check cast compatibility with resolved attrs */
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ vpt->cast = type;
+ break;
+
default:
fr_assert(0);
}
+ /*
+ * Fixup quoting
+ */
+ switch (type) {
+ case FR_TYPE_STRING:
+ switch (vpt->quote) {
+ case T_SINGLE_QUOTED_STRING:
+ case T_DOUBLE_QUOTED_STRING:
+ break;
+
+ default:
+ vpt->quote = T_SINGLE_QUOTED_STRING;
+ break;
+ }
+ break;
+
+ default:
+ vpt->quote = T_BARE_WORD;
+ break;
+ }
+
TMPL_VERIFY(vpt);
return 0;
}
-/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+/** Resolve an unresolved attribute
*
- * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
- * #fr_dict_attr_t inserted into the dictionary.
+ * Multi-pass parsing fixups for attribute references.
+ */
+static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt)
+{
+ tmpl_attr_t *ar = NULL;
+ fr_dict_attr_t const *parent = NULL;
+ fr_dict_attr_t const *da;
+
+ fr_assert(tmpl_is_attr_unresolved(vpt));
+
+ /*
+ * First ref is special as it can resolve in the
+ * internal dictionary or the protocol specific
+ * dictionary.
+ */
+ ar = fr_dlist_head(&vpt->data.attribute.ar);
+ if (fr_dict_attr_by_qualified_name(&da, vpt->rules.dict_def,
+ ar->ar_unresolved, true) != FR_DICT_ATTR_OK) {
+ parent = fr_dict_root(vpt->rules.dict_def);
+ goto unknown;
+ } else {
+ ar->da = da;
+ ar->type = TMPL_ATTR_TYPE_NORMAL;
+ parent = ar->da;
+ }
+
+ /*
+ * Loop, resolving each unresolved attribute in turn
+ */
+ while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+ ssize_t slen;
+ bool is_raw;
+
+ if ((ar->type == TMPL_ATTR_TYPE_NORMAL) || (ar->type == TMPL_ATTR_TYPE_UNKNOWN)) break;
+
+ is_raw = ar->ar_unresolved_raw;
+
+ slen = fr_dict_attr_child_by_name_substr(NULL, &da, parent,
+ &FR_SBUFF_IN(ar->ar_unresolved, strlen(ar->ar_unresolved)),
+ false);
+ if (slen <= 0) {
+ fr_dict_attr_t *unknown_da;
+
+ unknown:
+ /*
+ * Can't find it under its regular name. Try an unknown attribute.
+ */
+ slen = fr_dict_unknown_afrom_oid_str(vpt, &unknown_da, parent, ar->ar_unresolved);
+ if ((slen <= 0) || (ar->ar_unresolved[slen] != '\0')) {
+ fr_strerror_printf_push("Failed resolving unresolved attribute");
+ return -1;
+ }
+
+ ar->type = TMPL_ATTR_TYPE_UNKNOWN;
+ ar->da = ar->unknown.da = unknown_da;
+ parent = ar->da;
+ continue;
+ }
+
+ /*
+ * Known attribute, just rewrite.
+ */
+ ar->da = da;
+ parent = ar->da;
+
+ /*
+ * If the user wanted the leaf
+ * to be raw, and it's not, correct
+ * that now.
+ */
+ if (is_raw) tmpl_attr_ref_to_raw(vpt, ar);
+ }
+
+ vpt->type ^= TMPL_FLAG_UNRESOLVED;
+
+ TMPL_VERIFY(vpt);
+
+ return 0;
+}
+
+/** Resolve an unresolved xlat, i.e. one containing unresolved attribute references or xlat functions
+ *
+ * Multi-pass parsing fixups for attribute references.
+ *
+ * Works for base types:
+ * - TMPL_TYPE_XLAT
+ * - TMPL_TYPE_EXEC
+ * - TMPL_TYPE_REGEX_XLAT
+ *
+ * @param[in] vpt Containing the xlat expansion to resolve.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static inline CC_HINT(always_inline) int tmpl_xlat_resolve(tmpl_t *vpt)
+{
+ if (xlat_resolve(&vpt->data.xlat.ex, &vpt->data.xlat.flags, false) < 0) return -1;
+
+ vpt->type ^= TMPL_FLAG_UNRESOLVED;
+
+ return 0;
+}
+
+/** Attempt to resolve functions and attributes in xlats and attribute references
+ *
+ * @param[in,out] vpt to resolve. Should be of type TMPL_TYPE_XLAT_UNRESOLVED
+ * or TMPL_TYPE_ATTR_UNRESOLVED. All other types will be
+ * noops.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int tmpl_resolve(tmpl_t *vpt)
+{
+ int ret = 0;
+
+ if (!tmpl_needs_resolving(vpt)) return 0; /* Nothing to do */
+
+ /*
+ * The xlat component of the #tmpl_t needs resolving.
+ */
+ if (tmpl_contains_xlat(vpt)) {
+ ret = tmpl_xlat_resolve(vpt);
+ /*
+ * The attribute reference needs resolving.
+ */
+ } else if (tmpl_contains_attr(vpt)) {
+ ret = tmpl_attr_resolve(vpt);
+
+ /*
+ * Convert unresolved tmpls into literal string values.
+ */
+ } else if (tmpl_is_unresolved(vpt)) {
+ char *unescaped = vpt->data.unescaped; /* Copy the pointer before zeroing the union */
+
+ fr_value_box_init_null(&vpt->data.literal);
+ fr_value_box_bstrdup_buffer_shallow(NULL, &vpt->data.literal, NULL, unescaped, false);
+ vpt->type = TMPL_TYPE_DATA;
+ }
+
+ TMPL_VERIFY(vpt);
+
+ return ret;
+}
+
+/** Reset the tmpl, leaving only the name in place
+ *
+ * After calling this function, the tmpl type will revert to TMPL_TYPE_UNRESOLVED
+ * and only the name and quoting will be preserved.
+ *
+ * @param[in] vpt to reset.
+ */
+void tmpl_unresolve(tmpl_t *vpt)
+{
+ tmpl_t tmp = {
+ .type = TMPL_TYPE_UNRESOLVED,
+ .name = vpt->name,
+ .len = vpt->len,
+ .quote = vpt->quote
+ };
+
+ switch (vpt->type) {
+ case TMPL_TYPE_UNINITIALISED:
+ case TMPL_TYPE_MAX:
+ fr_assert(0);
+ break;
+
+ case TMPL_TYPE_NULL:
+ case TMPL_TYPE_UNRESOLVED:
+ case TMPL_TYPE_REGEX_UNCOMPILED:
+ break;
+
+ case TMPL_TYPE_DATA:
+ fr_value_box_clear(&vpt->data.literal);
+ break;
+
+ /*
+ * These types contain dynamically allocated
+ * attribute and request references.
+ */
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_ATTR:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ fr_dlist_talloc_free(&vpt->data.attribute.ar);
+ fr_dlist_talloc_free(&vpt->data.attribute.rr);
+ break;
+
+ /*
+ * These all store an xlat expansion
+ */
+ case TMPL_TYPE_EXEC:
+ case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_REGEX_XLAT:
+ case TMPL_TYPE_EXEC_UNRESOLVED:
+ case TMPL_TYPE_XLAT_UNRESOLVED:
+ case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
+ xlat_exp_free(&vpt->data.xlat.ex);
+ break;
+
+ case TMPL_TYPE_REGEX:
+ talloc_free(vpt->data.reg.ex);
+ break;
+
+ }
+
+ memcpy(vpt, &tmp, sizeof(*vpt));
+
+ TMPL_VERIFY(vpt);
+}
+
+/** Convert an attribute reference to an xlat expansion
+ *
+ * This is where a user attempts to use an attribute reference which is actually
+ * a virtual attribute.
+ *
+ * @param[in] ctx to convert new tmpl in.
+ * @param[in,out] vpt_p pointer to #tmpl_t of TMPL_TYPE_ATTR | TMPL_TYPE_ATTR_UNPARSED.
+ */
+int tmpl_attr_to_xlat(TALLOC_CTX *ctx, tmpl_t **vpt_p)
+{
+
+ tmpl_t *vpt;
+ tmpl_t *attr = *vpt_p;
+
+ /*
+ * First alloc a new tmpl to hold the xlat expansion
+ */
+ vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, attr->quote, attr->name, attr->len);
+
+ /*
+ * ...then wrap the old tmpl_t in an xlat expansion
+ * doing conversion to a virtual attribute if necessary.
+ */
+ if (xlat_from_tmpl_attr(vpt, &vpt->data.xlat.ex, &vpt->data.xlat.flags, vpt_p) < 0) {
+ talloc_free(vpt);
+ return -1;
+ }
+
+ *vpt_p = vpt;
+
+ return 0;
+}
+
+static void tmpl_attr_ref_to_raw(tmpl_t *vpt, tmpl_attr_t *ref)
+{
+ if (!ref) return;
+
+ switch (ref->type) {
+ case TMPL_ATTR_TYPE_NORMAL:
+ {
+ char buffer[256] = "raw.";
+ char *p = buffer + strlen(buffer);
+ char *end = buffer + sizeof(buffer);
+ size_t len;
+ fr_dict_attr_t *da;
+
+ len = fr_dict_print_attr_oid(NULL, p, end - p, NULL, ref->da);
+ p += len;
+
+ ref->da = ref->ar_unknown = da = fr_dict_unknown_acopy(vpt, ref->da);
+ ref->ar_unknown->type = FR_TYPE_OCTETS;
+ ref->ar_unknown->flags.is_raw = 1;
+ ref->ar_unknown->flags.is_unknown = 1;
+
+ talloc_const_free(da->name);
+ MEM(da->name = talloc_bstrndup(da, buffer, p - buffer));
+
+ ref->type = TMPL_ATTR_TYPE_UNKNOWN;
+ }
+ break;
+
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ ref->ar_unknown->type = FR_TYPE_OCTETS;
+ ref->ar_unknown->flags.is_raw = 1;
+ break;
+
+ case TMPL_ATTR_TYPE_UNRESOLVED:
+ ref->ar_unresolved_raw = true;
+ break;
+ }
+
+ TMPL_ATTR_VERIFY(vpt);
+}
+
+/** Covert the leaf attribute of a tmpl to a unknown/raw type
+ *
+ */
+void tmpl_attr_to_raw(tmpl_t *vpt)
+{
+ tmpl_attr_ref_to_raw(vpt, fr_dlist_tail(&vpt->data.attribute.ar));
+}
+
+/** Convert an abstract da into a concrete one
+ *
+ * Usually used to fixup combo ip addresses
+ */
+int tmpl_attr_abstract_to_concrete(tmpl_t *vpt, fr_type_t type)
+{
+ fr_dict_attr_t const *abstract;
+ fr_dict_attr_t const *concrete;
+ tmpl_attr_t *ref;
+
+ tmpl_assert_type(tmpl_is_attr(vpt));
+
+ abstract = tmpl_da(vpt);
+ if (abstract->type != FR_TYPE_COMBO_IP_ADDR) {
+ fr_strerror_printf("Abstract attribute \"%s\" is of incorrect type '%s'", abstract->name,
+ fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"));
+ return -1;
+ }
+
+ concrete = fr_dict_attr_by_type(abstract, type);
+ if (!concrete) {
+ fr_strerror_printf("Can't convert abstract type '%s' to concrete type '%s'",
+ fr_table_str_by_value(fr_value_box_type_table, abstract->type, "<INVALID>"),
+ fr_table_str_by_value(fr_value_box_type_table, type, "<INVALID>"));
+ return -1;
+ }
+
+ ref = fr_dlist_tail(&vpt->data.attribute.ar);
+ ref->da = concrete;
+
+ TMPL_ATTR_VERIFY(vpt);
+
+ return 0;
+}
+
+/** Add an unknown #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+ *
+ * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
+ * #fr_dict_attr_t inserted into the dictionary.
+ * @return
+ * - 1 noop (did nothing) - Not possible to convert tmpl.
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int tmpl_attr_unknown_add(tmpl_t *vpt)
+{
+ fr_dict_attr_t const *da;
+
+ if (!vpt) return 1;
+
+ tmpl_assert_type(tmpl_is_attr(vpt));
+
+ TMPL_VERIFY(vpt);
+
+ if (!tmpl_da(vpt)->flags.is_unknown) return 1;
+
+ da = tmpl_da(vpt);
+ da = fr_dict_unknown_add(fr_dict_unconst(fr_dict_by_da(da)), da);
+ if (!da) return -1;
+ tmpl_attr_set_leaf_da(vpt, da);
+
+ return 0;
+}
+
+/** Add an unresolved #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+ *
+ * @note fr_dict_attr_add will not return an error if the attribute already exists
+ * meaning that multiple #tmpl_t specifying the same attribute can be
+ * passed to this function to be fixed up, so long as the type and flags
+ * are identical.
+ *
+ * @param[in] dict_def Default dictionary to use if none is
+ * specified by the tmpl_attr_unresolved.
+ * @param[in] vpt specifying unresolved attribute to add.
+ * ``tmpl_da`` pointer will be updated to
+ * point to the #fr_dict_attr_t inserted
+ * into the dictionary. Lists and requests
+ * will be preserved.
+ * @param[in] type to define unresolved attribute as.
+ * @param[in] flags to define unresolved attribute with.
+ * @return
+ * - 1 noop (did nothing) - Not possible to convert tmpl.
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int tmpl_attr_unresolved_add(fr_dict_t *dict_def, tmpl_t *vpt,
+ fr_type_t type, fr_dict_attr_flags_t const *flags)
+{
+ fr_dict_attr_t const *da;
+
+ if (!vpt) return -1;
+
+ TMPL_VERIFY(vpt);
+
+ if (!tmpl_is_attr_unresolved(vpt)) return 1;
+
+ if (fr_dict_attr_add(dict_def,
+ fr_dict_root(fr_dict_internal()), tmpl_attr_unresolved(vpt), -1, type, flags) < 0) {
+ return -1;
+ }
+ da = fr_dict_attr_by_name(dict_def, tmpl_attr_unresolved(vpt));
+ if (!da) return -1;
+
+ if (type != da->type) {
+ fr_strerror_printf("Attribute %s of type %s already defined with type %s",
+ da->name, fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"),
+ fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
+ return -1;
+ }
+
+ if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
+ fr_strerror_printf("Attribute %s already defined with different flags", da->name);
+ return -1;
+ }
+
+ tmpl_attr_set_da(vpt, da);
+ vpt->type = TMPL_TYPE_ATTR;
+
+ return 0;
+}
+
+#ifdef HAVE_REGEX
+/** Convert a TMPL_TYPE_REGEX_UNCOMPILED into a TMPL_TYPE_REGEX
+ *
+ * Other regex types become noops.
+ */
+ssize_t tmpl_regex_compile(tmpl_t *vpt, bool subcaptures, bool runtime)
+{
+ ssize_t slen;
+ char *unescaped = vpt->data.unescaped;
+
+ if (tmpl_is_regex_xlat(vpt) || tmpl_is_regex(vpt)) return 0; /* Don't need compiling */
+
+ fr_assert(tmpl_is_regex_uncompiled(vpt));
+
+ slen = regex_compile(vpt, &vpt->data.reg.ex,
+ unescaped, talloc_array_length(unescaped) - 1,
+ &vpt->data.reg.flags, subcaptures, runtime);
+ if (slen < 0) return slen;
+ talloc_free(unescaped);
+
+ vpt->type = TMPL_TYPE_REGEX;
+
+ TMPL_VERIFY(vpt);
+
+ return slen;
+}
+#endif
+/** @} */
+
+/** @name Print the contents of a #tmpl_t
+ * @{
+ */
+
+/** Print an attribute or list #tmpl_t to a string
+ *
+ * This function is the direct counterpart to #fr_tmpl_afrom_attr_substr.
+ *
+ * @param[in] out Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt to print.
+ * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
+ * references.
+ * - TMPL_ATTR_REF_PREFIX_YES - always print.
+ * - TMPL_ATTR_REF_PREFIX_NO - never print.
+ * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
+ * was prefixed.
+ * @return
+ * - >0 the number of bytes written to the out buffer.
+ * - 0 invalid argument.
+ * - <0 the number of bytes we would have needed to complete the print.
+ */
+ssize_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix)
+{
+ tmpl_request_t *rr = NULL;
+ tmpl_attr_t *ar = NULL;
+ char printed_rr = false;
+ fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
+
+ if (unlikely(!vpt)) return 0;
+
+ TMPL_VERIFY(vpt);
+
+ /*
+ * Only print things we can print...
+ */
+ switch (vpt->type) {
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ case TMPL_TYPE_ATTR:
+ break;
+
+ default:
+ return 0;
+ }
+
+ /*
+ * Handle printing the request reference
+ * prefix.
+ */
+ if ((ar_prefix == TMPL_ATTR_REF_PREFIX_YES) ||
+ ((ar_prefix == TMPL_ATTR_REF_PREFIX_AUTO) && vpt->data.attribute.ref_prefix)) {
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, '&');
+ }
+
+ /*
+ * Print request references
+ */
+ while ((rr = fr_dlist_next(&vpt->data.attribute.rr, rr))) {
+ if (rr->request == REQUEST_CURRENT) continue; /* Don't print the default request */
+
+ FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, request_ref_table, rr->request, "<INVALID>");
+ printed_rr = true;
+ }
+
+ /*
+ * Print list
+ */
+ if (tmpl_list(vpt) != PAIR_LIST_REQUEST) { /* Don't print the default list */
+ if (printed_rr) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+
+ FR_SBUFF_IN_TABLE_STR_RETURN(&our_out, pair_list_table, tmpl_list(vpt), "<INVALID>");
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, vpt->data.attribute.old_list_sep ? ':' : '.');
+ } else if (printed_rr) { /* Request qualifier with no list qualifier */
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, vpt->data.attribute.old_list_sep ? ':' : '.');
+ }
+
+ /*
+ *
+ * If the leaf attribute is unknown and raw we
+ * add the .raw prefix.
+ *
+ * If the leaf attribute is unknown and not raw
+ * we add the .unknown prefix.
+ *
+ */
+ if (tmpl_contains_attr(vpt) && (ar = fr_dlist_tail(&vpt->data.attribute.ar))) {
+ switch (ar->type) {
+ case TMPL_ATTR_TYPE_NORMAL:
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ if (ar->ar_da->flags.is_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
+ break;
+
+ case TMPL_ATTR_TYPE_UNRESOLVED:
+ if (ar->ar_unresolved_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw.");
+ break;
+ }
+ ar = NULL;
+ }
+
+ /*
+ * Print attribute identifiers
+ */
+ while ((ar = fr_dlist_next(&vpt->data.attribute.ar, ar))) {
+ switch(ar->type) {
+ /*
+ * For normal attributes we use the name
+ */
+ case TMPL_ATTR_TYPE_NORMAL:
+ if (ar->ar_da->flags.is_raw) goto do_raw;
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_da->name);
+ break;
+
+ /*
+ * For unknown attributes we use the number
+ */
+ case TMPL_ATTR_TYPE_UNKNOWN:
+ do_raw:
+ /*
+ * We need some context for unknown attributes
+ * so print the first known attribute.
+ */
+ if ((fr_dlist_head(&vpt->data.attribute.ar) == ar) &&
+ ar->da->parent && !ar->da->parent->flags.is_root) {
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->da->parent->name);
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+ }
+ FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", ar->ar_da->attr);
+ break;
+
+ /*
+ * For unresolved attribute we print the raw identifier we
+ * got when parsing the tmpl.
+ */
+ case TMPL_ATTR_TYPE_UNRESOLVED:
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(&our_out, ar->ar_unresolved);
+ break;
+ }
+
+ /*
+ * Add array subscript.
+ *
+ * Will later be complex filters.
+ */
+ switch (ar->ar_num) {
+ case NUM_ANY:
+ break;
+
+ case NUM_ALL:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[*]");
+ break;
+
+ case NUM_COUNT:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[#]");
+ break;
+
+ case NUM_LAST:
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "[n]");
+ break;
+
+ default:
+ FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "[%i]", ar->ar_num);
+ break;
+ }
+
+ if (fr_dlist_next(&vpt->data.attribute.ar, ar)) FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
+ }
+ return fr_sbuff_set(out, &our_out);
+}
+
+/** Print a #tmpl_t to a string
+ *
+ * This function should primarily be used for regenerating vpt->name when the contents
+ * of the #tmpl_t is changed programatically, or when the #tmpl_t is being serialized
+ * in some non-standard way, i.e. as a value for a field in a database.
+ *
+ * This function is the direct counterpart to #fr_tmpl_afrom_substr.
+ *
+ * @note Does not print flags for regular expressions, as the quoting char is needed
+ * to separate the elements of the expression.
+ * Call regex_flags_print to write the flags values to the output buffer.
+ *
+ * @param[out] out Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt to print.
+ * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
+ * references.
+ * - TMPL_ATTR_REF_PREFIX_YES - always print.
+ * - TMPL_ATTR_REF_PREFIX_NO - never print.
+ * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
+ * was prefixed.
+ * @param[in] e_rules Escaping rules used to print strings.
* @return
- * - 1 noop (did nothing) - Not possible to convert tmpl.
- * - 0 on success.
- * - -1 on failure.
+ * - >0 the number of bytes written to the out buffer.
+ * - 0 invalid argument.
+ * - <0 the number of bytes we would have needed to complete the print.
*/
-int tmpl_unknown_attr_add(tmpl_t *vpt)
+ssize_t tmpl_print(fr_sbuff_t *out, tmpl_t const *vpt,
+ tmpl_attr_ref_prefix_t ar_prefix, fr_sbuff_escape_rules_t const *e_rules)
{
- fr_dict_attr_t const *da;
+ fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
- if (!vpt) return 1;
+ if (unlikely(!vpt)) return 0;
TMPL_VERIFY(vpt);
- if (!tmpl_is_attr(vpt)) return 1;
+ switch (vpt->type) {
+ case TMPL_TYPE_LIST:
+ case TMPL_TYPE_ATTR_UNRESOLVED:
+ case TMPL_TYPE_ATTR:
+ FR_SBUFF_RETURN(tmpl_attr_print, &our_out, vpt, ar_prefix);
+ break;
- if (!tmpl_da(vpt)->flags.is_unknown) return 1;
+ case TMPL_TYPE_DATA:
+ FR_SBUFF_RETURN(fr_value_box_print, &our_out, tmpl_value(vpt), e_rules);
+ break;
- da = fr_dict_unknown_add(fr_dict_unconst(fr_dict_internal()), tmpl_da(vpt));
- if (!da) return -1;
- tmpl_attr_set_leaf_da(vpt, da);
+ case TMPL_TYPE_REGEX:
+ FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, vpt->name, vpt->len); /* Fixme - double escapes */
+ break;
- return 0;
+ case TMPL_TYPE_REGEX_UNCOMPILED:
+ FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
+ break;
+
+ case TMPL_TYPE_UNINITIALISED:
+ case TMPL_TYPE_NULL:
+ case TMPL_TYPE_MAX:
+ break;
+
+ /*
+ * The remaining types will either
+ * be xlat expansions, or need
+ * resolving, in which case the
+ * unescaped string is available
+ * in vpt->unescaped.
+ */
+ default:
+ if (tmpl_contains_xlat(vpt)) {
+ FR_SBUFF_RETURN(xlat_print, &our_out, tmpl_xlat(vpt), e_rules);
+ break;
+ }
+
+ if (tmpl_needs_resolving(vpt)) {
+ FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(&our_out, vpt->data.unescaped, e_rules);
+ break;
+ }
+
+ fr_assert_fail("Can't print invalid tmpl type %s",
+ fr_table_str_by_value(tmpl_type_table, vpt->type, "<INVALID>"));
+ break;
+ }
+
+ return fr_sbuff_set(out, &our_out);
}
-/** Add an undefined #fr_dict_attr_t specified by a #tmpl_t to the main dictionary
+/** Print a #tmpl_t to a string with quotes
*
- * @note fr_dict_attr_add will not return an error if the attribute already exists
- * meaning that multiple #tmpl_t specifying the same attribute can be
- * passed to this function to be fixed up, so long as the type and flags
- * are identical.
+ * This function should be used when the tmpl is embedded in some other construct
+ * in the server's configuration.
*
- * @param[in] dict_def Default dictionary to use if none is
- * specified by the tmpl_attr_unresolved.
- * @param[in] vpt specifying undefined attribute to add.
- * ``tmpl_da`` pointer will be updated to
- * point to the #fr_dict_attr_t inserted
- * into the dictionary. Lists and requests
- * will be preserved.
- * @param[in] type to define undefined attribute as.
- * @param[in] flags to define undefined attribute with.
+ * It adds standard quoting around tmpl's used as operands in expressions and applies
+ * the correct escaping rules.
+ *
+ * @param[out] out Where to write the presentation format #tmpl_t string.
+ * @param[in] vpt to print.
+ * @param[in] ar_prefix Whether to print the '&' at the beginning of attribute
+ * references.
+ * - TMPL_ATTR_REF_PREFIX_YES - always print.
+ * - TMPL_ATTR_REF_PREFIX_NO - never print.
+ * - TMPL_ATTR_REF_PREFIX_AUTO - print if the original tmpl
+ * was prefixed.
* @return
- * - 1 noop (did nothing) - Not possible to convert tmpl.
- * - 0 on success.
- * - -1 on failure.
+ * - >0 the number of bytes written to the out buffer.
+ * - 0 invalid argument.
+ * - <0 the number of bytes we would have needed to complete the print.
*/
-int tmpl_unresolved_attr_add(fr_dict_t *dict_def, tmpl_t *vpt,
- fr_type_t type, fr_dict_attr_flags_t const *flags)
+ssize_t tmpl_print_quoted(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_ref_prefix_t ar_prefix)
{
- fr_dict_attr_t const *da;
-
- if (!vpt) return -1;
-
- TMPL_VERIFY(vpt);
-
- if (!tmpl_is_attr_unresolved(vpt)) return 1;
-
- if (fr_dict_attr_add(dict_def, fr_dict_root(fr_dict_internal()), tmpl_attr_unresolved(vpt), -1, type, flags) < 0) {
- return -1;
- }
- da = fr_dict_attr_by_name(dict_def, tmpl_attr_unresolved(vpt));
- if (!da) return -1;
+ fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
- if (type != da->type) {
- fr_strerror_printf("Attribute %s of type %s already defined with type %s",
- da->name, fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"),
- fr_table_str_by_value(fr_value_box_type_table, da->type, "<UNKNOWN>"));
- return -1;
- }
+ char quote = fr_token_quote[vpt->quote];
- if (memcmp(flags, &da->flags, sizeof(*flags)) != 0) {
- fr_strerror_printf("Attribute %s already defined with different flags", da->name);
- return -1;
- }
+ if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
+ FR_SBUFF_RETURN(tmpl_print, &our_out, vpt,
+ ar_prefix, fr_value_escape_by_quote[vpt->quote]);
+ if (quote != '\0') FR_SBUFF_IN_CHAR_RETURN(&our_out, quote);
- tmpl_attr_set_da(vpt, da);
- vpt->type = TMPL_TYPE_ATTR;
+ /*
+ * Optionally print the flags
+ */
+ if (vpt->type & TMPL_FLAG_REGEX) FR_SBUFF_RETURN(regex_flags_print, &our_out, tmpl_regex_flags(vpt));
- return 0;
+ return fr_sbuff_set(out, &our_out);
}
/** @} */
* 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
}
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;
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;
/*
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;
* @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
break;
case TMPL_TYPE_XLAT:
+ case TMPL_TYPE_REGEX_XLAT:
{
fr_value_box_t tmp;
fr_type_t src_type = FR_TYPE_STRING;
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);
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) { /* / + <c> + / + \0 */
- no_space:
- if (out_p > end) out_p = end; /* Safety */
- *out_p = '\0';
- return outlen + 1;
- }
-
- *out_p++ = '/';
- len = fr_snprint(out_p, end - out_p, vpt->name, vpt->len, '\0');
- RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-
- if ((end - out_p) <= 1) goto no_space;
- *out_p++ = '/';
-
- len = regex_flags_snprint(out_p, end - out_p, &tmpl_regex_flags(vpt));
- RETURN_IF_TRUNCATED(need, len, out_p, out, end);
-
- goto finish;
-
- case TMPL_TYPE_XLAT_UNRESOLVED:
- case TMPL_TYPE_XLAT:
- c = '"';
- goto do_literal;
-
- case TMPL_TYPE_EXEC:
- c = '`';
- goto do_literal;
-
- case TMPL_TYPE_UNRESOLVED:
- /*
- * Nasty nasty hack that needs to be fixed.
- *
- * Determines what quoting to use around strings based on their content.
- * Should use vpt->quote, but that's not always set correctly
- * at the moment.
- */
- for (p = vpt->name; *p != '\0'; p++) {
- if (*p == ' ') break;
- if (*p == '\'') break;
- if (*p == '.') continue;
- if (!fr_dict_attr_allowed_chars[(uint8_t) *p]) break;
- }
- c = *p ? '"' : '\0';
-
-do_literal:
- if ((end - out_p) <= 3) goto no_space; /* / + <c> + / + \0 */
- if (c != '\0') *out_p++ = c;
- len = fr_snprint(out_p, (end - out_p) - ((c == '\0') ? 0 : 1), vpt->name, vpt->len, c);
- RETURN_IF_TRUNCATED(need, len, out_p, out, end - ((c == '\0') ? 0 : 1));
-
- if ((end - out_p) <= 1) goto no_space;
- if (c != '\0') *out_p++ = c;
- break;
-
- case TMPL_TYPE_DATA:
- return fr_value_box_snprint(out, end - out_p, tmpl_value(vpt), fr_token_quote[vpt->quote]);
-
- default:
- goto empty;
- }
-
-finish:
- *out_p = '\0';
- return (out_p - out);
-}
-
-#define TMPL_TAG_MATCH(_a, _t) ((_a->da == tmpl_da(_t)) && ATTR_TAG_MATCH(_a, tmpl_tag(_t)))
-
static void *_tmpl_cursor_next(void **prev, void *curr, void *ctx)
{
VALUE_PAIR *c, *p, *fc = NULL, *fp = NULL;
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;
}
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;
}
}
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--;
* - -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.
* @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
*
* @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
*
* @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.
* - 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;
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
*
"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) {
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;
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;
}
*/
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 "
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);
}
*/
case TMPL_TYPE_EXEC:
+ case TMPL_TYPE_EXEC_UNRESOLVED:
/* tmpl_xlat(vpt) can be initialized */
break;
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);
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
*/
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,
}
}
- 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",
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);
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);
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);
}
#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.
*
/** 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.
*/
* 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
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.
/** Helpers to verify the type of #tmpl_t
*/
-#define tmpl_is_uninitialised(vpt) (vpt->type == TMPL_TYPE_UNINITIALISED)
+#define tmpl_is_uninitialised(vpt) (vpt->type == TMPL_TYPE_UNINITIALISED)
-#define tmpl_is_null(vpt) (vpt->type == TMPL_TYPE_NULL)
-#define tmpl_is_data(vpt) (vpt->type == TMPL_TYPE_DATA)
+#define tmpl_is_null(vpt) (vpt->type == TMPL_TYPE_NULL)
+#define tmpl_is_data(vpt) (vpt->type == TMPL_TYPE_DATA)
-#define tmpl_is_attr(vpt) (vpt->type == TMPL_TYPE_ATTR)
-#define tmpl_is_list(vpt) (vpt->type == TMPL_TYPE_LIST)
+#define tmpl_is_attr(vpt) (vpt->type == TMPL_TYPE_ATTR)
+#define tmpl_is_list(vpt) (vpt->type == TMPL_TYPE_LIST)
-#define tmpl_is_xlat(vpt) (vpt->type == TMPL_TYPE_XLAT)
-#define tmpl_is_exec(vpt) (vpt->type == TMPL_TYPE_EXEC)
+#define tmpl_is_xlat(vpt) (vpt->type == TMPL_TYPE_XLAT)
+#define tmpl_is_exec(vpt) (vpt->type == TMPL_TYPE_EXEC)
-#define tmpl_is_regex(vpt) (vpt->type == TMPL_TYPE_REGEX)
+#define tmpl_is_regex(vpt) (vpt->type == TMPL_TYPE_REGEX)
+#define tmpl_is_regex_uncompiled(vpt) (vpt->type == TMPL_TYPE_REGEX_UNCOMPILED)
+#define tmpl_is_regex_xlat(vpt) (vpt->type == TMPL_TYPE_REGEX_XLAT)
#define tmpl_is_unresolved(vpt) (vpt->type == TMPL_TYPE_UNRESOLVED)
-#define tmpl_is_attr_unresolved(vpt) (vpt->type == TMPL_TYPE_ATTR_UNRESOLVED)
-#define tmpl_is_xlat_unresolved(vpt) (vpt->type == TMPL_TYPE_XLAT_UNRESOLVED)
-#define tmpl_is_regex_unresolved(vpt) (vpt->type == TMPL_TYPE_REGEX_UNRESOLVED)
+#define tmpl_is_exec_unresolved(vpt) (vpt->type == TMPL_TYPE_EXEC_UNRESOLVED)
+#define tmpl_is_attr_unresolved(vpt) (vpt->type == TMPL_TYPE_ATTR_UNRESOLVED)
+#define tmpl_is_xlat_unresolved(vpt) (vpt->type == TMPL_TYPE_XLAT_UNRESOLVED)
+#define tmpl_is_regex_xlat_unresolved(vpt) (vpt->type == TMPL_TYPE_REGEX_XLAT_UNRESOLVED)
+
+#define tmpl_needs_resolving(vpt) (vpt->type & TMPL_FLAG_UNRESOLVED)
+#define tmpl_contains_attr(vpt) (vpt->type & TMPL_FLAG_ATTR)
+#define tmpl_contains_regex(vpt) (vpt->type & TMPL_FLAG_REGEX)
+#define tmpl_contains_xlat(vpt) (vpt->type & TMPL_FLAG_XLAT)
-extern fr_table_num_sorted_t const tmpl_type_table[];
+extern fr_table_num_ordered_t const tmpl_type_table[];
extern size_t tmpl_type_table_len;
-typedef struct tmpl_s tmpl_t;
+
typedef struct tmpl_rules_s tmpl_rules_t;
+typedef struct tmpl_s tmpl_t;
#include <freeradius-devel/unlang/xlat.h>
#include <freeradius-devel/util/packet.h>
*/
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;
///< 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)
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;
*
* @{
*/
+#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.
* @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;
/*
*/
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) \
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));
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)
*
* @{
*/
-#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
#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
/** @} */
*
* 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;
* @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"), \
///< 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
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
#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);
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);
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);
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,
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,
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)
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;
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.
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:
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);
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]);
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;
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;
*/
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,
);
}
} else {
- fr_pair_value_asprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
+ fr_pair_value_aprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
}
break;
}
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;
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;
*/
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:
/*
* 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");
* 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;
/*
* 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);
* 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;
if (unlang_subrequest_op_init() < 0) return -1;
unlang_switch_init();
unlang_call_init();
- unlang_tmpl_init();
+ unlang_tmpl_init_shallow();
return 0;
}
/* 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);
}
};
-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 */
* 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) {
* @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;
}
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;
}
}
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;
}
#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
/*
*/
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;
}
/*
if (!paircmp_find(tmpl_da(map->lhs))) return true;
- if (tmpl_is_regex_unresolved(map->rhs)) {
+ if (tmpl_is_regex_xlat_unresolved(map->rhs)) {
cf_log_err(map->ci, "Cannot compare virtual attribute %s via a regex", map->lhs->name);
return false;
}
if (c->cast) {
- cf_log_err(map->ci, "Cannot cast virtual attribute %s", map->lhs->name);
+ cf_log_err(map->ci, "Cannot cast virtual attribute %s to %s", map->lhs->name,
+ fr_table_str_by_value(fr_value_box_type_table, c->cast->type, "<INVALID>"));
return false;
}
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;
/*
* 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
* 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;
}
}
* 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;
}
/*
* 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;
}
}
*/
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)
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);
}
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);
}
/*
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);
}
/*
/*
* 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;
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:
* 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;
* 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;
}
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;
}
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;
}
* 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;
}
* 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;
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);
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");
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);
* 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;
/*
* 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;
}
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;
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)
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:
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;
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;
/*
* 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;
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 = ↦
fr_assert(g->vpt != NULL);
null_case = found = NULL;
- data.datum.ptr = NULL;
/*
* The attribute doesn't exist. We can skip
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);
}
/*
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;
}
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"
}
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;
}
*/
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.
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){
unlang_t self;
tmpl_t const *tmpl;
bool inline_exec;
+ xlat_flags_t flags;
} unlang_tmpl_t;
/** State of a redundant operation
void unlang_switch_init(void);
-void unlang_tmpl_init(void);
+void unlang_tmpl_init_shallow(void);
/** @} */
#ifdef __cplusplus
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;
#include <freeradius-devel/util/time.h>
#include <freeradius-devel/util/pair.h>
#include <freeradius-devel/util/value.h>
+#include <freeradius-devel/util/sbuff.h>
/** Instance data for an xlat expansion node
*
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;
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
/*
* 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
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);
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.
*
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);
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;
}
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);
/** 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
/** 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
* 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,
fr_table_num_ordered_t const *type;
size_t i;
- if (vp->da->flags.has_tag) {
- RIDEBUG2("&%s:%s:%i %s %pV",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
- vp->da->name,
- vp->tag,
- fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
- &vp->data);
- } else {
- RIDEBUG2("&%s:%s %s %pV",
- fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
- vp->da->name,
- fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
- &vp->data);
- }
+ RIDEBUG2("&%s:%s %s %pV",
+ fr_table_str_by_value(pair_list_table, tmpl_list(vpt), "<INVALID>"),
+ vp->da->name,
+ fr_table_str_by_value(fr_tokens_table, vp->op, "<INVALID>"),
+ &vp->data);
if (!RDEBUG_ENABLED3) continue;
+ RIDEBUG3("da : %p", vp->da);
+ RIDEBUG3("is_raw : %pV", fr_box_bool(vp->da->flags.is_raw));
+ RIDEBUG3("is_unknown : %pV", fr_box_bool(vp->da->flags.is_unknown));
+
+ if (RDEBUG_ENABLED3) {
+ RIDEBUG3("parent : %s (%p)", vp->da->parent->name, vp->da->parent);
+ } else {
+ RIDEBUG2("parent : %s", vp->da->parent->name);
+ }
+ RIDEBUG3("attr : %u", vp->da->attr);
vendor = fr_dict_vendor_by_da(vp->da);
- if (vendor) RIDEBUG2("Vendor : %i (%s)", vendor->pen, vendor->name);
- RIDEBUG2("Type : %s", fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
+ if (vendor) RIDEBUG2("vendor : %i (%s)", vendor->pen, vendor->name);
+ RIDEBUG3("type : %s", fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
switch (vp->vp_type) {
case FR_TYPE_VARIABLE_SIZE:
- RIDEBUG2("Length : %zu", vp->vp_length);
+ RIDEBUG3("length : %zu", vp->vp_length);
break;
default:
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:
*/
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;
}
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);
*/
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 */
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;
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;
}
switch (map->lhs->type) {
case TMPL_TYPE_ATTR:
case TMPL_TYPE_LIST:
- case TMPL_TYPE_XLAT_UNRESOLVED:
+ case TMPL_TYPE_XLAT:
break;
default:
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:
* 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();
*/
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;
* Example:
@verbatim
"%{concat:, %{User-Name}%{Calling-Station-Id}" == "bob, aa:bb:cc:dd:ee:ff"
-"%{concat:, %{request:[*]}" == "<attr1value>, <attr2value>, <attr3value>, ..."
+"%{concat:, %{request[*]}" == "<attr1value>, <attr2value>, <attr3value>, ..."
@endverbatim
*
* @ingroup xlat_functions
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));
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");
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;
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;
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
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);
};
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.
{
switch (node->type) {
case XLAT_LITERAL:
- case XLAT_CHILD:
+ case XLAT_GROUP:
return talloc_asprintf(ctx, "%s", node->fmt);
case XLAT_ONE_LETTER:
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:
{
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;
* 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);
}
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
* 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;
switch (node->type) {
case XLAT_FUNC:
- switch (node->xlat->type) {
+ switch (node->call.func->type) {
case XLAT_FUNC_LEGACY:
{
fr_value_box_t *value;
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);
/*
* 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);
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) {
}
break;
- case XLAT_CHILD:
+ case XLAT_GROUP:
{
fr_value_box_t *value;
fr_cursor_t from;
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, "");
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.
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);
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);
*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:
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
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;
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);
* 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");
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);
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);
}
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);
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;
}
*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);
#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);
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;
}
/*
* 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;
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
* 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;
}
/*
* 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';
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);
if (slen < 0) return -i;
}
- return xlat->count;
+ return count;
}
/** Turn xlat_tokenize_argv() into an argv[] array
*/
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);
my_argv[i] = node->child;
}
- return xlat->count;
+ return count;
}
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;
}
/** 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.
*/
{
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;
{
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;
(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));
}
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
}
/*
* 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;
}
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));
}
* 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
}
*/
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;
}
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;
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);
{
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;
}
* 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.
*
*/
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.
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 {
* @file xlat_tokenize.c
* @brief String expansion ("translation"). Tokenizes xlat expansion strings.
*
- * @copyright 2000,2006 The FreeRADIUS server project
* @copyright 2000 Alan DeKok (aland@freeradius.org)
+ * @copyright 2000,2006 The FreeRADIUS server project
+ * @copyright 2017-2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
*/
RCSID("$Id$")
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/sbuff.h>
#include <freeradius-devel/server/request.h>
#include <freeradius-devel/server/regex.h>
#include <freeradius-devel/unlang/xlat_priv.h>
#include <ctype.h>
#undef XLAT_DEBUG
+#undef XLAT_HEXDUMP
#ifdef DEBUG_XLAT
-# define XLAT_DEBUG DEBUG3
+# define XLAT_DEBUG(_fmt, ...) DEBUG3("%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
+# define XLAT_HEXDUMP(_data, _len, _fmt, ...) HEXDUMP3(_data, _len, "%s[%i] "_fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#else
# define XLAT_DEBUG(...)
+# define XLAT_HEXDUMP(...)
#endif
+/** These rules apply to literals and function arguments inside of an expansion
+ *
+ */
+static fr_sbuff_unescape_rules_t const xlat_unescape = {
+ .name = "xlat",
+ .chr = '\\',
+ .subs = {
+ ['a'] = '\a',
+ ['b'] = '\b',
+ ['e'] = '\\',
+ ['n'] = '\n',
+ ['r'] = '\r',
+ ['t'] = '\t',
+ ['v'] = '\v',
+ ['\\'] = '\\',
+ ['%'] = '%', /* Expansion begin */
+ ['}'] = '}' /* Expansion end */
+ },
+ .do_hex = true,
+ .do_oct = true
+};
+
+/** These rules apply to literals and function arguments inside of an expansion
+ *
+ */
+static fr_sbuff_escape_rules_t const xlat_escape = {
+ .name = "xlat",
+ .chr = '\\',
+ .subs = {
+ ['\a'] = 'a',
+ ['\b'] = 'b',
+ ['\n'] = 'n',
+ ['\r'] = 'r',
+ ['\t'] = 't',
+ ['\v'] = 'v',
+ ['\\'] = '\\',
+ ['%'] = '%', /* Expansion begin */
+ ['}'] = '}' /* Expansion end */
+ },
+ .esc = {
+ SBUFF_CHAR_UNPRINTABLES_LOW,
+ SBUFF_CHAR_UNPRINTABLES_EXTENDED
+ },
+ .do_utf8 = true,
+ .do_oct = true
+};
+
+/** Parse rules for literals inside of an expansion
+ *
+ * These rules are used to parse literals as arguments to functions and
+ * on the RHS of alternations.
+ *
+ * The caller sets the literal parse rules for outside of expansions when they
+ * call xlat_tokenize.
+ */
+static fr_sbuff_parse_rules_t const xlat_rules = {
+ .escapes = &xlat_unescape,
+ .terminals = &FR_SBUFF_TERM("}") /* These get merged with other literal terminals */
+};
+
+/** Allocate an xlat node with no name, and no type set
+ *
+ * @param[in] ctx to allocate node in.
+ * @return A new xlat node.
+ */
+static inline CC_HINT(always_inline) xlat_exp_t *xlat_exp_alloc_null(TALLOC_CTX *ctx)
+{
+ xlat_exp_t *node;
+
+ MEM(node = talloc_zero(ctx, xlat_exp_t));
+
+ return node;
+}
+
/** Allocate an xlat node
*
* @param[in] ctx to allocate node in.
* @param[in] type of the node.
* @param[in] in original input string.
- * @param[in] inlen Portion of the input string that this node represents.
* @return A new xlat node.
*/
-static inline xlat_exp_t *xlat_exp_alloc(TALLOC_CTX *ctx, xlat_type_t type, char const *in, size_t inlen)
+static inline CC_HINT(always_inline) xlat_exp_t *xlat_exp_alloc(TALLOC_CTX *ctx, xlat_type_t type,
+ char const *in, size_t inlen)
{
xlat_exp_t *node;
- MEM(node = talloc_zero(ctx, xlat_exp_t));
+ node = xlat_exp_alloc_null(ctx);
node->type = type;
if (in) node->fmt = talloc_bstrndup(node, in, inlen);
return node;
}
-/** Try to convert an xlat to a tmpl for efficiency
+/** Set the type of an xlat node
*
- * @param ctx to allocate new tmpl_t in.
- * @param node to convert.
- * @return
- * - NULL if unable to convert (not necessarily error).
- * - A new #tmpl_t.
+ * @param[in] node to set type for.
+ * @param[in] type to set.
*/
-tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
+static inline CC_HINT(always_inline) void xlat_exp_set_type(xlat_exp_t *node, xlat_type_t type)
{
- tmpl_t *vpt;
-
- if (node->next || (node->type != XLAT_ATTRIBUTE) || !tmpl_is_attr(node->attr)) return NULL;
-
- /*
- * Concat means something completely different as an attribute reference
- * Count isn't implemented.
- */
- if ((tmpl_num(node->attr) == NUM_COUNT) || (tmpl_num(node->attr) == NUM_ALL)) return NULL;
-
- vpt = tmpl_alloc(ctx, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1, T_BARE_WORD);
- if (!vpt) return NULL;
-
- tmpl_attr_copy(vpt, node->attr);
-
- TMPL_VERIFY(vpt);
+ node->type = type;
+}
- return vpt;
+#if 0
+/** Set the format string for an xlat node
+ *
+ * @param[in] node to set fmt for.
+ * @param[in] fmt talloced buffer to set as the fmt string.
+ */
+static inline CC_HINT(always_inline) void xlat_exp_set_name_buffer(xlat_exp_t *node, char const *fmt)
+{
+ if (node->fmt) talloc_const_free(node->fmt);
+ node->fmt = talloc_bstrdup(node, fmt);
}
+#endif
-/** Convert attr tmpl to an xlat for &attr[*]
+/** Set the format string for an xlat node
*
- * @param ctx to allocate new xlat_expt_t in.
- * @param vpt to convert.
- * @return
- * - NULL if unable to convert (not necessarily error).
- * - a new #tmpl_t.
+ * @param[in] node to set fmt for.
+ * @param[in] fmt talloced buffer to set as the fmt string.
*/
-xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, tmpl_t *vpt)
+static inline CC_HINT(always_inline) void xlat_exp_set_name_buffer_shallow(xlat_exp_t *node, char const *fmt)
{
- xlat_exp_t *node;
+ if (node->fmt) talloc_const_free(node->fmt);
+ node->fmt = fmt;
+}
- if (!tmpl_is_attr(vpt)) return NULL;
+/** Merge flags from child to parent
+ *
+ * For pass2, if either the parent or child is marked up for pass2, then the parent
+ * is marked up for pass2.
+ *
+ * For needs_async, if both the parent and the child are needs_async, the parent is
+ * needs_async.
+ */
+static inline CC_HINT(always_inline) void xlat_flags_merge(xlat_flags_t *parent, xlat_flags_t const *child)
+{
+ parent->needs_async |= child->needs_async;
+ parent->needs_resolving |= child->needs_resolving;
+}
- node = xlat_exp_alloc(ctx, XLAT_ATTRIBUTE, vpt->name, vpt->len);
- node->attr = tmpl_alloc(node, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1, T_BARE_WORD);
- tmpl_attr_copy(node->attr, vpt);
+/** Free a linked list of xlat nodes
+ *
+ * @param[in,out] head to free. Will be set to NULL
+ */
+void xlat_exp_free(xlat_exp_t **head)
+{
+ xlat_exp_t *to_free = *head, *next;
- return node;
+ while (to_free) {
+ next = to_free->next;
+ talloc_free(to_free);
+ to_free = next;
+ };
+ *head = NULL;
}
-static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules);
-static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- bool brace, tmpl_rules_t const *rules);
+static int xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ tmpl_rules_t const *ar_rules);
-static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules)
+static int xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ bool brace,
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules);
+
+static inline int xlat_tokenize_alternation(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ tmpl_rules_t const *ar_rules)
{
- ssize_t slen;
- char const *p = in;
xlat_exp_t *node;
- fr_assert(in[0] == '%');
- fr_assert(in[1] == '{');
- fr_assert(in[2] == '%');
- fr_assert(in[3] == '{');
-
- XLAT_DEBUG("ALTERNATE <-- %s", in);
-
- node = xlat_exp_alloc(ctx, XLAT_ALTERNATE, NULL, 0);
- p += 2;
- slen = xlat_tokenize_expansion(node, &node->child, p, in + inlen - p, rules);
- if (slen <= 0) {
- talloc_free(node);
- return slen - (p - in);
- }
- p += slen;
+ XLAT_DEBUG("ALTERNATE <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
- if (p[0] != ':') {
+ node = xlat_exp_alloc_null(ctx);
+ xlat_exp_set_type(node, XLAT_ALTERNATE);
+ if (xlat_tokenize_expansion(node, &node->child, &node->flags, in, ar_rules) < 0) {
+ error:
+ *head = NULL;
talloc_free(node);
- fr_strerror_printf("Expected ':' after first expansion, got '%pV'",
- fr_box_strvalue_len(p, 1));
- return -(p - in);
+ return -1;
}
- p++;
- if (p[0] != '-') {
- talloc_free(node);
- fr_strerror_printf("Expected '-' after ':'");
- return -(p - in);
+ if (!fr_sbuff_adv_past_str_literal(in, ":-")) {
+ fr_strerror_printf("Expected ':-' after first expansion");
+ goto error;
}
- p++;
/*
* Allow the RHS to be empty as a special case.
*/
- switch (*p) {
- case '}':
+ if (fr_sbuff_next_if_char(in, '}')) {
node->alternate = xlat_exp_alloc(node, XLAT_LITERAL, "", 0);
- node->async_safe = node->child->async_safe;
+ xlat_flags_merge(&node->flags, &node->child->flags);
*head = node;
- return (p + 1) - in;
-
- case '\0':
- fr_strerror_printf("No matching closing brace");
- talloc_free(node);
- return -2;
+ return 0;
}
/*
* Parse the alternate expansion.
*/
- slen = xlat_tokenize_literal(node, &node->alternate, p, in + inlen - p, true, rules);
- if (slen <= 0) {
- talloc_free(node);
- return slen - (p - in);
- }
+ if (xlat_tokenize_literal(node, &node->alternate, &node->flags, in,
+ true, &xlat_rules, ar_rules) < 0) goto error;
if (!node->alternate) {
talloc_free(node);
fr_strerror_printf("Empty expansion is invalid");
- return -(p - in);
+ goto error;
+ }
+
+ if (!fr_sbuff_next_if_char(in, '}')) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
}
- p += slen;
- node->async_safe = (node->child->async_safe && node->alternate->async_safe);
+ xlat_flags_merge(flags, &node->flags);
*head = node;
- return p - in;
+ return 0;
}
#ifdef HAVE_REGEX
* @verbatim %{<num>} @endverbatim
*
*/
-static inline ssize_t xlat_tokenize_regex(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen)
+static inline int xlat_tokenize_regex(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in)
{
- unsigned long num;
- char const *p, *end;
- xlat_exp_t *node;
-
- /*
- * Must be at least %{0} in length
- */
- if (inlen < 4) return 0;
+ uint8_t num;
+ xlat_exp_t *node;
+ fr_sbuff_parse_error_t err;
+ fr_sbuff_marker_t m_s;
- fr_assert(in[0] == '%');
- fr_assert(in[1] == '{');
+ XLAT_DEBUG("REGEX <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
- p = in + 2;
- end = in + inlen;
+ fr_sbuff_marker(&m_s, in);
- if (*p == '}') {
- fail:
+ fr_sbuff_out(&err, &num, in);
+ if (err != FR_SBUFF_PARSE_OK) {
+ invalid_ref:
fr_strerror_printf("Invalid regex reference. Must be in range 0-%u", REQUEST_MAX_REGEX);
- return -(p - in); /* error */
+ fr_sbuff_marker_release(&m_s);
+ return -1;
}
- num = 0;
- while (p < end) {
- if (*p == '}') break;
-
- /*
- * We allow numbers followed by letters for attributes such as 3GPP-...
- */
- if (!((p[0] >= '0') && (p[0] <= '9'))) return 0;
- num *= 10;
- num += p[0] - '0';
- p++;
-
- if (num > REQUEST_MAX_REGEX) {
- p = in + 2;
- goto fail;
- };
+ if (num > REQUEST_MAX_REGEX) {
+ fr_sbuff_set(in, &m_s);
+ goto invalid_ref;
}
- if (*p != '}') {
- fr_strerror_printf("No matching closing brace");
- return -1;
+ if (!fr_sbuff_is_char(in, '}')) {
+ if (!fr_sbuff_remaining(in)) {
+ fr_strerror_printf("Missing closing brace");
+ fr_sbuff_marker_release(&m_s);
+ return -1;
+ }
+ fr_sbuff_set(in, &m_s);
+ fr_sbuff_marker_release(&m_s);
+ return 1;
}
- XLAT_DEBUG("REGEX <-- %pV", fr_box_strvalue_len(in, (p - in) + 1));
-
- node = xlat_exp_alloc(ctx, XLAT_REGEX, p, p - (in + 2));
+ node = xlat_exp_alloc(ctx, XLAT_REGEX, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
node->regex_index = num;
- node->async_safe = true;
- *head = node;
+ node->flags.needs_async = false;
+
+ fr_sbuff_marker_release(&m_s);
+ fr_sbuff_next(in); /* Skip '}' */
- p++; /* Skip over '}' */
+ xlat_flags_merge(flags, &node->flags);
+ *head = node;
- return p - in;
+ return 0;
}
#endif
* Parses a function call string in the format
* @verbatim %{<func>:<arguments} @endverbatim
*
+ * @return
+ * - 0 if the string was parsed into a function.
+ * - <0 on parse error.
*/
-static inline ssize_t xlat_tokenize_function(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules)
+static inline int xlat_tokenize_function(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ tmpl_rules_t const *rules)
{
- ssize_t slen;
- char const *p, *q, *end;
- xlat_exp_t *node;
- xlat_t *func;
-
- if (inlen < 2) return 0;
+ xlat_exp_t *node;
+ xlat_t *func;
+ fr_sbuff_marker_t m_s;
- fr_assert(in[0] == '%');
- fr_assert(in[1] == '{');
+ /*
+ * Special characters, spaces, etc. cannot be
+ * module names.
+ */
+ static bool const func_chars[UINT8_MAX + 1] = {
+ SBUFF_CHAR_CLASS_ALPHA_NUM,
+ ['.'] = true, ['-'] = true, ['_'] = true,
+ };
- p = in + 2;
- end = in + inlen;
+ XLAT_DEBUG("FUNC <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
/*
* %{module:args}
*/
- for (q = p; q < end; q++) {
- if (*q == ':') break;
-
- /*
- * Special characters, spaces, etc. cannot be
- * module names.
- */
- if ((*q < '0') && (*q != '.') && (*q != '-')) return 0;
+ fr_sbuff_marker(&m_s, in);
+ fr_sbuff_adv_past_allowed(in, SIZE_MAX, func_chars);
+
+ if (!fr_sbuff_is_char(in, ':')) {
+ fr_strerror_printf("Can't find function/argument separator");
+ bad_function:
+ *head = NULL;
+ fr_sbuff_set(in, &m_s); /* backtrack */
+ fr_sbuff_marker_release(&m_s);
+ return -1;
}
- if (q >= end) return 0;
-
- func = xlat_func_find(p, q - p);
- if (!func) return 0;
+ func = xlat_func_find(fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
/*
* Allocate a node to hold the function
*/
- node = xlat_exp_alloc(ctx, XLAT_FUNC, p, q - p);
- node->xlat = func;
+ node = xlat_exp_alloc(ctx, XLAT_FUNC, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
+ if (!func) {
+ if (!rules || !rules->allow_unresolved) {
+ fr_strerror_printf("Unresolved expansion functions are not allowed here");
+ goto bad_function;
+ }
+ xlat_exp_set_type(node, XLAT_FUNC_UNRESOLVED);
+ node->flags.needs_resolving = true; /* Needs resolution during pass2 */
+ } else {
+ node->call.func = func;
+ }
- p = q + 1;
- XLAT_DEBUG("FUNC <-- %s ... %s", node->fmt, p);
+ fr_sbuff_next(in); /* Skip the ':' */
+ XLAT_DEBUG("FUNC-ARGS <-- %s ... %pV",
+ node->fmt, fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
+
+ fr_sbuff_marker_release(&m_s);
/*
* Now parse the child nodes that form the
* function's arguments.
*/
- slen = xlat_tokenize_literal(node, &node->child, p, in + inlen - p, true, rules);
- if (slen < 0) {
+ if (xlat_tokenize_literal(node, &node->child, &node->flags, in, true, &xlat_rules, rules) < 0) {
+ error:
+ *head = NULL;
talloc_free(node);
- return slen - (p - in); /* error */
+ return -1;
}
- p += slen;
- if (*(p - 1) != '}') { /* @todo: xlat_tokenize_literal should not consume the closing brace */
- fr_strerror_printf("No matching closing brace");
- talloc_free(node);
- return -1; /* error at the second character of format string */
+ if (!fr_sbuff_next_if_char(in, '}')) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
}
- node->async_safe = (func->async_safe && (!node->child || node->child->async_safe));
+ xlat_flags_merge(flags, &node->flags);
*head = node;
- return p - in;
+ return 0;
}
/** Parse an attribute ref or a virtual attribute
*
*/
-static inline ssize_t xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules)
+static inline int xlat_tokenize_attribute(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
{
ssize_t slen;
- char const *p, *q;
attr_ref_error_t err;
- tmpl_t *vpt = NULL;
+ tmpl_t *vpt = NULL;
xlat_exp_t *node;
xlat_t *func;
+ fr_sbuff_marker_t m_s;
+
+ XLAT_DEBUG("ATTRIBUTE <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
/*
* We need a local copy as we always allow unknowns.
* and instead are "virtual" attributes like
* Foreach-Variable-N.
*/
- tmpl_rules_t our_rules;
+ tmpl_rules_t our_ar_rules;
- if (inlen < 2) return 0;
-
- fr_assert(in[0] == '%');
- fr_assert(in[1] == '{');
-
- if (rules) {
- memcpy(&our_rules, rules, sizeof(our_rules));
+ if (ar_rules) {
+ memcpy(&our_ar_rules, ar_rules, sizeof(our_ar_rules));
} else {
- memset(&our_rules, 0, sizeof(our_rules));
+ memset(&our_ar_rules, 0, sizeof(our_ar_rules));
}
- p = in + 2;
+ our_ar_rules.allow_unresolved = true; /* So we can check for virtual attributes later */
+ our_ar_rules.prefix = TMPL_ATTR_REF_PREFIX_NO; /* Must be NO to stop %{&User-Name} */
- our_rules.allow_unresolved = true; /* So we can check for virtual attributes later */
- our_rules.prefix = TMPL_ATTR_REF_PREFIX_NO; /* Must be NO to stop %{&User-Name} */
- slen = tmpl_afrom_attr_substr(NULL, &err, &vpt, p, inlen - 2, &our_rules);
+ fr_sbuff_marker(&m_s, in);
+
+ MEM(node = xlat_exp_alloc_null(ctx));
+ slen = tmpl_afrom_attr_substr(node, &err, &vpt, in, p_rules, &our_ar_rules);
if (slen <= 0) {
+ fr_sbuff_advance(in, slen * -1);
+
/*
* If the parse error occurred before the ':'
* then the error is changed to 'Unknown module',
* as it was more likely to be a bad module name,
* than a request qualifier.
*/
- if (err == ATTR_REF_ERROR_INVALID_LIST_QUALIFIER) {
- fr_strerror_printf("Unknown expansion function or invalid list qualifier");
- }
- return slen - (p - in); /* error somewhere after second character */
- }
- q = p + slen;
-
- if (*q != '}') {
- fr_strerror_printf("No matching closing brace");
- talloc_free(vpt);
- return -1; /* error @ second character of format string */
+ if (err == ATTR_REF_ERROR_MISSING_TERMINATOR) fr_sbuff_set(in, &m_s);
+ error:
+ *head = NULL;
+ fr_sbuff_marker_release(&m_s);
+ talloc_free(node);
+ return -1;
}
- q++; /* Skip over the closing brace */
-
/*
* Might be a virtual XLAT attribute, which is identical
* to a normal function but called without an argument
* list.
+ *
+ * We only consider virtual attributes at the top level
+ * but we do allow request references and lists to be
+ * specified so that the virtual attribute can operate
+ * in different contexts (i.e. on the parent request).
*/
- if (tmpl_is_attr_unresolved(vpt)) {
- func = xlat_func_find(tmpl_attr_unresolved(vpt), -1);
- if (func && (func->type == XLAT_FUNC_LEGACY)) {
- node = xlat_exp_alloc(ctx, XLAT_VIRTUAL,
- tmpl_attr_unresolved(vpt), talloc_array_length(tmpl_attr_unresolved(vpt)) - 1);
- talloc_free(vpt); /* Free the tmpl, we don't need it */
-
- XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
- node->xlat = func;
- node->async_safe = func->async_safe;
- *head = node;
+ if ((tmpl_is_attr_unresolved(vpt) || (tmpl_is_attr(vpt) && tmpl_da(vpt)->flags.virtual)) &&
+ (tmpl_attr_ref_count(vpt) == 1)) {
+ if (tmpl_is_attr(vpt)) {
+ func = xlat_func_find(tmpl_da(vpt)->name, -1);
+ } else {
+ func = xlat_func_find(tmpl_attr_unresolved(vpt), -1);
+ }
+ if (func) {
+ xlat_exp_set_type(node, XLAT_VIRTUAL);
+ xlat_exp_set_name_buffer_shallow(node, vpt->name);
+
+ XLAT_DEBUG("VIRTUAL <-- %pV",
+ fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
+ node->call.func = func;
+ node->attr = vpt; /* Store for context */
+ node->flags.needs_async = func->needs_async;
+
+ if (!fr_sbuff_next_if_char(in, '}')) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
+ }
- return q - in;
+ goto done;
}
- talloc_free(vpt);
- fr_strerror_printf("Unknown attribute");
- return -2; /* error @ third character of format string */
- }
+ /*
+ * Hack - We need a proper virtual attribute
+ * registry. There seems to have been a
+ * partial conversion at some point which
+ * needs completing.
+ *
+ * This means we fall back to creating the
+ * tmpl at a normal attribute which gets
+ * processed correctly by the eval code
+ * because of the virtual flag in the dictionary.
+ */
+ if (tmpl_is_attr(vpt)) goto do_attr;
- node = xlat_exp_alloc(ctx, XLAT_ATTRIBUTE, vpt->name, vpt->len);
- node->attr = talloc_steal(node, vpt);
- node->async_safe = true; /* attribute expansions are always async-safe */
- *head = node;
+ /*
+ * Not a normal attribute, not a virtual attribute
+ * and we're not allowing unresolved attributes.
+ */
+ if (!ar_rules || !ar_rules->allow_unresolved) {
+ talloc_free(vpt);
- return q - in;
-}
+ fr_strerror_printf("Unresolved attributes not allowed in expansions here");
+ fr_sbuff_set(in, &m_s); /* Error at the start of the attribute */
+ goto error;
+ }
-static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules)
-{
- ssize_t slen;
- char const *p = in, *q, *end;
+ /*
+ * Mark the xlat exp as needing pass2 resolution.
+ */
+ xlat_exp_set_type(node, XLAT_VIRTUAL_UNRESOLVED);
+ xlat_exp_set_name_buffer_shallow(node, vpt->name); /* vpt may be freed */
+
+ node->attr = vpt;
+ node->flags.needs_resolving = true;
+ /*
+ * It's a straight attribute, nothing special
+ */
+ } else {
+ do_attr:
+ xlat_exp_set_type(node, XLAT_ATTRIBUTE);
+ xlat_exp_set_name_buffer_shallow(node, vpt->name);
+
+ node->attr = vpt;
+ }
- XLAT_DEBUG("EXPANSION <-- %s", in);
+ if (!fr_sbuff_next_if_char(in, '}')) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
+ }
- if (inlen < 2) return 0;
+done:
+ xlat_flags_merge(flags, &node->flags);
+ *head = node;
+ fr_sbuff_marker_release(&m_s);
+ return 0;
+}
- fr_assert(in[0] == '%');
- fr_assert(in[1] == '{');
+static int xlat_tokenize_expansion(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in,
+ tmpl_rules_t const *ar_rules)
+{
+ size_t len;
+ fr_sbuff_marker_t s_m;
+ char hint;
+ int ret;
+ fr_sbuff_term_t hint_tokens = FR_SBUFF_TERMS(
+ L(" "), /* First special token is a ' ' - Likely a syntax error */
+ L(":"), /* First special token is a ':' i.e. '%{func:' */
+ L("["), /* First special token is a '[' i.e. '%{attr[<idx>]}' */
+ L("}") /* First special token is a '}' i.e. '%{<attrref>}' */
+ );
+
+ fr_sbuff_parse_rules_t attr_p_rules = {
+ .escapes = &xlat_unescape,
+ .terminals = &FR_SBUFF_TERM("}")
+ };
+
+ XLAT_DEBUG("EXPANSION <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
/*
- * %{%{...}:-bar}
+ * %{...}:-bar}
*/
- if ((in[2] == '%') && (in[3] == '{')) return xlat_tokenize_alternation(ctx, head, in, inlen, rules);
+ if (fr_sbuff_adv_past_str_literal(in, "%{")) {
+ return xlat_tokenize_alternation(ctx, head, flags, in, ar_rules);
+ }
/*
- * %{:-bar}
+ * :-bar}
*/
- if ((in[2] == ':') && (in[3] == '-')) {
+ if (fr_sbuff_is_str_literal(in, ":-")) {
fr_strerror_printf("First item in alternation cannot be empty");
return -2;
}
/*
* Handle regex's %{<num>} specially.
*/
- if (isdigit(in[2])) {
- slen = xlat_tokenize_regex(ctx, head, in, inlen);
- if (slen != 0) return slen; /* If slen == 0 means this wasn't a regex */
+ if (fr_sbuff_is_digit(in)) {
+ ret = xlat_tokenize_regex(ctx, head, flags, in);
+ if (ret <= 0) return ret;
}
#endif /* HAVE_REGEX */
/*
* %{Attr-Name}
* %{Attr-Name[#]}
- * %{Tunnel-Password:1}
- * %{Tunnel-Password:1[#]}
- * %{request:Attr-Name}
- * %{request:Tunnel-Password:1}
- * %{request:Tunnel-Password:1[#]}
+ * %{request.Attr-Name}
* %{mod:foo}
*/
/*
- * This is for efficiency, so we don't search for an xlat,
- * when what's being referenced is obviously an attribute.
+ * Check for empty expressions %{} %{: %{[
*/
- p = in;
- end = in + inlen;
-
- for (q = p; (q < end) && *q; q++) {
- if (*q == ':') break; /* First special token is a ':' i.e. '%{func:' */
+ fr_sbuff_marker(&s_m, in);
+ len = fr_sbuff_adv_until(in, SIZE_MAX, &hint_tokens, '\0');
- if (isspace((int) *q)) break; /* First special token is a ' ' - Likely a syntax error */
-
- if (*q == '[') break; /* First special token is a '[' i.e. '%{attr[<idx>]}' */
-
- if (*q == '}') break; /* First special token is a '}' i.e. '%{<attrref>}' */
+ /*
+ * This means the end of a string not containing any of the other
+ * tokens was reached.
+ *
+ * e.g. '%{myfirstxlat'
+ */
+ if (!fr_sbuff_remaining(in)) {
+ fr_strerror_printf("Missing closing brace");
+ fr_sbuff_marker_release(&s_m);
+ return -1;
}
- XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", *q);
+ hint = *fr_sbuff_current(in);
- /*
- * Check for empty expressions %{} %{: %{[
- */
- if (q == (p + 2)) {
- switch (*q) {
+ XLAT_DEBUG("EXPANSION HINT TOKEN '%c'", hint);
+ if (len == 0) {
+ switch (hint) {
case '}':
fr_strerror_printf("Empty expression is invalid");
- return -2; /* error @ third character of format string */
+ return -1;
case ':':
- fr_strerror_printf("Missing expansion function or list qualifier");
- return -2;
+ fr_strerror_printf("Missing expansion function");
+ return -1;
case '[':
fr_strerror_printf("Missing attribute name");
- return -2;
+ return -1;
default:
break;
}
/*
- * Hint token is a ':' it's either:
- * - An xlat function %{<func>:<args}
- * - An attribute reference with a list separator %{<list>:<attr>}
+ * Hint token is a ':' it's an xlat function %{<func>:<args}
*/
- switch (*q) {
+ switch (hint) {
case ':':
- slen = xlat_tokenize_function(ctx, head, in, inlen, rules);
- if (slen != 0) return slen;
- FALL_THROUGH;
+ {
+ fr_sbuff_set(in, &s_m); /* backtrack */
+ fr_sbuff_marker_release(&s_m);
+
+ ret = xlat_tokenize_function(ctx, head, flags, in, ar_rules);
+ if (ret <= 0) return ret;
+ }
+ break;
/*
* Hint token is a:
* - '[' - Which is an attribute index, so it must be an attribute.
* - '}' - The end of the expansion, which means it was a bareword.
*/
+ case '.':
case '}':
case '[':
- slen = xlat_tokenize_attribute(ctx, head, in, inlen, rules);
- if (slen < 0) return slen;
- fr_assert(slen != 0);
+ fr_sbuff_set(in, &s_m); /* backtrack */
+ fr_sbuff_marker_release(&s_m);
- p += slen;
+ if (xlat_tokenize_attribute(ctx, head, flags, in, &attr_p_rules, ar_rules) < 0) return -1;
break;
- /*
- * Hint token is a '\0'
- *
- * This means the end of a string not containing any of the other
- * tokens was reached.
- *
- * e.g. '%{myfirstxlat'
- */
- case '\0':
- fr_strerror_printf("No matching closing brace");
- return -1; /* error @ second character of format string */
-
/*
* Hint token was whitespace
*
*/
default:
/*
- * Box is so we get \t \n etc..
+ * Box print is so we get \t \n etc..
*/
- fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(q, 1));
- return -(q - in);
+ fr_strerror_printf("Invalid char '%pV' in expression", fr_box_strvalue_len(fr_sbuff_current(in), 1));
+ return -1;
}
- return (p - in);
+ return 0;
}
-
-static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- bool brace, tmpl_rules_t const *rules)
+/** Parse an xlat literal i.e. a non-expansion or non-function
+ *
+ * When this function is being used outside of an xlat expansion, i.e. on a string
+ * which contains one or more xlat expansions, it uses the terminal grammar and
+ * escaping rules of that string type.
+ *
+ * Which this function is being used inside of an xlat expansion, it uses xlat specific
+ * terminal grammar and escaping rules.
+ *
+ * This allows us to be smart about processing quotes within the expansions themselves.
+ *
+ * @param[in] ctx to allocate nodes in. Note: All nodes will be
+ * allocated in the same ctx. This is to allow
+ * manipulation by xlat instantiation functions
+ * later.
+ * @param[out] head Where to write the first child node.
+ * @param[out] flags where we store flags information for the parent.
+ * @param[in] in sbuff to parse.
+ * @param[in] brace true if we're inside a braced expansion, else false.
+ * @param[in] p_rules that control parsing.
+ * @param[in] ar_rules that control attribute reference and xlat function parsing.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int xlat_tokenize_literal(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags,
+ fr_sbuff_t *in, bool brace,
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *ar_rules)
{
- char const *p, *end;
- xlat_exp_t *node;
- char *start;
-
- if (!*in) return 0;
+ xlat_exp_t *node = NULL;
+ size_t len;
+ fr_sbuff_term_t expansions = FR_SBUFF_TERMS(
+ L("%C"),
+ L("%D"),
+ L("%G"),
+ L("%H"),
+ L("%I"),
+ L("%M"),
+ L("%S"),
+ L("%T"),
+ L("%Y"),
+ L("%c"),
+ L("%d"),
+ L("%e"),
+ L("%l"),
+ L("%m"),
+ L("%n"),
+ L("%s"),
+ L("%t"),
+ L("%v"),
+ L("%{")
+ );
+ fr_sbuff_term_t *tokens;
+ fr_cursor_t cursor;
+ fr_sbuff_unescape_rules_t const *escapes;
- XLAT_DEBUG("LITERAL <-- %s", in);
-
- node = talloc_zero(ctx, xlat_exp_t);
- node->fmt = start = talloc_bstrndup(node, in, inlen);
- node->len = 0;
- node->type = XLAT_LITERAL;
+ *head = NULL;
- p = in;
- end = in + inlen;
+ fr_cursor_init(&cursor, head);
+ escapes = p_rules ? p_rules->escapes : NULL;
+ tokens = p_rules && p_rules->terminals ?
+ fr_sbuff_terminals_amerge(NULL, p_rules->terminals, &expansions) : &expansions;
- while (p < end) {
- if (*p == '\\') {
- if (((p + 1) >= end) || !p[1]) {
- talloc_free(node);
- fr_strerror_printf("Invalid escape at end of string");
- return -(p - in);
- }
+ for (;;) {
+ char *str;
- p += 2;
- node->len += 2;
- continue;
- }
+ /*
+ * pre-allocate the node so we don't have to steal it later.
+ */
+ node = xlat_exp_alloc_null(ctx);
- if ((p[0] == '%') && (p + 1) >= end) goto invalid_variable;
+ /*
+ * Find the next token
+ */
+ len = fr_sbuff_out_aunescape_until(node, &str, in, SIZE_MAX, tokens, escapes);
/*
- * Process the expansion. Which should be at least %{0} in length
+ * It's a literal, create a literal node...
*/
- if ((p[0] == '%') && (p[1] == '{')) {
- ssize_t slen;
+ if (len > 0) {
+ xlat_exp_set_type(node, XLAT_LITERAL);
+ xlat_exp_set_name_buffer_shallow(node, str);
+
+ XLAT_DEBUG("LITERAL (%s)<-- %pV",
+ escapes ? escapes->name : "(none)",
+ fr_box_strvalue_len(str, talloc_array_length(str) - 1));
+ XLAT_HEXDUMP((uint8_t const *)str, talloc_array_length(str) - 1, " LITERAL ");
+ node->flags.needs_async = false; /* literals are always true */
+ fr_cursor_insert(&cursor, node);
+ node = NULL;
+ }
- XLAT_DEBUG("EXPANSION-2 <-- %s", node->fmt);
+ if (fr_sbuff_adv_past_str_literal(in, "%{")) {
+ if (len == 0) TALLOC_FREE(node); /* Free the empty node */
- slen = xlat_tokenize_expansion(node, &node->next, p, end - p, rules);
- if (slen <= 0) {
+ if (xlat_tokenize_expansion(ctx, &node, flags, in, ar_rules) < 0) {
+ error:
talloc_free(node);
- return slen - (p - in);
- }
- p += slen;
-
- fr_assert(node->next != NULL);
-
- /*
- * Short-circuit the recursive call.
- * This saves another function call and
- * memory allocation.
- */
- if (!*p || (p >= end)) break;
+ fr_cursor_head(&cursor);
+ fr_cursor_free_list(&cursor);
- /*
- * "foo %{User-Name} bar"
- * LITERAL "foo "
- * EXPANSION User-Name
- * LITERAL " bar"
- */
- slen = xlat_tokenize_literal(node->next, &(node->next->next), p, end - p, brace, rules);
- fr_assert(slen != 0);
- if (slen < 0) {
- talloc_free(node);
- return slen - (p - in);
+ /*
+ * Free our temporary array of terminals
+ */
+ if (tokens != &expansions) talloc_free(tokens);
+ return -1;
}
-
- brace = false; /* it was found above, or else the above code errored out */
- p += slen;
- break; /* stop processing the string */
+ fr_cursor_insert(&cursor, node);
+ node = NULL;
+ continue;
}
/*
- * Check for valid single-character expansions.
+ * %[a-z] - A one letter expansion
*/
- if (p[0] == '%') {
- ssize_t slen;
- xlat_exp_t *next;
-
- if (!p[1] || !strchr("%}cdlmnsetCDGHIMSTYv", p[1])) {
- invalid_variable:
- talloc_free(node);
- fr_strerror_printf("Invalid variable expansion");
- p++;
- return -(p - in);
+ if (fr_sbuff_next_if_char(in, '%') && fr_sbuff_is_alpha(in)) {
+ XLAT_DEBUG("ONE-LETTER <-- %pV",
+ fr_box_strvalue_len(str, talloc_array_length(str) - 1));
+
+ if (len == 0) {
+ talloc_free_children(node); /* re-use empty nodes */
+ } else {
+ node = xlat_exp_alloc_null(ctx);
}
- next = talloc_zero(node, xlat_exp_t);
- next->len = 1;
+ fr_sbuff_out_abstrncpy(node, &str, in, 1);
+ xlat_exp_set_type(node, XLAT_ONE_LETTER);
+ xlat_exp_set_name_buffer_shallow(node, str);
- switch (p[1]) {
- case '%':
- case '}':
- next->fmt = talloc_bstrndup(next, p + 1, 1);
-
- XLAT_DEBUG("LITERAL-ESCAPED <-- %s", next->fmt);
- next->type = XLAT_LITERAL;
- break;
+ node->flags.needs_async = false; /* literals are always true */
+ xlat_flags_merge(flags, &node->flags);
+ fr_cursor_insert(&cursor, node);
+ node = NULL;
+ continue;
+ }
- default:
- next->fmt = p + 1;
+ /*
+ * We were told to look for a brace, but we ran off of
+ * the end of the string before we found one.
+ */
+ if (brace) {
+ if (len == 0) TALLOC_FREE(node); /* Free the empty node */
- XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
- next->type = XLAT_ONE_LETTER;
- break;
+ if (!fr_sbuff_is_char(in, '}')) {
+ fr_strerror_printf("Missing closing brace");
+ goto error;
}
-
- node->next = next;
- p += 2;
-
- if (!*p || (p >= end)) break;
-
- /*
- * And recurse.
+ /*
+ * We're parsing the string *containing* the xlat
+ * expansions.
+ */
+ } else {
+ /* If we have an empty node, finish building it and
+ * emit it.
+ *
+ * We're about to return, and it's a useful
+ * indication to the caller that this wasn't a parse
+ * error but just an empty string.
*/
- slen = xlat_tokenize_literal(node->next, &(node->next->next), p, end - p, brace, rules);
- fr_assert(slen != 0);
- if (slen < 0) {
- talloc_free(node);
- return slen - (p - in);
- }
+ if (len == 0) {
+ /*
+ * This isn't the only node in the sequence
+ * don't emit an empty trailing literal.
+ */
+ if (*head) {
+ talloc_free(node);
+ break;
+ }
- brace = false; /* it was found above, or else the above code errored out */
- p += slen;
- break; /* stop processing the string */
- }
+ xlat_exp_set_type(node, XLAT_LITERAL);
+ xlat_exp_set_name_buffer_shallow(node, str);
- /*
- * If required, eat the brace.
- */
- if (brace && (*p == '}')) {
- brace = false;
- p++;
- break;
+ XLAT_DEBUG("LITERAL <-- (empty)");
+ node->flags.needs_async = false; /* literals are always true */
+ xlat_flags_merge(flags, &node->flags);
+ fr_cursor_insert(&cursor, node);
+ }
}
-
- p++;
- node->len++;
+ break;
}
/*
- * We were told to look for a brace, but we ran off of
- * the end of the string before we found one.
+ * Free our temporary array of terminals
*/
- if (brace) {
- fr_strerror_printf("Missing closing brace at end of string");
- talloc_free(node);
- return -(p - in);
- }
+ if (tokens != &expansions) talloc_free(tokens);
- /*
- * Squash zero-width literals
- */
- if (node->len <= 0) {
- (void) talloc_steal(ctx, node->next);
- *head = node->next;
- talloc_free(node);
- return p - in;
- }
-
- node->async_safe = true; /* literals are always true */
- *head = node;
-
- /*
- * Shrink the buffer to the right size
- */
- MEM(start = talloc_bstr_realloc(ctx, start, node->len));
- node->fmt = start;
-
- return p - in;
+ return 0;
}
+static fr_table_num_sorted_t const xlat_quote_table[] = {
+ { L("\""), T_DOUBLE_QUOTED_STRING }, /* Don't re-order, backslash throws off ordering */
+ { L("'"), T_SINGLE_QUOTED_STRING },
+ { L("`"), T_BACK_QUOTED_STRING }
+};
+static size_t xlat_quote_table_len = NUM_ELEMENTS(xlat_quote_table);
-/** skip xlat expansions, included embedded strings and nested xlats.
- *
- */
-static ssize_t skip_xlat(char const *start, char const *end)
-{
- char const *p = start + 2;
- ssize_t slen;
-
- while (p < end) {
- if (*p == '}') {
- p++;
- return p - start;
- }
-
- if ((*p == '"') || (*p == '\'')) {
- slen = fr_skip_string(p, end);
- if (slen < 0) return slen - (p - start);
-
- p += slen;
- continue;
- }
-
- if ((*p == '%') && ((p + 1) < end) && (p[1] == '{')) {
- slen = skip_xlat(p, end);
- if (slen < 0) return slen - (p - start);
- p += slen;
- continue;
- }
-
- p++;
- }
-
- return -(p - start);
-}
-
-/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
- *
- * @param[in] ctx to allocate dynamic buffers in.
- * @param[out] head the head of the xlat list / tree structure.
- * @param[in] in the format string to expand.
- * @param[in] inlen the length of the input string. If the string is \0 terminated, inlen may be -1.
- * @param[in] rules controlling how attribute references are parsed.
- * @return
- * - <=0 on error.
- * - >0 on success which is the number of characters parsed.
- */
-ssize_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_t **head, char const *in, size_t inlen,
- tmpl_rules_t const *rules)
-{
- char const *p, *end;
- ssize_t slen;
- xlat_exp_t *node, *my_head, **last;
- TALLOC_CTX *my_ctx;
- int count = 0;
-
- p = in;
- end = in + inlen;
-
- my_head = NULL;
- last = &my_head;
- my_ctx = ctx;
-
- while (p < end) {
- /*
- * Skip spaces between command-line arguments.
- */
- if (isspace((int) *p)) {
- while ((p < end) && isspace((int) *p)) p++;
-
- continue;
- }
-
- *last = node = talloc_zero(my_ctx, xlat_exp_t);
- node->fmt = NULL;
- node->len = 0;
- node->type = XLAT_CHILD;
- count++;
-
- /*
- * Back-tick quoted strings are forbidden.
- */
- if (*p == '`') {
- fr_strerror_printf("Unexpected `...` string");
- talloc_free(my_head);
- return -(p - in);
- }
-
- /*
- * Look for quoted strings.
- */
- if ((*p == '"') || (*p == '\'')) {
- slen = fr_skip_string(p, end);
- if (slen < 0) return slen - (p - in);
-
- } else {
- char const *q = p;
-
- /*
- * Bare words can contain xlat expansions.
- *
- * --foo=%{Bar}
- */
- while ((q < end) && !isspace((int) *q)) {
- /*
- * Things like: echo foo"bar"
- * are forbidden.
- *
- * The Unix shell typically allows this
- * kind of nonsense, but there's no reason
- * for us to do so.
- */
- if ((*q == '`') || (*q == '"') || (*q == '\'')) {
- fr_strerror_printf("Unexpected quoted string");
- talloc_free(my_head);
- return -(q - in);
- }
-
- if (*q == '%') {
- if (q[1] != '{') {
- slen = 2;
- } else {
- slen = skip_xlat(q, end);
- if (slen < 0) return slen - (q - in);
- }
-
- q += slen;
- continue;
- }
-
- q++;
- }
- slen = q - p;
- }
-
- /*
- * Tokenize this literal as a child.
- */
- slen = xlat_tokenize_literal(node, &node->child, p, slen, false, rules);
- if (slen <= 0) {
- talloc_free(my_head);
- return slen - (p - in);
- }
-
- /*
- * Arguments MUST be separated by spaces.
- */
- if (((p + slen) < end) && !isspace((int) p[slen])) {
- talloc_free(my_head);
- fr_strerror_printf("Unexpected text after argument");
- p += slen;
- return -(p - in);
- }
-
- /*
- * Remember how much of the current string we allocated.
- */
- node->fmt = talloc_bstrndup(node, p, slen);
- node->len = slen;
- node->async_safe = node->child->async_safe;
- p += slen;
-
- /*
- * Parent the next node from the previous one.
- * This lets us free everything in one swell foop
- * on error.
- */
- my_ctx = node;
- last = &(node->next);
- }
-
- if (!my_head) {
- fr_strerror_printf("Empty string is invalid");
- return -1;
- }
-
- *head = my_head;
- my_head->count = count;
- return p - in;
-}
-
-static void xlat_tokenize_debug(REQUEST *request, xlat_exp_t const *node)
+void xlat_debug(xlat_exp_t const *node)
{
fr_assert(node != NULL);
-
- RINDENT();
while (node) {
switch (node->type) {
case XLAT_LITERAL:
- RDEBUG3("literal --> %s", node->fmt);
+ INFO("literal --> %s", node->fmt);
break;
- case XLAT_CHILD:
- RDEBUG3("child --> %s", node->fmt);
- RDEBUG3("{");
- RINDENT();
- xlat_tokenize_debug(request, node->child);
- REXDENT();
- RDEBUG3("}");
+ case XLAT_GROUP:
+ INFO("child --> %s", node->fmt);
+ INFO("{");
+ xlat_debug(node->child);
+ INFO("}");
break;
case XLAT_ONE_LETTER:
- RDEBUG3("percent --> %c", node->fmt[0]);
+ INFO("percent --> %c", node->fmt[0]);
break;
case XLAT_ATTRIBUTE:
fr_assert(tmpl_da(node->attr) != NULL);
- RDEBUG3("attribute --> %s", tmpl_da(node->attr)->name);
+ INFO("attribute --> %s", tmpl_da(node->attr)->name);
fr_assert(node->child == NULL);
- if ((tmpl_tag(node->attr) != TAG_ANY) || (tmpl_num(node->attr) != NUM_ANY)) {
- RDEBUG3("{");
-
- RINDENT();
- RDEBUG3("ref %d", tmpl_request(node->attr));
- RDEBUG3("list %d", tmpl_list(node->attr));
-
- if (tmpl_tag(node->attr) != TAG_ANY) {
- RDEBUG3("tag %d", tmpl_tag(node->attr));
- }
+ if (tmpl_num(node->attr) != NUM_ANY) {
+ INFO("{");
+ INFO("ref %d", tmpl_request(node->attr));
+ INFO("list %d", tmpl_list(node->attr));
if (tmpl_num(node->attr) != NUM_ANY) {
if (tmpl_num(node->attr) == NUM_COUNT) {
- RDEBUG3("[#]");
+ INFO("[#]");
} else if (tmpl_num(node->attr) == NUM_ALL) {
- RDEBUG3("[*]");
+ INFO("[*]");
} else {
- RDEBUG3("[%d]", tmpl_num(node->attr));
+ INFO("[%d]", tmpl_num(node->attr));
}
}
- REXDENT();
- RDEBUG3("}");
+ INFO("}");
}
break;
case XLAT_VIRTUAL:
fr_assert(node->fmt != NULL);
- RDEBUG3("virtual --> %s", node->fmt);
+ INFO("virtual --> %s", node->fmt);
+ break;
+
+ case XLAT_VIRTUAL_UNRESOLVED:
+ fr_assert(node->fmt != NULL);
+ INFO("virtual-unresolved --> %s", node->fmt);
break;
case XLAT_FUNC:
- fr_assert(node->xlat != NULL);
- RDEBUG3("xlat --> %s", node->xlat->name);
+ fr_assert(node->call.func != NULL);
+ INFO("xlat --> %s", node->call.func->name);
+ if (node->child) {
+ INFO("{");
+ xlat_debug(node->child);
+ INFO("}");
+ }
+ break;
+
+ case XLAT_FUNC_UNRESOLVED:
+ INFO("xlat-unresolved --> %s", node->fmt);
if (node->child) {
- RDEBUG3("{");
- xlat_tokenize_debug(request, node->child);
- RDEBUG3("}");
+ INFO("{");
+ xlat_debug(node->child);
+ INFO("}");
}
break;
#ifdef HAVE_REGEX
case XLAT_REGEX:
- RDEBUG3("regex-var --> %d", node->regex_index);
+ INFO("regex-var --> %d", node->regex_index);
break;
#endif
case XLAT_ALTERNATE:
DEBUG("XLAT-IF {");
- xlat_tokenize_debug(request, node->child);
+ xlat_debug(node->child);
DEBUG("}");
DEBUG("XLAT-ELSE {");
- xlat_tokenize_debug(request, node->alternate);
+ xlat_debug(node->alternate);
DEBUG("}");
break;
+
+ case XLAT_INVALID:
+ DEBUG("XLAT-INVALID");
+ break;
}
node = node->next;
}
- REXDENT();
}
-size_t xlat_snprint(char *out, size_t outlen, xlat_exp_t const *node)
+/** Reconstitute an xlat expression from its constituent nodes
+ *
+ * @param[in] out Where to write the output string.
+ * @param[in] head First node to print.
+ * @param[in] e_rules Specifying how to escape literal values.
+ */
+ssize_t xlat_print(fr_sbuff_t *out, xlat_exp_t const *head, fr_sbuff_escape_rules_t const *e_rules)
{
- char *p, *end;
-
- if (!node || (outlen <= 2)) {
- *out = '\0';
- return 0;
- }
-
- p = out;
- end = out + outlen;
+ ssize_t slen;
+ size_t at_in = fr_sbuff_used_total(out);
+ xlat_exp_t const *node = head;
-#define CHECK_SPACE(_p, _end) if (_p >= _end) goto oob
+ if (!node) return 0;
while (node) {
- if ((node->type == XLAT_LITERAL) || (node->type == XLAT_CHILD)) {
- p += strlcpy(p, node->fmt, end - p);
- CHECK_SPACE(p, end);
+ switch (node->type) {
+ case XLAT_GROUP:
+ if (node->quote != T_BARE_WORD) FR_SBUFF_IN_CHAR_RETURN(out, fr_token_quote[node->quote]);
+ xlat_print(out, node->child, fr_value_escape_by_quote[node->quote]);
+ if (node->quote != T_BARE_WORD) FR_SBUFF_IN_CHAR_RETURN(out, fr_token_quote[node->quote]);
+ if (node->next) FR_SBUFF_IN_CHAR_RETURN(out, ' '); /* Add ' ' between args */
goto next;
- }
- if (node->type == XLAT_ONE_LETTER) {
- *(p++) = '%';
- if (p >= end) {
- oob:
- out[outlen - 1] = '\0';
- return outlen + 1;
- }
+ case XLAT_LITERAL:
+ FR_SBUFF_IN_ESCAPE_BUFFER_RETURN(out, node->fmt, e_rules);
+ goto next;
- *(p++) = node->fmt[0];
- CHECK_SPACE(p, end);
+ case XLAT_ONE_LETTER:
+ FR_SBUFF_IN_CHAR_RETURN(out, '%', node->fmt[0]);
goto next;
- }
- *(p++) = '%';
- CHECK_SPACE(p, end);
- *(p++) = '{';
- CHECK_SPACE(p, end);
+ default:
+ break;
+ }
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, "%{");
switch (node->type) {
case XLAT_ATTRIBUTE:
- {
- size_t need;
-
- p += tmpl_snprint_attr_str(&need, p, end - p, node->attr);
- if (need > 0) goto oob;
- }
+ slen = tmpl_attr_print(out, node->attr, TMPL_ATTR_REF_PREFIX_NO);
+ if (slen < 0) {
+ error:
+ return slen;
+ }
break;
#ifdef HAVE_REGEX
case XLAT_REGEX:
- p += snprintf(p, end - p, "%i", node->regex_index);
- CHECK_SPACE(p, end);
+ FR_SBUFF_IN_SPRINTF_RETURN(out, "%i", node->regex_index);
break;
#endif
case XLAT_VIRTUAL:
- p += strlcpy(p, node->fmt, end - p);
- CHECK_SPACE(p, end);
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
+ break;
+
+ case XLAT_VIRTUAL_UNRESOLVED:
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->fmt);
break;
case XLAT_FUNC:
- p += strlcpy(p, node->xlat->name, end - p);
- CHECK_SPACE(p, end);
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->call.func->name);
+ FR_SBUFF_IN_CHAR_RETURN(out, ':');
+
+ if (node->child) {
+ slen = xlat_print(out, node->child, &xlat_escape);
+ if (slen < 0) goto error;
+ }
+ break;
- *(p++) = ':';
- CHECK_SPACE(p, end);
+ case XLAT_FUNC_UNRESOLVED:
+ FR_SBUFF_IN_BSTRCPY_BUFFER_RETURN(out, node->fmt);
+ FR_SBUFF_IN_CHAR_RETURN(out, ':');
if (node->child) {
- p += xlat_snprint(p, end - p, node->child);
- CHECK_SPACE(p, end);
+ slen = xlat_print(out, node->child, &xlat_escape);
+ if (slen < 0) goto error;
}
break;
case XLAT_ALTERNATE:
- p += xlat_snprint(p, end - p, node->child);
- CHECK_SPACE(p, end);
+ slen = xlat_print(out, node->child, &xlat_escape);
+ if (slen < 0) goto error;
- *(p++) = ':';
- CHECK_SPACE(p, end);
- *(p++) = '-';
- CHECK_SPACE(p, end);
+ FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, ":-");
+ slen = xlat_print(out, node->alternate, &xlat_escape);
+ if (slen < 0) goto error;
+ break;
- p += xlat_snprint(p, end - p, node->alternate);
- CHECK_SPACE(p, end);
+ fr_assert_fail(NULL);
break;
- default:
+ case XLAT_INVALID:
+ case XLAT_LITERAL:
+ case XLAT_ONE_LETTER:
+ case XLAT_GROUP:
fr_assert_fail(NULL);
break;
}
-
- *(p++) = '}';
- CHECK_SPACE(p, end);
-
+ FR_SBUFF_IN_CHAR_RETURN(out, '}');
next:
node = node->next;
}
- *p = '\0';
-
- return p - out;
+ return fr_sbuff_used_total(out) - at_in;
}
/** Tokenize an xlat expansion at runtime
*
* @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
*/
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;
+}
*/
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.
default:
type = FR_TYPE_OCTETS;
break;
- }
+ }
/*
* Initialize the rest of the fields.
}
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
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)) {
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)) {
fr_dict_attr_t *da;
fr_dict_attr_flags_t flags = {
.is_unknown = true,
- .is_raw = true,
};
if (!fr_cond_assert(parent)) {
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__);
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));
*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
return dict;
}
-
/** Decrement the reference count on a previously loaded dictionary
*
* @param[in] dict to free.
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
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 \
}
vp->op = T_OP_EQ;
- vp->tag = TAG_ANY;
vp->type = VT_NONE;
talloc_set_destructor(vp, _fr_pair_free);
if (!n) return NULL;
n->op = vp->op;
- n->tag = vp->tag;
n->next = NULL;
n->type = vp->type;
/** 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;
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;
}
*
* @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;
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;
/** 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;
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;
}
*
* @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);
}
* @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.
* 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;
/*
* @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;
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 {
* - 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
* - 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;
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
* - 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;
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;
}
/*
* 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"
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;
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;
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);
*
* @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);
*
* @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);
* 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;
* @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;
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
}
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;
return 0;
}
-char *fr_pair_type_asprint(TALLOC_CTX *ctx, fr_type_t type)
-{
- switch (type) {
- case FR_TYPE_STRING :
- return talloc_typed_strdup(ctx, "_");
-
- case FR_TYPE_UINT64:
- case FR_TYPE_SIZE:
- case FR_TYPE_INT32:
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_DATE:
- return talloc_typed_strdup(ctx, "0");
-
- case FR_TYPE_IPV4_ADDR:
- return talloc_typed_strdup(ctx, "?.?.?.?");
-
- case FR_TYPE_IPV4_PREFIX:
- return talloc_typed_strdup(ctx, "?.?.?.?/?");
-
- case FR_TYPE_IPV6_ADDR:
- return talloc_typed_strdup(ctx, "[:?:]");
-
- case FR_TYPE_IPV6_PREFIX:
- return talloc_typed_strdup(ctx, "[:?:]/?");
-
- case FR_TYPE_OCTETS:
- return talloc_typed_strdup(ctx, "??");
-
- case FR_TYPE_ETHERNET:
- return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
-
- case FR_TYPE_ABINARY:
- return talloc_typed_strdup(ctx, "??");
-
- case FR_TYPE_GROUP:
- return talloc_typed_strdup(ctx, "{ ? }");
-
- default :
- break;
- }
-
- return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
-}
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
- <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param out Where to write the string.
- * @param outlen Length of output buffer.
- * @param vp to print.
- * @return
- * - Length of data written to out.
- * - value >= outlen on truncation.
- */
-size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
-{
- char const *token = NULL;
- size_t len, freespace = outlen;
-
- if (!out) return 0;
-
- *out = '\0';
- if (!vp || !vp->da) return 0;
-
- VP_VERIFY(vp);
-
- if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
- token = fr_tokens[vp->op];
- } else {
- token = "<INVALID-TOKEN>";
- }
-
- if (vp->da->flags.has_tag && (vp->tag != 0) && (vp->tag != TAG_ANY)) {
- len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
- } else {
- len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
- }
-
- if (is_truncated(len, freespace)) return len;
- out += len;
- freespace -= len;
-
- len = fr_pair_value_snprint(out, freespace, vp, '"');
- if (is_truncated(len, freespace)) return (outlen - freespace) + len;
- freespace -= len;
-
- return (outlen - freespace);
-}
-
-/** Print one attribute and value to FP
- *
- * Complete string with '\\t' and '\\n' is written to buffer before printing to
- * avoid issues when running with multiple threads.
- *
- * @param fp to output to.
- * @param vp to print.
- */
-void fr_pair_fprint(FILE *fp, VALUE_PAIR const *vp)
-{
- char buf[1024];
- char *p = buf;
- size_t len;
-
- if (!fp) return;
- VP_VERIFY(vp);
-
- *p++ = '\t';
- len = fr_pair_snprint(p, sizeof(buf) - 1, vp);
- if (!len) {
- return;
- }
- p += len;
-
- /*
- * Deal with truncation gracefully
- */
- if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
- p = buf + (sizeof(buf) - 2);
- }
-
- *p++ = '\n';
- *p = '\0';
-
- fputs(buf, fp);
-}
-
-
-/** Print a list of attributes and enumv
- *
- * @param[in] log to output to.
- * @param[in] vp to print.
- * @param[in] file where the message originated
- * @param[in] line where the message originated
- */
-void _fr_pair_list_log(fr_log_t const *log, VALUE_PAIR const *vp, char const *file, int line)
-{
- VALUE_PAIR *our_vp;
- fr_cursor_t cursor;
-
- memcpy(&our_vp, &vp, sizeof(vp)); /* const work-arounds */
-
- for (vp = fr_cursor_init(&cursor, &our_vp); vp; vp = fr_cursor_next(&cursor)) {
- fr_log(log, L_DBG, file, line, "\t%pP", vp);
- }
-}
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
- <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param ctx to allocate string in.
- * @param vp to print.
- * @param[in] quote the quotation character
- * @return a talloced buffer with the attribute operator and value.
- */
-char *fr_pair_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
-{
- char const *token = NULL;
- char *str, *value;
-
- if (!vp || !vp->da) return 0;
-
- VP_VERIFY(vp);
-
- if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
- token = fr_tokens[vp->op];
- } else {
- token = "<INVALID-TOKEN>";
- }
-
- value = fr_pair_value_asprint(ctx, vp, quote);
-
- if (vp->da->flags.has_tag) {
- if (quote && (vp->vp_type == FR_TYPE_STRING)) {
- str = talloc_typed_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
- } else {
- str = talloc_typed_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
- }
- } else {
- if (quote && (vp->vp_type == FR_TYPE_STRING)) {
- str = talloc_typed_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
- } else {
- str = talloc_typed_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
- }
- }
-
- talloc_free(value);
-
- return str;
-}
-
#ifdef WITH_VERIFY_PTR
/*
* Verify a VALUE_PAIR
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.
};
#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);
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);
/* 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);
* - -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);
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
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
*
* @{
*/
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 {
* @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;
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;
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;
}
}
* @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;
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);
*
* @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;
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);
*
* @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;
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);
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);
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;
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;
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:
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");
} 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;
* @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.
*
* 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);
* 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) {
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;
}
*
* @copyright 2020 The FreeRADIUS server project
*/
+#include <freeradius-devel/util/pair.h>
+#include <freeradius-devel/util/talloc.h>
-char *fr_pair_type_asprint(TALLOC_CTX *ctx, fr_type_t type)
+/** Print the value of an attribute to a string
+ *
+ * @param[in] out Where to write the string.
+ * @param[in] vp to print.
+ * @param[in] quote Char to add before and after printed value,
+ * if 0 no char will be added, if < 0 raw string
+ * will be added.
+ * @return
+ * - >= 0 length of data written to out.
+ * - <0 the number of bytes we would have needed to write
+ * the complete string to out.
+ */
+ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, VALUE_PAIR const *vp, fr_token_t quote)
{
- switch (type) {
- case FR_TYPE_STRING :
- return talloc_typed_strdup(ctx, "_");
-
- case FR_TYPE_UINT64:
- case FR_TYPE_SIZE:
- case FR_TYPE_INT32:
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_DATE:
- return talloc_typed_strdup(ctx, "0");
+ fr_sbuff_t our_out;
+ VALUE_PAIR *child, *head;
+ fr_cursor_t cursor;
- case FR_TYPE_IPV4_ADDR:
- return talloc_typed_strdup(ctx, "?.?.?.?");
+ VP_VERIFY(vp);
- case FR_TYPE_IPV4_PREFIX:
- return talloc_typed_strdup(ctx, "?.?.?.?/?");
+ /*
+ * Legacy crap that needs to be removed
+ */
+ if (vp->type == VT_XLAT) {
+ char const *quote_str = fr_table_str_by_value(fr_token_quotes_table, quote, "");
- case FR_TYPE_IPV6_ADDR:
- return talloc_typed_strdup(ctx, "[:?:]");
+ return fr_sbuff_in_sprintf(out, "%s%s%s", quote_str, vp->xlat, quote_str);
+ }
- case FR_TYPE_IPV6_PREFIX:
- return talloc_typed_strdup(ctx, "[:?:]/?");
+ /*
+ * For simple types just print the box
+ */
+ if (vp->da->type != FR_TYPE_GROUP) {
+ return fr_value_box_print_quoted(out, &vp->data, quote);
+ }
- case FR_TYPE_OCTETS:
- return talloc_typed_strdup(ctx, "??");
+ /*
+ * Serialize all child VPs as full quoted
+ * <pair> = ["]<child>["]
+ */
+ our_out = FR_SBUFF_NO_ADVANCE(out);
- case FR_TYPE_ETHERNET:
- return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
+ head = vp->vp_ptr;
+ if (!fr_cond_assert(head != NULL)) return 0;
- case FR_TYPE_ABINARY:
- return talloc_typed_strdup(ctx, "??");
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, '{', ' ');
+ for (child = fr_cursor_init(&cursor, &head);
+ child != NULL;
+ child = fr_cursor_next(&cursor)) {
+ VP_VERIFY(child);
- case FR_TYPE_GROUP:
- return talloc_typed_strdup(ctx, "{ ? }");
+ FR_SBUFF_RETURN(fr_pair_print, &our_out, child);
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, ',', ' ');
+ }
- default :
- break;
+ if (fr_sbuff_used(&our_out)) {
+ fr_sbuff_set(&our_out, fr_sbuff_current(&our_out) - 2);
+ *fr_sbuff_current(&our_out) = '\0';
}
- return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
+ FR_SBUFF_IN_CHAR_RETURN(&our_out, ' ', '}');
+
+ return fr_sbuff_set(out, &our_out);
}
/** Print one attribute and value to a string
*
* Print a VALUE_PAIR in the format:
@verbatim
- <attribute_name>[:tag] <op> <value>
+ <attribute_name>[:tag] <op> [q]<value>[q]
@endverbatim
* to a string.
*
- * @param out Where to write the string.
- * @param outlen Length of output buffer.
- * @param vp to print.
+ * @param[in] out Where to write the string.
+ * @param[in] vp to print.
* @return
* - Length of data written to out.
* - value >= outlen on truncation.
*/
-size_t fr_pair_snprint(char *out, size_t outlen, VALUE_PAIR const *vp)
+ssize_t fr_pair_print(fr_sbuff_t *out, VALUE_PAIR const *vp)
{
char const *token = NULL;
- size_t len, freespace = outlen;
+ fr_sbuff_t our_out = FR_SBUFF_NO_ADVANCE(out);
if (!out) return 0;
- *out = '\0';
if (!vp || !vp->da) return 0;
VP_VERIFY(vp);
token = "<INVALID-TOKEN>";
}
- if (vp->da->flags.has_tag && (vp->tag != 0) && (vp->tag != TAG_ANY)) {
- len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
- } else {
- len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
- }
-
- if (is_truncated(len, freespace)) return len;
- out += len;
- freespace -= len;
-
- len = fr_pair_value_snprint(out, freespace, vp, '"');
- if (is_truncated(len, freespace)) return (outlen - freespace) + len;
- freespace -= len;
+ FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%s %s ", vp->da->name, token);
+ FR_SBUFF_RETURN(fr_pair_print_value_quoted, &our_out, vp, T_DOUBLE_QUOTED_STRING);
- return (outlen - freespace);
+ return fr_sbuff_set(out, &our_out);
}
/** Print one attribute and value to FP
*/
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);
}
fr_log(log, L_DBG, file, line, "\t%pP", vp);
}
}
-
-/** Print one attribute and value to a string
- *
- * Print a VALUE_PAIR in the format:
-@verbatim
- <attribute_name>[:tag] <op> <value>
-@endverbatim
- * to a string.
- *
- * @param ctx to allocate string in.
- * @param vp to print.
- * @param[in] quote the quotation character
- * @return a talloced buffer with the attribute operator and value.
- */
-char *fr_pair_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
-{
- char const *token = NULL;
- char *str, *value;
-
- if (!vp || !vp->da) return 0;
-
- VP_VERIFY(vp);
-
- if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
- token = fr_tokens[vp->op];
- } else {
- token = "<INVALID-TOKEN>";
- }
-
- value = fr_pair_value_asprint(ctx, vp, quote);
-
- if (vp->da->flags.has_tag) {
- if (quote && (vp->vp_type == FR_TYPE_STRING)) {
- str = talloc_typed_asprintf(ctx, "%s:%d %s %c%s%c", vp->da->name, vp->tag, token, quote, value, quote);
- } else {
- str = talloc_typed_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value);
- }
- } else {
- if (quote && (vp->vp_type == FR_TYPE_STRING)) {
- str = talloc_typed_asprintf(ctx, "%s %s %c%s%c", vp->da->name, token, quote, value, quote);
- } else {
- str = talloc_typed_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
- }
- }
-
- talloc_free(value);
-
- return str;
-}
-
-/** Print the attribute portion of a pair (only)
- *
- * @param[out] out Where to write the output attribute.
- * @param[in] end of the output buffer.
- * @param[in] vp to print the attribute name for.
- * @return
- * - >= 0 number of bytes written to out on success.
- * - < 0 number of bytes we need to write out the value.
- */
-static inline ssize_t fr_pair_snprint_attr(char * const out, char const * const end, VALUE_PAIR const *vp)
-{
- char *p = out;
- size_t len = strlen(vp->da->name);
-
- RETURN_SLEN_IF_NO_SPACE(len, p, end);
- memcpy(p, vp->da->name, len);
- p += len;
-
- if (vp->tag) {
- size_t ret;
-
- RETURN_SLEN_IF_NO_SPACE(1, p, end);
- *p++ = ':';
-
- ret = snprintf(p, end - p, "%u", vp->tag);
- RETURN_SLEN_IF_TRUNCATED(ret, p, end);
-
- p += ret;
- }
-
- return p - out;
-}
-
-/** Print a leaf attribute pair i.e. <attr> <op> <value>
- *
- * @param[out] out Where to write the output attribute.
- * @param[in] end of the output buffer.
- * @param[in] vp to print.
- * @param[in] str_quote to use when printing quoted string values.
- */
-static inline ssize_t fr_pair_snprintf_leaf(char * const out, char const * const end,
- VALUE_PAIR const *vp, char str_quote)
-{
- char *p = out;
-
- if ((vp->op > T_INVALID) && (vp->op < T_TOKEN_LAST)) {
- token = fr_tokens[vp->op];
- } else {
- token = "<INVALID-TOKEN>";
- }
-
- fr_value_box_snprint(out, )
-}
-
-
* @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.
*/
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
* 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);
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;
}
VP_VERIFY(in);
- subst = fr_pair_asprint(NULL, in, '"');
+ fr_pair_aprint(NULL, &subst, in);
}
goto do_splice;
* @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
#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');
DO_REGEX_FLAG(extended, 'x');
#undef DO_REGEX_FLAG
- return p - out;
+ return fr_sbuff_set(sbuff, &our_sbuff);
}
#endif
#include <freeradius-devel/build.h>
#include <freeradius-devel/missing.h>
+#include <freeradius-devel/util/sbuff.h>
#include <stdbool.h>
#include <stdint.h>
#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);
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.
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
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);
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);
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);
*
*/
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.
*
*/
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.
[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)
*
[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
*
* @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.
/** 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, ...) \
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 }
};
*
* - 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
};
fr_sbuff_unescape_rules_t fr_value_unescape_double = {
+ .name = "double",
.chr = '\\',
.subs = {
['"'] = '"', /* Quoting char */
};
fr_sbuff_unescape_rules_t fr_value_unescape_single = {
+ .name = "single",
.chr = '\\',
.subs = {
['\''] = '\'', /* Quoting char */
};
fr_sbuff_unescape_rules_t fr_value_unescape_solidus = {
+ .name = "solidus",
.chr = '\\',
.subs = {
['%'] = '%', /* xlat expansions */
['/'] = '/', /* Quoting char */
- ['\\'] = '\\',
['a'] = '\a',
['b'] = '\b',
['e'] = '\\',
['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 */
};
fr_sbuff_escape_rules_t fr_value_escape_double = {
+ .name = "double",
.chr = '\\',
.subs = {
['"'] = '"', /* Quoting char */
['\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 */
};
fr_sbuff_escape_rules_t fr_value_escape_solidus = {
+ .name = "solidus",
.chr = '\\',
.subs = {
['%'] = '%', /* xlat expansions */
['\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 */
['\v'] = 'v'
},
.esc = {
- SBUFF_CHAR_UNPRINTABLES_LOW
+ SBUFF_CHAR_UNPRINTABLES_LOW,
+ SBUFF_CHAR_UNPRINTABLES_EXTENDED
},
.do_utf8 = true,
.do_oct = true
{
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);
* @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
* @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.
dst->tainted = tainted;
/*
- * Fixup enumv
+ * Fixup enumvs
*/
dst->enumv = dst_enumv;
dst->next = NULL;
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;
/*
* 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;
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,
} 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;
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
* @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;
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;
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);
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;
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;
* - 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;
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) */
-}
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);
/*
* 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
*
* 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) {
/*
* 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) {
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++;
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);
}
}
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);
}
* 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) {
* 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]);
* 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) {
{
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");
/*
* 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);
/*
* 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));
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";
* Get the correct username based on the configured value
*/
if (!inst->log_stripped_names) {
- username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+ username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
} else {
- username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name, TAG_ANY);
- if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name, TAG_ANY);
+ username = fr_pair_find_by_da(request->packet->vps, attr_stripped_user_name);
+ if (!username) username = fr_pair_find_by_da(request->packet->vps, attr_user_name);
}
/*
* Clean up the password
*/
if (inst->log_auth_badpass || inst->log_auth_goodpass) {
- password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
+ password = fr_pair_find_by_da(request->packet->vps, attr_user_password);
if (!password) {
VALUE_PAIR *auth_type;
- auth_type = fr_pair_find_by_da(request->control, attr_auth_type, TAG_ANY);
+ auth_type = fr_pair_find_by_da(request->control, attr_auth_type);
if (auth_type) {
snprintf(password_buff, sizeof(password_buff), "<via Auth-Type = %s>",
fr_dict_enum_name_by_value(auth_type->da, &auth_type->data));
} else {
password_str = "<no User-Password attribute>";
}
- } else if (fr_pair_find_by_da(request->packet->vps, attr_chap_password, TAG_ANY)) {
+ } else if (fr_pair_find_by_da(request->packet->vps, attr_chap_password)) {
password_str = "<CHAP-Password>";
}
}
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");
* 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;
/*
* 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;
/*
* 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;
/*
* 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 {
* "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));
* 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 {
* 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;
/*
* 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:
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;
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;
/*
/*
* 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));
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;
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 "
}
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),
/*
* 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");
}
/*
* 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);
/*
* 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");
}
* 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;
* 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;
}
/*
* 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");
}
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;
/*
* Encode the entry created date
*/
- tmpl_init(&created_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
+ tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
fr_value_box_init(&created_value.data.literal, FR_TYPE_DATE, NULL, true);
tmpl_value(&created_value)->vb_date = c->created;
* Although Redis objects expire on their own, we still need this
* to ignore entries that were created before the last epoch.
*/
- tmpl_init(&expires_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
+ tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6);
fr_value_box_init(&expires_value.data.literal, FR_TYPE_DATE, NULL, true);
tmpl_value(&expires_value)->vb_date = c->expires;
expires.next = c->maps; /* Head of the list */
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();
}
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:
* 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;
}
/*
* 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);
* 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");
/*
* 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;
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
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;
}
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;
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,
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');
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;
}
* 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.
* 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);
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!");
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;
/* 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);
}
}
/* 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 {
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 */
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 */
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));
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));
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;
* 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");
/*
* 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;
/*
* 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;
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;
* 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)) {
/*
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;
/*
* 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;
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;
/*
* 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;
/*
* 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;
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;
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;
/*
* 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;
* 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
/*
* 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;
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;
}
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
* 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);
*/
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;
}
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;
* 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 {
* 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.
*/
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;
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;
* 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 {
* 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
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) {
* 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 {
* 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 {
/*
* 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) &&
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;
{
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;
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.
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;
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;
}
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");
/*
* 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;
* 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;
}
- 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));
/*
* 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);
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
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;
/*
}
/* 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");
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
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");
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");
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
/*
* 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
* 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;
*/
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");
*/
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];
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;
}
* 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;
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;
}
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;
/*
case FR_TYPE_ABINARY:
{
char buff[128];
- size_t len;
+ size_t slen;
- len = fr_pair_value_snprint(buff, sizeof(buff), vp, '\0');
- if (is_truncated(len, sizeof(buff))) {
+ slen = fr_pair_print_value_quoted(&FR_SBUFF_OUT(buff, sizeof(buff)), vp, T_BARE_WORD);
+ if (slen < 0) {
REDEBUG("Cannot convert %s to Lua type, insufficient buffer space",
fr_table_str_by_value(fr_value_box_type_table, vp->vp_type, "<INVALID>"));
return -1;
}
- lua_pushlstring(L, buff, len);
+ lua_pushlstring(L, buff, (size_t)slen);
}
break;
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);
/*
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,
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;
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;
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;
{
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");
* 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;
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;
* 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 {
* 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;
* 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;
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;
}
* 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;
/*
#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;
#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",
* 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;
}
* 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);
* 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;
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);
/*
* 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,
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
* 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;
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
* 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);
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;
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;
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);
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);
/*
* 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;
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;
}
fr_cursor_t cursor;
RINDENT();
- fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
+ fr_pair_list_sort(vps, fr_pair_cmp_by_da);
for (vp = fr_cursor_init(&cursor, vps);
vp;
vp = fr_cursor_next(&cursor)) {
VALUE_PAIR *next;
-
char const *name;
- char namebuf[256];
-
- /*
- * Tagged attributes are added to the hash with name
- * <attribute>:<tag>, others just use the normal attribute
- * name as the key.
- */
- if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
- snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
- name = namebuf;
- } else {
- name = vp->da->name;
- }
+ name = vp->da->name;
/*
* We've sorted by type, then tag, so attributes of the
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;
}
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 {
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) {
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;
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);
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);
}
* 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);
* 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);
}
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;
* 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;
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;
* 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);
*
* @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);
}
/*
* 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;
}
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;
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; \
.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
.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) {
.rhs = &expiry_rhs
};
- tmpl_init(&expiry_rhs, TMPL_TYPE_DATA, "", 0, T_DOUBLE_QUOTED_STRING);
+ tmpl_init_shallow(&expiry_rhs, TMPL_TYPE_DATA, T_DOUBLE_QUOTED_STRING, "", 0);
if (reply->element[3]->type != REDIS_REPLY_INTEGER) {
REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])",
fr_table_str_by_value(redis_reply_types, reply->element[3]->type, "<UNKNOWN>"));
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());
};
- 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) {
/*
* 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;
* 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);
}
* 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;
}
} 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;
* 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);
}
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;
// (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
char *escaped; /* Pointer to current URL escaped data */
size_t len = 0;
+ ssize_t slen;
size_t freespace = (size * nmemb) - 1;
/* Allow manual chunking */
/*
* 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;
}
vp->op = flags->op;
- vp->tag = flags->tag;
return vp;
}
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;
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
/*
* 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;
}
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
/*
* 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]) {
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
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;
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;
}
* 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 :
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;
}
/*
* 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);
* 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);
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;
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 {
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;
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;
/*
* 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) {
* 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;
/*
* 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;
}
* 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);
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;
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;
}
/*
* 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;
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;
/*
* 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");
* 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");
/*
* 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;
/*
* 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;
/*
* 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;
*
* 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);
*
* 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) {
/*
* 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");
}
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();
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);
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;
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
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;
/*
* 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;
/*
* 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
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;
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
*/
/* 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;
}
#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 {
}
/* 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;
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;
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;
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);
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);
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 {
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 {
/*
* 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);
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.
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 {
*/
/* 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);
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.
}
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;
}
* 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) {
/*
/*
* 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.
* 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");
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();
/* 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));
}
* 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. */
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;
}
/*
* 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 {
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.
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;
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;
}
fr_strerror_printf("%s: Internal sanity check %d", __FUNCTION__, __LINE__);
return -1;
}
- tag = TAG_NONE;
+ //tag = 0;
#ifndef NDEBUG
/*
* Fix for Coverity.
*/
vp = fr_pair_afrom_da(ctx, parent);
if (!vp) return -1;
- vp->tag = tag;
+// vp->tag = tag;
switch (parent->type) {
case FR_TYPE_OCTETS:
#include <freeradius-devel/io/test_point.h>
#include "attrs.h"
+#define TAG_VALID(x) ((x) > 0 && (x) < 0x20)
+#define TAG_VALID_ZERO(x) ((x) >= 0 && (x) < 0x20)
+
static ssize_t encode_value(uint8_t *out, size_t outlen,
fr_da_stack_t *da_stack, unsigned int depth,
fr_cursor_t *cursor, void *encoder_ctx);
* 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 {
* 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;
}
*
* 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);
* 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;
}
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",
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)) {
{ NULL }
};
-
char const *fr_tacacs_packet_codes[] = {
[FR_PACKET_TYPE_VALUE_AUTHENTICATION_START] = "Authentication-Start",
[FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE] = "Authentication-Continue",
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;
* 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)
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();
}
}
/* 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 */
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);
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
+};
# Set CHAP-Password
#
update request {
- &CHAP-Password := "%{chap_password:%{request:CHAP-Password}}"
+ &CHAP-Password := "%{chap_password:%{request.CHAP-Password}}"
}
# Set CHAP-Password
#
update request {
- &CHAP-Password := "%{chap_password:%{request:CHAP-Password}}"
+ &CHAP-Password := "%{chap_password:%{request.CHAP-Password}}"
}
}
update {
- &request:Tmp-String-1 := "%{concat:, %{request:[*]}}"
+ &request:Tmp-String-1 := "%{concat:, %{request[*]}}"
&request:Tmp-String-2 := "%{concat:, %{Tmp-String-0[*]}}"
}
&Tmp-String-7 := 'Œ=C4=8Cÿ'
}
-if (<string>"%{escape:%{request:Tmp-String-0}}" != &Tmp-String-0) {
+if (<string>"%{escape:%{Tmp-String-0}}" != &Tmp-String-0) {
test_fail
}
-if (<string>"%{escape:%{request:Tmp-String-1}}" != &Tmp-String-3) {
+if (<string>"%{escape:%{Tmp-String-1}}" != &Tmp-String-3) {
test_fail
}
-if (<string>"%{escape:%{request:Tmp-String-2}}" != &Tmp-String-4) {
+if (<string>"%{escape:%{Tmp-String-2}}" != &Tmp-String-4) {
test_fail
}
-if (<string>"%{unescape:%{request:Tmp-String-0}}" != &Tmp-String-0) {
+if (<string>"%{unescape:%{Tmp-String-0}}" != &Tmp-String-0) {
test_fail
}
-if (<string>"%{unescape:%{request:Tmp-String-3}}" != "%{Tmp-String-1}") {
+if (<string>"%{unescape:%{Tmp-String-3}}" != "%{Tmp-String-1}") {
test_fail
}
-if (<string>"%{unescape:%{request:Tmp-String-4}}" != &Tmp-String-2) {
+if (<string>"%{unescape:%{Tmp-String-4}}" != &Tmp-String-2) {
test_fail
}
-if (<string>"%{escape:%{request:Tmp-String-6}}" != &Tmp-String-7) {
+if (<string>"%{escape:%{Tmp-String-6}}" != &Tmp-String-7) {
test_fail
}
-if (<string>"%{unescape:%{request:Tmp-String-7}}" != &Tmp-String-6) {
+if (<string>"%{unescape:%{Tmp-String-7}}" != &Tmp-String-6) {
test_fail
}
#
# XLAT virtual comparisons
#
-if (&control:Tmp-Integer-0[*] != "%{control:Tmp-Integer-0[#]}") {
+if (&control:Tmp-Integer-0[*] != "%{control.Tmp-Integer-0[#]}") {
test_fail
}
# 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"
}
}
}
&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
}
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}}"
}
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}}"
&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 {
&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
}
# 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 {
&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 {
# 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
# 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
# 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
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
# 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
# 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
# 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
# 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
# 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
}
}
-if ("%{control:Tmp-Integer-0[#]}" != 4) {
+if ("%{control.Tmp-Integer-0[#]}" != 4) {
test_fail
return
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} }"
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}}"
}
#
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) {
&Tmp-String-0 := 'foo'
}
-switch "%{tolower:%{request:Tmp-String-0}}" {
+switch "%{tolower:%{Tmp-String-0}}" {
case 'foo' {
success
}
}
}
-switch "%{request:Tmp-String-0}" {
+switch "%{Tmp-String-0}" {
case 'foo' {
success
}
+++ /dev/null
-#
-# 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
- }
-
-}
# 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
}
# 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
}
# 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) {
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
}
+++ /dev/null
-#
-# 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
+++ /dev/null
-#
-# 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
# Verify we haven't acquired any extra..
-if ("%{control:Reply-Message[#]}" != 3) {
+if ("%{control.Reply-Message[#]}" != 3) {
test_fail
}
}
# 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
}
+++ /dev/null
-#
-# 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
}
# 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
}
+++ /dev/null
-#
-# 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
#
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
}
&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
}
&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) {
}
update {
- "Attr-%{attr_num:Tmp-String-1}" := 'hello2'
+ "%{attr_num:Tmp-String-1}" := 'hello2'
}
if (&Tmp-String-1 != 'hello2') {
}
update request {
- &Tmp-String-1 := "%{urlunquote:%{request:Tmp-String-0}}"
+ &Tmp-String-1 := "%{urlunquote:%{Tmp-String-0}}"
&Tmp-String-2 := "%{urlunquote:%%E,123}"
}
# 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 {
#
# PRE: update if
#
-if (&request:Packet-Type == Access-Request) {
+if (&Packet-Type == Access-Request) {
success
}
#
# 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) {
+++ /dev/null
-#
-# 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
#
-# PRE: if xlat-attr-tag
+# PRE: if
#
#
}
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
}
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
}
test_fail
}
-if ("%{control:Reply-Message[#]}" != 1) {
+if ("%{control.Reply-Message[#]}" != 1) {
test_fail
}
}
# 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}"
&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') {
}
# Response hasn't been set yet
-if ("%{reply:Packet-Type}" != '') {
+if ("%{reply.Packet-Type}" != '') {
test_fail
}
&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
}
# session-state should now contain all the reply attributes
-if ("%{session-state:[#]}" == 4) {
+if ("%{session-state[#]}" == 2) {
test_pass
}
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
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
&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'
}
update request {
- &CHAP-Password := "%{chap_password:%{control:Cleartext-Password}}"
+ &CHAP-Password := "%{chap_password:%{control.Cleartext-Password}}"
}
chap.authenticate
# 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.
format = "%{User-Name} default"
- reference = ".messages.%{control:Tmp-String-0}"
+ reference = ".messages.%{control.Tmp-String-0}"
messages {
test_exec = `/bin/echo "%{User-Name} exec"`
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"`
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'
}
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[*]
}
}
}
# 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
#
# 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
# 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`
}
#
#
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
}
}
# 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`
}
#
&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
#
# 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
#
# 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
# 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`
}
#
# 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`
}
#
# 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
#
# 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
# 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
# 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
# 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`
}
#
# 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
#
# 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
# 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 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
#
# 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
&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
# 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
}
# 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
# 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
&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
}
# 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
# 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
}
# 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
# 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
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 {
# 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
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 {
# 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
# 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) {
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 {
# 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) {
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 {
# 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 {
# 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) {
# 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) {
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 {
# 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) {
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 {
# 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 {
# 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) {
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 {
}
update reply {
- &raw.Attr-26 := &reply:Class
- &raw.Attr-26 += 0x483d342c493d43
+ &raw.26 := &reply:Class
+ &raw.26 += 0x483d342c493d43
}
}
# 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
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.
match ERROR offset 22 Unexpected cast
condition <ipaddr>&Filter-Id == <blerg>&Framed-IP-Address
-match ERROR offset 22 Unexpected cast
+match ERROR offset 23 Unknown data type
condition <blerg>&Filter-Id == "foo"
match ERROR offset 1 Unknown data type
#
# Normalize things
#
-condition <ipaddr>&Filter-Id == "127.0.0.1"
-match <ipaddr>&Filter-Id == '127.0.0.1'
-
condition <ipaddr>127.0.0.1 < &Framed-IP-Address
match &Framed-IP-Address > 127.0.0.1
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.
condition <ipaddr>127.0.0.1 == "127.0.0.1"
match true
-condition <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
-match <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
+condition <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
+match <ipaddr>127.0.0.1 == "%{md4: 127.0.0.1}"
#
# Bare %{...} is allowed.
#
-condition <ipaddr>127.0.0.1 == %{sql:127.0.0.1}
-match <ipaddr>127.0.0.1 == "%{sql:127.0.0.1}"
+condition <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
+match <ipaddr>127.0.0.1 == %{md4:127.0.0.1}
-condition <ipaddr>127.0.0.1 == %{sql: SELECT user FROM table WHERE user='%{User-Name}'}
-match <ipaddr>127.0.0.1 == "%{sql: SELECT user FROM table WHERE user='%{User-Name}'}"
+condition <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
+match <ipaddr>127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'}
condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
match true
-condition <ether> 00:11:22:33:44:55 == "ff:11:22:33:44:55"
-match false
-condition <ether> 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
-match <ether>00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
+condition <ether> 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
+match <ether>00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}"
condition <ether> 00:XX:22:33:44:55 == 00:11:22:33:44:55
match ERROR offset 8 Failed to parse field
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
condition (&User-Name == "bob")
match &User-Name == "bob"
-condition (&User-Name == "%{sql: blah}")
-match &User-Name == "%{sql: blah}"
+condition (&User-Name == "%{md4: blah}")
+match &User-Name == "%{md4: blah}"
condition <ipaddr>127.0.0.1 == 2130706433
match true
condition <string>"foo" == &User-Name
match &User-Name == "foo"
-condition <integer>"%{expr: 1 + 1}" < &NAS-Port
-match &NAS-Port > "%{expr: 1 + 1}"
+# This used to be expr, but expr isn't a builtin, so it failed...
+condition <integer>"%{md4: 1 + 1}" < &NAS-Port
+match &NAS-Port > "%{md4: 1 + 1}"
condition &Filter-Id == &Framed-IP-Address
-match ERROR offset 0 Attribute comparisons must be of the same data type
+match ERROR offset 0 Cannot compare attributes of type 'string' and 'ipaddr'
condition <ipaddr>127.0.0.1 == &Filter-Id
-match ERROR offset 0 Attribute comparisons must be of the same data type
+match ERROR offset 0 Cannot compare types 'ipaddr' (cast) and 'string' (attr)
condition &Tmp-Integer64-0 == &request:Foo-Stuff-Bar
match &Tmp-Integer64-0 == &Foo-Stuff-Bar
condition <ipaddr>&Class == &Framed-IP-Address
match <ipaddr>&Class == &Framed-IP-Address
-#
-# Tags of zero mean there's no tag
-#
-condition &Tunnel-Password:0 == "Hello"
-match &Tunnel-Password == "Hello"
-
-condition &Tunnel-Password:1 == "Hello"
-match &Tunnel-Password:1 == "Hello"
-
-#
-# Single quoted strings are left as-is
-#
-condition &Tunnel-Password:1 == 'Hello'
-match &Tunnel-Password:1 == 'Hello'
-
#
# zero offset into arrays get parsed and ignored
#
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,
#
# 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
#
# 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
# 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
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
#
# '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
#
match !&User-Name == "foo\nbar"
#
-# We require explicit casts
+# We infer that the LHS is a prefix and the RHS is
+# and ipaddr without requiring an explicit cast.
#
condition 192.168.0.0/16 > 192.168.1.2
-match false
+match true
condition <ipv4prefix>192.168.0.0/16 > 192.168.1.2
match true
condition &NAS-IP-Address < 192.168/16
match <ipv4prefix>&NAS-IP-Address < 192.168.0.0/16
-condition &NAS-IP-Address < "%{echo: 192.168/16}"
-match <ipv4prefix>&NAS-IP-Address < "%{echo: 192.168/16}"
+condition &NAS-IP-Address < "%{string: 192.168/16}"
+match <ipv4prefix>&NAS-IP-Address < "%{string: 192.168/16}"
condition &NAS-IP-Address < `/bin/echo 192.168/16`
match <ipv4prefix>&NAS-IP-Address < `/bin/echo 192.168/16`
match true
count
-match 334
+match 306
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
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
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
#
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 =~ /@|\\/
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
# | 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.
# | 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
# | 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.
# | 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.
# | 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.
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
# 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
# @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
-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
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
#
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
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]}
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}
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}\"
# 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}
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
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
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.
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