]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add `map list` as a "builtin" map
authorNick Porter <nick@portercomputing.co.uk>
Thu, 24 Jul 2025 10:54:00 +0000 (11:54 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Thu, 24 Jul 2025 12:32:15 +0000 (13:32 +0100)
src/lib/unlang/all.mk
src/lib/unlang/base.c
src/lib/unlang/map.h
src/lib/unlang/map_builtin.c [new file with mode: 0644]
src/lib/unlang/unlang_priv.h

index d88675e394fe5876307ad00f3c13377b285d06fd..0881c9e2db9d1f3822c577167a46040a05319a89 100644 (file)
@@ -20,6 +20,7 @@ SOURCES       :=      base.c \
                limit.c \
                load_balance.c \
                map.c \
+               map_builtin.c \
                mod_action.c \
                module.c \
                parallel.c \
@@ -55,6 +56,6 @@ LOG_ID_LIB    := 2
 
 # different pieces of this library
 $(call DEFINE_LOG_ID_SECTION,compile,  1,compile.c)
-$(call DEFINE_LOG_ID_SECTION,keywords, 2,call.c caller.c condition.c detach.c foreach.c function.c group.c io.c load_balance.c map.c module.c parallel.c return.c subrequest.c subrequest_child.c switch.c)
+$(call DEFINE_LOG_ID_SECTION,keywords, 2,call.c caller.c condition.c detach.c foreach.c function.c group.c io.c load_balance.c map.c map_builtin.c module.c parallel.c return.c subrequest.c subrequest_child.c switch.c)
 $(call DEFINE_LOG_ID_SECTION,interpret,        3, interpret.c interpret_synchronous.c)
 $(call DEFINE_LOG_ID_SECTION,expand,   4,tmpl.c xlat.c xlat_builtin.c xlat_eval.c xlat_inst.c xlat_pair.c xlat_tokenize.c)
index 7358bf696313a5cbe3514f88a9ebf13fc5c66cd8..e68b0a20ef59388f4d885bd07edf8640a9ca41b0 100644 (file)
@@ -112,6 +112,11 @@ static int _unlang_global_init(UNUSED void *uctx)
                return -1;
        }
 
+       /*
+        *      Initialise global maps
+        */
+       if (map_global_init() < 0) goto fail;
+
        unlang_interpret_init_global(unlang_ctx);
 
        /*
index f44c18ce56082f6ad0e75157106dcd74cc5fb167..e84e1b12a29d16f56d32b73252e2d4752dec72b1 100644 (file)
@@ -44,6 +44,12 @@ typedef void (*unlang_map_signal_t)(map_ctx_t const *mpctx, request_t *request,
 
 unlang_action_t unlang_map_yield(request_t *request,
                                 map_proc_func_t resume, unlang_map_signal_t signal, fr_signal_t sigmask, void *rctx);
+
+/*
+ *     map_builtin.c
+ */
+int            map_global_init(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/lib/unlang/map_builtin.c b/src/lib/unlang/map_builtin.c
new file mode 100644 (file)
index 0000000..6ba4df1
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file map_builtin.c
+ * @brief Built in map expansions.
+ *
+ * @copyright 2025 Network RADIUS SAS (legal@networkradius.com)
+ */
+
+
+RCSID("$Id$")
+
+#include <freeradius-devel/server/base.h>
+#include "map.h"
+
+static TALLOC_CTX *map_ctx;
+
+static int list_map_verify(CONF_SECTION *cs, UNUSED void const *mod_inst, UNUSED void *proc_inst,
+                          tmpl_t const *src, UNUSED map_list_t const *maps)
+{
+       if (!src) {
+               cf_log_err(cs, "Missing source of value list");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int _list_map_proc_get_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
+                                   request_t *request, map_t const *map, void *uctx)
+{
+       fr_pair_t       *vp;
+       fr_value_box_t  *value = talloc_get_type_abort(uctx, fr_value_box_t);
+
+       vp = fr_pair_afrom_da_nested(ctx, out, tmpl_attr_tail_da(map->lhs));
+       if (!vp) return -1;
+
+       if (fr_value_box_cast(vp, &vp->data, vp->data.type, vp->da, value) < 0) {
+               RPEDEBUG("Failed casting \"%pV\" for attribute %s", value, vp->da->name);
+               fr_pair_delete(out, vp);
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Map a list of value boxes to attributes using the index number in the list.
+ */
+static unlang_action_t mod_list_map_proc(unlang_result_t *p_result, UNUSED map_ctx_t const *mpctx, request_t *request,
+                                        fr_value_box_list_t *in, map_list_t const *maps)
+{
+       rlm_rcode_t             rcode = RLM_MODULE_NOOP;
+       fr_value_box_t          *vb = NULL;
+       fr_value_box_t          **values;
+       size_t                  index, i = 0, value_count = fr_value_box_list_num_elements(in);
+       TALLOC_CTX              *local = talloc_new(NULL);
+       map_t                   *map = NULL;
+
+       if (value_count == 0) goto finish;
+       /*
+        *      Use an array to point to the list entries so we don't
+        *      repeatedly walk the list to find each index in the map.
+        */
+       MEM(values = talloc_array(local, fr_value_box_t *, value_count));
+       while ((vb = fr_value_box_list_next(in, vb))) values[i++] = vb;
+
+       /*
+        *      Indexes are zero offset - so reduce value_count to the max index.
+        */
+       value_count --;
+
+       while ((map = map_list_next(maps, map))) {
+               if (tmpl_aexpand(local, &index, request, map->rhs, NULL, NULL) < 0) {
+                       RPERROR("Failed expanding map RHS");
+                       rcode = RLM_MODULE_FAIL;
+                       goto finish;
+               }
+               if (index > value_count) {
+                       RWARN("Asked for index %ld when max is %ld.", index, value_count);
+                       continue;
+               }
+               if (values[index]->type == FR_TYPE_NULL) {
+                       RDEBUG2("Skipping null value for index %ld.", index);
+                       continue;
+               }
+
+               if (map_to_request(request, map, _list_map_proc_get_value, values[index]) < 0) {
+                       rcode = RLM_MODULE_FAIL;
+                       goto finish;
+               }
+               rcode = RLM_MODULE_UPDATED;
+       }
+
+finish:
+       talloc_free(local);
+
+       RETURN_UNLANG_RCODE(rcode);
+}
+
+static int _map_global_init(UNUSED void *uctx)
+{
+       map_ctx = talloc_init("map");
+       map_proc_register(map_ctx, NULL, "list", mod_list_map_proc, list_map_verify, 0, FR_VALUE_BOX_SAFE_FOR_ANY);
+       return 0;
+}
+
+static int _map_global_free(UNUSED void *uctx)
+{
+       talloc_free(map_ctx);
+       return 0;
+}
+
+int map_global_init(void)
+{
+       int ret;
+       fr_atexit_global_once_ret(&ret, _map_global_init, _map_global_free, NULL);
+       return ret;
+}
index 291fdb20330f149d88bca5aa17bc3c452723aa6b..95bb2105a8caaaa9fcf5ed0a89804113e6d128c9 100644 (file)
@@ -31,6 +31,7 @@
 #include <freeradius-devel/server/time_tracking.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/unlang/base.h>
+#include <freeradius-devel/unlang/map.h>
 #include <freeradius-devel/io/listen.h>
 
 #ifdef __cplusplus