[options="header,autowidth"]
|===
| XLAT | Description
-| `%{client:nas_type}` | Expands to client's `nas_type` (or "" if nas_type not set).
-| `%{client:<ipaddr>.nas_type}` | Expands to the `nas_type` of the client specified
- by `<ipaddr>` (or "" if `nas_type` not set).
+| `%(client:<field>)` | Expands to a field in the client definition.
+| `%(client:<field> <ipaddr>)` | Expands to a field in the client definition specified
+ by `<ipaddr>`.
|===
Module rlm_exec.shell_escape = yes
```
-=== %{client:<key>}
+=== %(client:<key>)
Refers to a variable that was defined in the client section for the
current client. See the sections `client { ... }` in `clients.conf`.
[source,unlang]
----
-"The client ipaddr is %{client:ipaddr}"
+"The client ipaddr is %(client:ipaddr)"
----
.Output
=== +%{Packet-SRC-IP-Address} and %{Packet-SRC-IPv6-Address}+
The source IPv4 or IPv6 address of the packet. See also the expansions
-`%{client:ipaddr}` and `%{client:ipv6addr}`. The two expansions
-should be identical, unless `%{client:ipaddr}` contains a DNS hostname.
+`%(client:ipaddr)` and `%(client:ipv6addr)`. The two expansions
+should be identical, unless `%(client:ipaddr)` contains a DNS hostname.
=== +%{Packet-DST-IP-Address} and %{Packet-DST-IPv6-Address}+
# [options="header,autowidth"]
# |===
# | XLAT | Description
-# | `%{client:nas_type}` | Expands to client's `nas_type` (or "" if nas_type not set).
-# | `%{client:<ipaddr>.nas_type}` | Expands to the `nas_type` of the client specified
-# by `<ipaddr>` (or "" if `nas_type` not set).
+# | `%(client:<field>)` | Expands to a field in the client definition.
+# | `%(client:<field> <ipaddr>)` | Expands to a field in the client definition specified
+ by `<ipaddr>`.
# |===
#
return RLM_MODULE_REJECT;
} else {
# Accept user and set some attribute
- if (&radiusd::xlat("%{client:group}") eq 'UltraAllInclusive') {
+ if (&radiusd::xlat("%(client:group)") eq 'UltraAllInclusive') {
# User called from NAS with unlim plan set, set higher limits
$RAD_REPLY{'h323-credit-amount'} = "1000000";
} else {
abfab_client_check {
# check that the acceptor host name is correct
- if ("%{client:gss_acceptor_host_name}" && &GSS-acceptor-host-name) {
- if ("%{client:gss_acceptor_host_name}" != "%{gss-acceptor-host-name}") {
+ if ("%(client:gss_acceptor_host_name)" && &GSS-acceptor-host-name) {
+ if ("%(client:gss_acceptor_host_name)" != "%{gss-acceptor-host-name}") {
update reply {
&Reply-Message = "GSS-Acceptor-Host-Name incorrect"
}
}
# set trust-router-coi attribute from the client configuration
- if ("%{client:trust_router_coi}") {
+ if ("%(client:trust_router_coi)") {
update request {
- &Trust-Router-COI := "%{client:trust_router_coi}"
+ &Trust-Router-COI := "%(client:trust_router_coi)"
}
}
# set gss-acceptor-realm-name attribute from the client configuration
- if ("%{client:gss_acceptor_realm_name}") {
+ if ("%(client:gss_acceptor_realm_name)") {
update request {
- &GSS-Acceptor-Realm-Name := "%{client:gss_acceptor_realm_name}"
+ &GSS-Acceptor-Realm-Name := "%(client:gss_acceptor_realm_name)"
}
}
}
# }
#
cui.authorize {
- if ("%{client:add_cui}" == 'yes') {
+ if ("%(client:add_cui)" == 'yes') {
update request {
&Chargeable-User-Identity := 0x00
}
# an Operator-Name attribute
#
operator-name.authorize {
- if ("%{client:Operator-Name}") {
+ if ("%(client:Operator-Name)") {
update request {
- &Operator-Name = "%{client:Operator-Name}"
+ &Operator-Name = "%(client:Operator-Name)"
}
}
}
return rcode;
}
+static xlat_arg_parser_t const xlat_client_args[] = {
+ { .required = true, .single = true, .type = FR_TYPE_STRING },
+ { .single = true, .type = FR_TYPE_STRING },
+ XLAT_ARG_PARSER_TERMINATOR
+};
+
/** xlat to get client config data
*
* Example:
@verbatim
-%{client:[<ipaddr>.]foo}
+%(client:foo [<ipaddr>])
@endverbatim
*
* @ingroup xlat_functions
*/
-static ssize_t xlat_client(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
- UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
- request_t *request, char const *fmt)
+static xlat_action_t xlat_client(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request,
+ UNUSED void const *xlat_inst, UNUSED void *xlat_thread_inst,
+ fr_value_box_list_t *in)
{
char const *value = NULL;
- char buffer[INET6_ADDRSTRLEN], *q;
- char const *p = fmt;
fr_ipaddr_t ip;
CONF_PAIR *cp;
RADCLIENT *client = NULL;
-
- *out = NULL;
-
- q = strrchr(p, '.');
- if (q) {
- strlcpy(buffer, p, (q + 1) - p);
- if (fr_inet_pton(&ip, buffer, -1, AF_UNSPEC, false, true) < 0) goto request_client;
-
- p = q + 1;
+ fr_value_box_t *field = fr_dlist_head(in);
+ fr_value_box_t *client_ip = fr_dlist_next(in, field);
+ fr_value_box_t *vb;
+
+ if (client_ip) {
+ if (fr_inet_pton(&ip, client_ip->vb_strvalue, -1, AF_UNSPEC, false, true) < 0) {
+ RDEBUG("Invalid client IP address \"%s\"", client_ip->vb_strvalue);
+ return XLAT_ACTION_FAIL;
+ }
client = client_find(NULL, &ip, IPPROTO_IP);
if (!client) {
- RDEBUG("No client found with IP \"%s\"", buffer);
- return 0;
+ RDEBUG("No client found with IP \"%s\"", client_ip->vb_strvalue);
+ return XLAT_ACTION_FAIL;
}
} else {
- request_client:
client = request->client;
if (!client) {
RERROR("No client associated with this request");
-
- return -1;
+ return XLAT_ACTION_FAIL;
}
}
- cp = cf_pair_find(client->cs, p);
+ cp = cf_pair_find(client->cs, field->vb_strvalue);
if (!cp || !(value = cf_pair_value(cp))) {
- if (strcmp(fmt, "shortname") == 0 && request->client->shortname) {
+ if (strcmp(field->vb_strvalue, "shortname") == 0 && request->client->shortname) {
value = request->client->shortname;
}
- else if (strcmp(fmt, "nas_type") == 0 && request->client->nas_type) {
+ else if (strcmp(field->vb_strvalue, "nas_type") == 0 && request->client->nas_type) {
value = request->client->nas_type;
}
- if (!value) return 0;
+ if (!value) return XLAT_ACTION_DONE;
+ }
+
+ MEM(vb = fr_value_box_alloc_null(ctx));
+
+ if (fr_value_box_strdup(ctx, vb, NULL, value, false) < 0) {
+ talloc_free(vb);
+ return XLAT_ACTION_FAIL;
}
- *out = talloc_typed_strdup(ctx, value);
- return talloc_array_length(*out) - 1;
+ fr_dcursor_append(out, vb);
+ return XLAT_ACTION_DONE;
}
*/
static int mod_bootstrap(void *instance, UNUSED CONF_SECTION *conf)
{
- xlat_register_legacy(instance, "client", xlat_client, NULL, NULL, 0, 0);
+ xlat_t *xlat;
+ xlat = xlat_register(instance, "client", xlat_client, false);
+ xlat_func_args(xlat, xlat_client_args);
map_proc_register(instance, "client", map_proc_client, NULL, 0);
return 0;
/*
* This is a wraper for xlat_aeval
* Now users are able to get data that is accessible only via xlat
- * e.g. %{client:...}
+ * e.g. %(client:...)
* Call syntax is radiusd::xlat(string), string will be handled the
* same way it is described in EXPANSIONS section of man unlang
*/
&Tmp-String-0 := 'nas_type'
}
-if (&Tmp-String-0 == 'a_type') {
- test_pass
-}
-else {
+if (&Tmp-String-0 != 'a_type') {
test_fail
}
&Tmp-String-0 := 'nas_type'
}
-if (&Tmp-String-0 == 'a_type') {
- test_pass
-}
-else {
+if (&Tmp-String-0 != 'a_type') {
test_fail
}
&Tmp-String-0 := 'nas_type'
}
-if (&Tmp-String-0 == 'b_type') {
- test_pass
-}
-else {
+if (&Tmp-String-0 != 'b_type') {
test_fail
}
&Tmp-String-0 := 'nas_type'
}
-if (&Tmp-String-0 == 'b_type') {
- test_pass
-}
-else {
+if (&Tmp-String-0 != 'b_type') {
test_fail
}
&Tmp-String-1 += 'group'
}
-if (&Tmp-String-1[0] == 'a') {
- test_pass
-}
-else {
+if (&Tmp-String-1[0] != 'a') {
test_fail
}
-if (&Tmp-String-1[1] == 'b') {
- test_pass
-}
-else {
+if (&Tmp-String-1[1] != 'b') {
test_fail
}
-if (&Tmp-String-1[2] == 'c') {
- test_pass
-}
-else {
+if (&Tmp-String-1[2] != 'c') {
test_fail
}
&Tmp-String-2 += 'group'
}
-if (&Tmp-String-2[0] == 'd') {
- test_pass
-}
-else {
+if (&Tmp-String-2[0] != 'd') {
test_fail
}
-if (&Tmp-String-2[1] == 'e') {
- test_pass
-}
-else {
+if (&Tmp-String-2[1] != 'e') {
test_fail
}
-if (&Tmp-String-2[2] == 'f') {
- test_pass
-}
-else {
+if (&Tmp-String-2[2] != 'f') {
test_fail
}
if (&Tmp-String-3) {
test_fail
}
-else {
- test_pass
-}
if (&Tmp-String-4) {
test_fail
}
-else {
- test_pass
-}
+test_pass
\ No newline at end of file
--- /dev/null
+update request {
+ &Tmp-String-0 := "%(client:nas_type)"
+}
+
+if (&Tmp-String-0 != 'a_type') {
+ test_fail
+}
+
+update request {
+ &Tmp-String-0 := "%(client:nas_type 127.0.0.1)"
+}
+
+if (&Tmp-String-0 != 'a_type') {
+ test_fail
+}
+
+update request {
+ &Tmp-String-0 := "%(client:nas_type 127.0.0.2)"
+}
+
+if (&Tmp-String-0 != 'b_type') {
+ test_fail
+}
+
+update request {
+ &Tmp-String-0 := "%(client:nas_type 127.0.0.5)"
+}
+
+if (&Tmp-String-0 != 'b_type') {
+ test_fail
+}
+
+#
+# Test non-existent client properties
+#
+update request {
+ &Tmp-String-3 := "%(client:non-existent-attr)"
+ &Tmp-String-4 += "%(client:non-existing-attr2)"
+}
+
+if (&Tmp-String-3 != "") {
+ test_fail
+}
+
+if (&Tmp-String-4 != "") {
+ test_fail
+}
+
+#
+# Tests for multi-valued keys to be added when update takes multiple values
+#
+
+test_pass
\ No newline at end of file
return RLM_MODULE_REJECT;
} else {
# Accept user and set some attribute
- if (&radiusd::xlat("%{client:group}") eq 'UltraAllInclusive') {
+ if (&radiusd::xlat("%(client:group)") eq 'UltraAllInclusive') {
# User called from NAS with unlim plan set, set higher limits
$RAD_REPLY{'h323-credit-amount'} = "1000000";
} else {