The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_brotli.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 0aa072e860ced14c67e41a1ccb3c644ca0941bea $
19 * @file rlm_brotli.c
20 * @brief Add support for brotli compression
21 *
22 * @copyright 2024 The FreeRADIUS server project
23 * @copyright 2024 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24 */
25RCSID("$Id: 0aa072e860ced14c67e41a1ccb3c644ca0941bea $")
26
27#define LOG_PREFIX mctx->mi->name
28
29#include <brotli/encode.h>
30#include <brotli/decode.h>
31
32#include <freeradius-devel/util/atexit.h>
33#include <freeradius-devel/util/value.h>
34
35#include <freeradius-devel/server/module_rlm.h>
36#include <freeradius-devel/server/cf_parse.h>
37
38#include <freeradius-devel/unlang/xlat.h>
39#include <freeradius-devel/unlang/xlat_func.h>
40
41static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
42static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
43static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
44
45typedef struct {
46 BrotliEncoderMode mode; //!< Default mode to use when compressing data.
47 int quality; //!< Default quality to use when compressing data.
48 int window_bits; //!< Default window bits to use when compressing data.
49 int block_bits; //!< Default block bits to use when compressing data.
50 bool block_bits_is_set; //!< Whether block_bits has been set.
52
53typedef struct {
54 size_t max_size; //!< Maximum amount we attempt to decode
56
57typedef struct {
58 rlm_brotli_compress_t compress; //!< Compression settings
59 rlm_brotli_decompress_t decompress; //!< Decompression settings
60 bool large_window; //!< non-standard "large", window size.
62
64 { L("font"), BROTLI_MODE_FONT }, //!< Probably not useful?
65 { L("generic"), BROTLI_MODE_GENERIC },
66 { L("text"), BROTLI_MODE_TEXT },
67};
69
71 { FR_CONF_OFFSET("mode", rlm_brotli_compress_t, mode), .dflt = "generic",
72 .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = brotli_mode, .len = &brotli_mode_len } },
73 { FR_CONF_OFFSET("quality", rlm_brotli_compress_t, quality), .dflt = STRINGIFY(BROTLI_DEFAULT_QUALITY), .func = quality_parse },
74 { FR_CONF_OFFSET("window_bits", rlm_brotli_compress_t, window_bits), .dflt = STRINGIFY(BROTLI_DEFAULT_WINDOW), .func = window_bits_parse },
75 { FR_CONF_OFFSET_IS_SET("block_bits", FR_TYPE_INT32, 0, rlm_brotli_compress_t, block_bits), .dflt = STRINGIFY(BROTLI_MAX_INPUT_BLOCK_BITS), .func = block_bits_parse },
77};
78
80 { FR_CONF_OFFSET_TYPE_FLAGS("max_size", FR_TYPE_SIZE, 0, rlm_brotli_decompress_t, max_size), .dflt = "10M" }, /* 10MB */
82};
83
84static const conf_parser_t module_config[] = {
86 { FR_CONF_OFFSET_SUBSECTION("decompress", 0, rlm_brotli_t, decompress, module_decompress_config) },
87 { FR_CONF_OFFSET("large_window", rlm_brotli_t, large_window), .dflt = "no" }, /* For both compress and decompress */
88
90};
91
92static _Thread_local TALLOC_CTX *brotli_pool; //!< Thread-local pool for brotli state
93
94static inline CC_HINT(always_inline)
95TALLOC_CTX *brotli_pool_get(void)
96{
97 if (unlikely(brotli_pool == NULL)) {
98 TALLOC_CTX *pool;
99
100 MEM(pool = talloc_pool(NULL, 4096));
101 fr_atexit_thread_local(brotli_pool, fr_atexit_talloc_free, pool);
102
103 }
104 return brotli_pool;
105}
106
107static void *brotli_talloc_alloc(void *uctx, size_t size)
108{
109 void *ptr = talloc_size(uctx, size);
110 return ptr;
111}
112
113static void brotli_talloc_free(UNUSED void *uctx, void *to_free)
114{
115 talloc_free(to_free);
116}
117
118static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
119{
120 int ret;
121 int value;
122
123 ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
124 if (ret < 0) return ret;
125
126 value = *(int *) out;
127 if ((value > BROTLI_MAX_QUALITY) || (value < BROTLI_MIN_QUALITY)) {
128 cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY, value);
129 return -1;
130 }
131
132 return 0;
133}
134
135static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
136{
137 int ret;
138 int value;
139
140 ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
141 if (ret < 0) return ret;
142
143 value = *(int *) out;
144 if ((value > BROTLI_MAX_WINDOW_BITS) || (value < BROTLI_MIN_WINDOW_BITS)) {
145 cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS, value);
146 return -1;
147 }
148
149 return 0;
150}
151
152static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
153{
154 int ret;
155 int value;
156
157 ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
158 if (ret < 0) return ret;
159
160 value = *(int *) out;
161 if ((value > BROTLI_MAX_INPUT_BLOCK_BITS) || (value < BROTLI_MIN_INPUT_BLOCK_BITS)) {
162 cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_INPUT_BLOCK_BITS, BROTLI_MAX_INPUT_BLOCK_BITS, value);
163 return -1;
164 }
165
166 return 0;
167}
168
170 { .required = true, .type = FR_TYPE_OCTETS }, /* Input converted to raw binary data. All inputs will be added to the same stream */
172};
173
174/** Produce a brotli compressed string
175 *
176 * Example:
177@verbatim
178%brotli.compress(<input>) == <compressed data>
179@endverbatim
180 *
181 * @ingroup xlat_functions
182 */
184 xlat_ctx_t const *xctx,
185 request_t *request, fr_value_box_list_t *args)
186{
188 fr_value_box_t const *data_vb;
189
190 BrotliEncoderState *state;
191 TALLOC_CTX *pool;
192
193 size_t available_out = 0, total_out = 0, total_in = 0;
194 fr_value_box_t *out_vb;
195 uint8_t *out_buff;
196
198
199 XLAT_ARGS(args, &data_vb);
200
201 fr_assert(fr_type_is_group(data_vb->type));
202
203 /*
204 * Calculate the maximum size of our output buffer
205 * and pre-allocate that. We'll shrink it later.
206 */
207 fr_value_box_list_foreach(&data_vb->vb_group, vb) {
208 total_in += vb->vb_length;
209 available_out += BrotliEncoderMaxCompressedSize(vb->vb_length);
210 }
211
212 MEM(out_vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
213 MEM(fr_value_box_mem_alloc(out_vb, &out_buff, out_vb, NULL, available_out, false) == 0);
214
215 pool = brotli_pool_get();
216 MEM(state = BrotliEncoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
217
218 BrotliEncoderSetParameter(state, BROTLI_PARAM_MODE, inst->compress.mode);
219 BrotliEncoderSetParameter(state, BROTLI_PARAM_QUALITY, inst->compress.quality);
220 BrotliEncoderSetParameter(state, BROTLI_PARAM_LGWIN, inst->compress.window_bits);
221 if (inst->compress.block_bits_is_set) BrotliEncoderSetParameter(state, BROTLI_PARAM_LGBLOCK, inst->compress.block_bits);
222 BrotliEncoderSetParameter(state, BROTLI_PARAM_LARGE_WINDOW, inst->large_window ? BROTLI_TRUE : BROTLI_FALSE);
223 BrotliEncoderSetParameter(state, BROTLI_PARAM_SIZE_HINT, total_in);
224 /*
225 * Loop over all the input data and ingest it into brotli
226 * which will add it to an internal buffer (hopefully
227 * allocated, in our thread local pool).
228 */
229 {
230 fr_value_box_list_foreach(&data_vb->vb_group, vb) {
231 size_t available_in = vb->vb_length;
232 const uint8_t *next_in = vb->vb_octets;
233 bool more = fr_value_box_list_next(&data_vb->vb_group, vb) != NULL;
234
235 /*
236 * In reality this loop is probably unnecessary,
237 * but the brotli docs state:
238 *
239 * "client should repeat BROTLI_OPERATION_FINISH operation until available_in becomes 0,
240 * and BrotliEncoderHasMoreOutput returns BROTLI_FALSE"
241 */
242 do {
243 BROTLI_BOOL bret;
244
245 bret = BrotliEncoderCompressStream(state,
246 more ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH,
247 &available_in, &next_in, &available_out, &out_buff, &total_out);
248 if (bret == BROTLI_FALSE) {
249 fr_assert_msg(0, "BrotliEncoderCompressStream returned false, this shouldn't happen");
250 RERROR("BrotliEncoderCompressStream failed");
251 ret = XLAT_ACTION_FAIL;
252 goto finish;
253 }
254 } while (more && (available_in > 0) && (BrotliEncoderHasMoreOutput(state) == BROTLI_FALSE));
255
256 /*
257 * There's no reason brotli wouldn't consume the complete
258 * buffer on BROTLI_OPERATION_PROCESS.
259 */
260 fr_assert(available_in == 0);
261 }
262 }
263
264 /*
265 * Realloc to the correct size if necessary
266 */
267 if (available_out != 0) MEM(fr_value_box_mem_realloc(out_vb, NULL, out_vb, total_out) == 0);
268
269 fr_dcursor_insert(out, out_vb);
270
271finish:
272 BrotliEncoderDestroyInstance(state);
273 talloc_free_children(pool);
274
275 return ret;
276}
277
279 { .required = true, .single = true, .type = FR_TYPE_OCTETS },
281};
282
283/** Decompress a brotli string
284 *
285 * Example:
286@verbatim
287%brotli.decompress(<input>) == <decompressed data>
288@endverbatim
289 *
290 * @ingroup xlat_functions
291 */
293 xlat_ctx_t const *xctx,
294 request_t *request, fr_value_box_list_t *args)
295{
297 fr_value_box_t const *data_vb;
298
299 BrotliDecoderState *state;
300 TALLOC_CTX *pool;
301
302 fr_value_box_t *out_vb;
303
304 size_t total_in;
305 size_t available_out;
306 size_t total_out = 0;
307
308 uint8_t const *in_buff;
309 uint8_t *out_buff;
310
312
313 XLAT_ARGS(args, &data_vb);
314
315 pool = brotli_pool_get();
316 MEM(state = BrotliDecoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
317
318 MEM(out_vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
319 total_in = data_vb->vb_length;
320 in_buff = data_vb->vb_octets;
321 available_out = (data_vb->vb_length * 2);
322
323 MEM(fr_value_box_mem_alloc(out_vb, &out_buff, out_vb, NULL, available_out, false) == 0);
324 pool = brotli_pool_get();
325 MEM(state = BrotliDecoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
326
327 BrotliDecoderSetParameter(state, BROTLI_DECODER_PARAM_LARGE_WINDOW, inst->large_window ? BROTLI_TRUE : BROTLI_FALSE);
328
329 for (;;) {
330 switch (BrotliDecoderDecompressStream(state, &total_in, &in_buff, &available_out, &out_buff, &total_out)) {
331 default:
332 case BROTLI_DECODER_RESULT_ERROR:
333 {
334 BrotliDecoderErrorCode error = BrotliDecoderGetErrorCode(state);
335 REDEBUG("Decompressing brotli data failed - %s", BrotliDecoderErrorString(error));
336 ret = XLAT_ACTION_FAIL;
337 goto finish;
338 }
339
340 case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
341 REDEBUG("Incomplete or truncated brotli data provided. Decompressor wants more input...");
342 ret = XLAT_ACTION_FAIL;
343 goto finish;
344
345 case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
346 {
347 size_t extra = out_vb->vb_length;
348
349 /*
350 * Stop runaway brotli decodings...
351 */
352 if ((out_vb->vb_length + extra) > inst->decompress.max_size) {
353 RERROR("Decompressed data exceeds maximum size of %zu", inst->decompress.max_size);
354 ret = XLAT_ACTION_FAIL;
355 goto finish;
356 }
357
358 MEM(fr_value_box_mem_realloc(out_vb, &out_buff, out_vb, out_vb->vb_length + extra) == 0);
359 available_out += extra;
360 }
361 continue; /* Again! */
362
363 case BROTLI_DECODER_RESULT_SUCCESS:
364 if (BrotliDecoderIsFinished(state) == BROTLI_TRUE) {
365 MEM(fr_value_box_mem_realloc(out_vb, &out_buff, out_vb, total_out) == 0);
366 fr_dcursor_insert(out, out_vb);
367 goto finish;
368 }
369 continue; /* Again! */
370 }
371 }
372
373finish:
374 BrotliDecoderDestroyInstance(state);
375 talloc_free_children(pool);
376
377 return ret;
378}
379
380static int mod_bootstrap(module_inst_ctx_t const *mctx)
381{
382 xlat_t *xlat;
383
384 if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "compress", brotli_xlat_compress,
385 FR_TYPE_OCTETS)) == NULL)) return -1;
387
388 if (unlikely((xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "decompress", brotli_xlat_decompress,
389 FR_TYPE_OCTETS)) == NULL)) return -1;
391
392 return 0;
393}
394
395/*
396 * The module name should be the only globally exported symbol.
397 * That is, everything else should be 'static'.
398 *
399 * If the module needs to temporarily modify it's instantiation
400 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
401 * The server will then take care of ensuring that the module
402 * is single-threaded.
403 */
406 .common = {
407 .magic = MODULE_MAGIC_INIT,
408 .name = "brotli",
409 .inst_size = sizeof(rlm_brotli_t),
410 .bootstrap = mod_bootstrap,
412 }
413};
va_list args
Definition acutest.h:770
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define STRINGIFY(x)
Definition build.h:197
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition cf_parse.c:1550
int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM *ci, conf_parser_t const *rule)
Parses a CONF_PAIR into a C data type.
Definition cf_parse.c:171
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:596
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:282
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:297
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
Common header for all CONF_* types.
Definition cf_priv.h:49
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define MEM(x)
Definition debug.h:36
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static xlat_action_t brotli_xlat_decompress(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Decompress a brotli string.
Definition rlm_brotli.c:292
static xlat_action_t brotli_xlat_compress(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Produce a brotli compressed string.
Definition rlm_brotli.c:183
#define RERROR(fmt,...)
Definition log.h:298
talloc_free(reap)
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_OCTETS
Raw octets.
unsigned char uint8_t
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:257
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
static const conf_parser_t module_decompress_config[]
Definition rlm_brotli.c:79
static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_brotli.c:152
module_rlm_t rlm_brotli
Definition rlm_brotli.c:405
static void brotli_talloc_free(UNUSED void *uctx, void *to_free)
Definition rlm_brotli.c:113
static const conf_parser_t module_compress_config[]
Definition rlm_brotli.c:70
bool large_window
non-standard "large", window size.
Definition rlm_brotli.c:60
int quality
Default quality to use when compressing data.
Definition rlm_brotli.c:47
static xlat_arg_parser_t const brotli_xlat_compress_args[]
Definition rlm_brotli.c:169
static fr_table_num_sorted_t const brotli_mode[]
Definition rlm_brotli.c:63
static xlat_arg_parser_t const brotli_xlat_decompress_args[]
Definition rlm_brotli.c:278
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_brotli.c:380
static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_brotli.c:135
rlm_brotli_decompress_t decompress
Decompression settings.
Definition rlm_brotli.c:59
static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition rlm_brotli.c:118
size_t max_size
Maximum amount we attempt to decode.
Definition rlm_brotli.c:54
size_t brotli_mode_len
Definition rlm_brotli.c:68
BrotliEncoderMode mode
Default mode to use when compressing data.
Definition rlm_brotli.c:46
static TALLOC_CTX * brotli_pool_get(void)
Definition rlm_brotli.c:95
bool block_bits_is_set
Whether block_bits has been set.
Definition rlm_brotli.c:50
int block_bits
Default block bits to use when compressing data.
Definition rlm_brotli.c:49
int window_bits
Default window bits to use when compressing data.
Definition rlm_brotli.c:48
rlm_brotli_compress_t compress
Compression settings.
Definition rlm_brotli.c:58
static _Thread_local TALLOC_CTX * brotli_pool
Thread-local pool for brotli state.
Definition rlm_brotli.c:92
static const conf_parser_t module_config[]
Definition rlm_brotli.c:84
static void * brotli_talloc_alloc(void *uctx, size_t size)
Definition rlm_brotli.c:107
size_t inst_size
Size of the module's instance data.
Definition module.h:203
void * data
Module's instance data.
Definition module.h:271
void * boot
Data allocated during the boostrap phase.
Definition module.h:274
eap_aka_sim_process_conf_t * inst
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_get_type_abort_const
Definition talloc.h:282
bool required
Argument must be present, and non-empty.
Definition xlat.h:148
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:381
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:168
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:147
static fr_slen_t parent
Definition pair.h:851
#define fr_type_is_group(_x)
Definition types.h:355
int fr_value_box_mem_alloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Pre-allocate an octets buffer for filling by the caller.
Definition value.c:4371
int fr_value_box_mem_realloc(TALLOC_CTX *ctx, uint8_t **out, fr_value_box_t *dst, size_t len)
Change the length of a buffer already allocated to a value box.
Definition value.c:4404
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:621
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:206
static size_t char ** out
Definition value.h:997
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365