The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: d87f5b0f2364c7c760d53e0df3a00a3f373f9c99 $
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  */
25 RCSID("$Id: d87f5b0f2364c7c760d53e0df3a00a3f373f9c99 $")
26 
27 #define LOG_PREFIX mctx->inst->name
28 
29 #include <talloc.h>
30 
31 #include <brotli/encode.h>
32 #include <brotli/decode.h>
33 
34 #include <freeradius-devel/util/atexit.h>
35 #include <freeradius-devel/util/value.h>
36 
37 #include <freeradius-devel/server/module_rlm.h>
38 #include <freeradius-devel/server/cf_parse.h>
39 
40 #include <freeradius-devel/unlang/xlat.h>
41 #include <freeradius-devel/unlang/xlat_func.h>
42 
43 static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
44 static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
45 static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
46 
47 typedef struct {
48  BrotliEncoderMode mode; //!< Default mode to use when compressing data.
49  int quality; //!< Default quality to use when compressing data.
50  int window_bits; //!< Default window bits to use when compressing data.
51  int block_bits; //!< Default block bits to use when compressing data.
52  bool block_bits_is_set; //!< Whether block_bits has been set.
54 
55 typedef struct {
56  size_t max_size; //!< Maximum amount we attempt to decode
58 
59 typedef struct {
60  rlm_brotli_compress_t compress; //!< Compression settings
61  rlm_brotli_decompress_t decompress; //!< Decompression settings
62  bool large_window; //!< non-standard "large", window size.
63 } rlm_brotli_t;
64 
66  { L("font"), BROTLI_MODE_FONT }, //!< Probably not useful?
67  { L("generic"), BROTLI_MODE_GENERIC },
68  { L("text"), BROTLI_MODE_TEXT },
69 };
71 
73  { FR_CONF_OFFSET("mode", rlm_brotli_compress_t, mode), .dflt = "generic",
74  .func = cf_table_parse_int, .uctx = &(cf_table_parse_ctx_t){ .table = brotli_mode, .len = &brotli_mode_len } },
75  { FR_CONF_OFFSET("quality", rlm_brotli_compress_t, quality), .dflt = STRINGIFY(BROTLI_DEFAULT_QUALITY), .func = quality_parse },
76  { FR_CONF_OFFSET("window_bits", rlm_brotli_compress_t, window_bits), .dflt = STRINGIFY(BROTLI_DEFAULT_WINDOW), .func = window_bits_parse },
77  { 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 },
79 };
80 
82  { FR_CONF_OFFSET_TYPE_FLAGS("max_size", FR_TYPE_SIZE, 0, rlm_brotli_decompress_t, max_size), .dflt = "10M" }, /* 10MB */
84 };
85 
86 static const conf_parser_t module_config[] = {
87  { FR_CONF_OFFSET_SUBSECTION("compress", 0, rlm_brotli_t, compress, module_compress_config) },
88  { FR_CONF_OFFSET_SUBSECTION("decompress", 0, rlm_brotli_t, decompress, module_decompress_config) },
89  { FR_CONF_OFFSET("large_window", rlm_brotli_t, large_window), .dflt = "no" }, /* For both compress and decompress */
90 
92 };
93 
94 static _Thread_local TALLOC_CTX *brotli_pool; //!< Thread-local pool for brotli state
95 
96 static inline CC_HINT(always_inline)
97 TALLOC_CTX *brotli_pool_get(void)
98 {
99  if (unlikely(brotli_pool == NULL)) {
100  TALLOC_CTX *pool;
101 
102  MEM(pool = talloc_pool(NULL, 4096));
103  fr_atexit_thread_local(brotli_pool, fr_atexit_talloc_free, pool);
104 
105  }
106  return brotli_pool;
107 }
108 
109 static void *brotli_talloc_alloc(void *uctx, size_t size)
110 {
111  void *ptr = talloc_size(uctx, size);
112  return ptr;
113 }
114 
115 static void brotli_talloc_free(UNUSED void *uctx, void *to_free)
116 {
117  talloc_free(to_free);
118 }
119 
120 static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
121 {
122  int ret;
123  int value;
124 
125  ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
126  if (ret < 0) return ret;
127 
128  value = *(int *) out;
129  if ((value > BROTLI_MAX_QUALITY) || (value < BROTLI_MIN_QUALITY)) {
130  cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY, value);
131  return -1;
132  }
133 
134  return 0;
135 }
136 
137 static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
138 {
139  int ret;
140  int value;
141 
142  ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
143  if (ret < 0) return ret;
144 
145  value = *(int *) out;
146  if ((value > BROTLI_MAX_WINDOW_BITS) || (value < BROTLI_MIN_WINDOW_BITS)) {
147  cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS, value);
148  return -1;
149  }
150 
151  return 0;
152 }
153 
154 static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
155 {
156  int ret;
157  int value;
158 
159  ret = cf_pair_parse_value(ctx, out, parent, ci, rule);
160  if (ret < 0) return ret;
161 
162  value = *(int *) out;
163  if ((value > BROTLI_MAX_INPUT_BLOCK_BITS) || (value < BROTLI_MIN_INPUT_BLOCK_BITS)) {
164  cf_log_err(ci, "Allowed values are between %d-%d, got %d", BROTLI_MIN_INPUT_BLOCK_BITS, BROTLI_MAX_INPUT_BLOCK_BITS, value);
165  return -1;
166  }
167 
168  return 0;
169 }
170 
172  { .required = true, .type = FR_TYPE_OCTETS }, /* Input converted to raw binary data. All inputs will be added to the same stream */
174 };
175 
176 /** Produce a brotli compressed string
177  *
178  * Example:
179 @verbatim
180 %brotli.compress(<input>) == <compressed data>
181 @endverbatim
182  *
183  * @ingroup xlat_functions
184  */
186  xlat_ctx_t const *xctx,
187  request_t *request, fr_value_box_list_t *args)
188 {
190  fr_value_box_t const *data_vb;
191 
192  BrotliEncoderState *state;
193  TALLOC_CTX *pool;
194 
195  size_t available_out = 0, total_out = 0, total_in = 0;
196  fr_value_box_t *out_vb;
197  uint8_t *out_buff;
198 
200 
201  XLAT_ARGS(args, &data_vb);
202 
203  fr_assert(fr_type_is_group(data_vb->type));
204 
205  /*
206  * Calculate the maximum size of our output buffer
207  * and pre-allocate that. We'll shrink it later.
208  */
209  fr_value_box_list_foreach(&data_vb->vb_group, vb) {
210  total_in += vb->vb_length;
211  available_out += BrotliEncoderMaxCompressedSize(vb->vb_length);
212  }
213 
214  MEM(out_vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
215  MEM(fr_value_box_mem_alloc(out_vb, &out_buff, out_vb, NULL, available_out, false) == 0);
216 
217  pool = brotli_pool_get();
218  MEM(state = BrotliEncoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
219 
220  BrotliEncoderSetParameter(state, BROTLI_PARAM_MODE, inst->compress.mode);
221  BrotliEncoderSetParameter(state, BROTLI_PARAM_QUALITY, inst->compress.quality);
222  BrotliEncoderSetParameter(state, BROTLI_PARAM_LGWIN, inst->compress.window_bits);
223  if (inst->compress.block_bits_is_set) BrotliEncoderSetParameter(state, BROTLI_PARAM_LGBLOCK, inst->compress.block_bits);
224  BrotliEncoderSetParameter(state, BROTLI_PARAM_LARGE_WINDOW, inst->large_window ? BROTLI_TRUE : BROTLI_FALSE);
225  BrotliEncoderSetParameter(state, BROTLI_PARAM_SIZE_HINT, total_in);
226  /*
227  * Loop over all the input data and ingest it into brotli
228  * which will add it to an internal buffer (hopefully
229  * allocated, in our thread local pool).
230  */
231  {
232  fr_value_box_list_foreach(&data_vb->vb_group, vb) {
233  size_t available_in = vb->vb_length;
234  const uint8_t *next_in = vb->vb_octets;
235  bool more = fr_value_box_list_next(&data_vb->vb_group, vb) != NULL;
236 
237  /*
238  * In reality this loop is probably unnecessary,
239  * but the brotli docs state:
240  *
241  * "client should repeat BROTLI_OPERATION_FINISH operation until available_in becomes 0,
242  * and BrotliEncoderHasMoreOutput returns BROTLI_FALSE"
243  */
244  do {
245  BROTLI_BOOL bret;
246 
247  bret = BrotliEncoderCompressStream(state,
248  more ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH,
249  &available_in, &next_in, &available_out, &out_buff, &total_out);
250  if (bret == BROTLI_FALSE) {
251  fr_assert_msg(0, "BrotliEncoderCompressStream returned false, this shouldn't happen");
252  RERROR("BrotliEncoderCompressStream failed");
253  ret = XLAT_ACTION_FAIL;
254  goto finish;
255  }
256  } while (more && (available_in > 0) && (BrotliEncoderHasMoreOutput(state) == BROTLI_FALSE));
257 
258  /*
259  * There's no reason brotli wouldn't consume the complete
260  * buffer on BROTLI_OPERATION_PROCESS.
261  */
262  fr_assert(available_in == 0);
263  }
264  }
265 
266  /*
267  * Realloc to the correct size if necessary
268  */
269  if (available_out != 0) MEM(fr_value_box_mem_realloc(out_vb, NULL, out_vb, total_out) == 0);
270 
271  fr_dcursor_insert(out, out_vb);
272 
273 finish:
274  BrotliEncoderDestroyInstance(state);
275  talloc_free_children(pool);
276 
277  return ret;
278 }
279 
281  { .required = true, .single = true, .type = FR_TYPE_OCTETS },
283 };
284 
285 /** Decompress a brotli string
286  *
287  * Example:
288 @verbatim
289 %brotli.decompress(<input>) == <decompressed data>
290 @endverbatim
291  *
292  * @ingroup xlat_functions
293  */
295  xlat_ctx_t const *xctx,
296  request_t *request, fr_value_box_list_t *args)
297 {
299  fr_value_box_t const *data_vb;
300 
301  BrotliDecoderState *state;
302  TALLOC_CTX *pool;
303 
304  fr_value_box_t *out_vb;
305 
306  size_t total_in;
307  size_t available_out;
308  size_t total_out = 0;
309 
310  uint8_t const *in_buff;
311  uint8_t *out_buff;
312 
314 
315  XLAT_ARGS(args, &data_vb);
316 
317  pool = brotli_pool_get();
318  MEM(state = BrotliDecoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
319 
320  MEM(out_vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
321  total_in = data_vb->vb_length;
322  in_buff = data_vb->vb_octets;
323  available_out = (data_vb->vb_length * 2);
324 
325  MEM(fr_value_box_mem_alloc(out_vb, &out_buff, out_vb, NULL, available_out, false) == 0);
326  pool = brotli_pool_get();
327  MEM(state = BrotliDecoderCreateInstance(brotli_talloc_alloc, brotli_talloc_free, pool));
328 
329  BrotliDecoderSetParameter(state, BROTLI_DECODER_PARAM_LARGE_WINDOW, inst->large_window ? BROTLI_TRUE : BROTLI_FALSE);
330 
331  for (;;) {
332  switch (BrotliDecoderDecompressStream(state, &total_in, &in_buff, &available_out, &out_buff, &total_out)) {
333  default:
334  case BROTLI_DECODER_RESULT_ERROR:
335  {
336  BrotliDecoderErrorCode error = BrotliDecoderGetErrorCode(state);
337  REDEBUG("Decompressing brotli data failed - %s", BrotliDecoderErrorString(error));
338  ret = XLAT_ACTION_FAIL;
339  goto finish;
340  }
341 
342  case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
343  REDEBUG("Incomplete or truncated brotli data provided. Decompressor wants more input...");
344  ret = XLAT_ACTION_FAIL;
345  goto finish;
346 
347  case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
348  {
349  size_t extra = out_vb->vb_length;
350 
351  /*
352  * Stop runaway brotli decodings...
353  */
354  if ((out_vb->vb_length + extra) > inst->decompress.max_size) {
355  RERROR("Decompressed data exceeds maximum size of %zu", inst->decompress.max_size);
356  ret = XLAT_ACTION_FAIL;
357  goto finish;
358  }
359 
360  MEM(fr_value_box_mem_realloc(out_vb, &out_buff, out_vb, out_vb->vb_length + extra) == 0);
361  available_out += extra;
362  }
363  continue; /* Again! */
364 
365  case BROTLI_DECODER_RESULT_SUCCESS:
366  if (BrotliDecoderIsFinished(state) == BROTLI_TRUE) {
367  MEM(fr_value_box_mem_realloc(out_vb, &out_buff, out_vb, total_out) == 0);
368  fr_dcursor_insert(out, out_vb);
369  goto finish;
370  }
371  continue; /* Again! */
372  }
373  }
374 
375 finish:
376  BrotliDecoderDestroyInstance(state);
377  talloc_free_children(pool);
378 
379  return ret;
380 }
381 
382 static int mod_bootstrap(module_inst_ctx_t const *mctx)
383 {
384  xlat_t *xlat;
385 
386  if (unlikely((xlat = xlat_func_register_module(NULL, mctx, "compress", brotli_xlat_compress,
387  FR_TYPE_OCTETS)) == NULL)) return -1;
389 
390  if (unlikely((xlat = xlat_func_register_module(NULL, mctx, "decompress", brotli_xlat_decompress,
391  FR_TYPE_OCTETS)) == NULL)) return -1;
393 
394  return 0;
395 }
396 
397 /*
398  * The module name should be the only globally exported symbol.
399  * That is, everything else should be 'static'.
400  *
401  * If the module needs to temporarily modify it's instantiation
402  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
403  * The server will then take care of ensuring that the module
404  * is single-threaded.
405  */
406 extern module_rlm_t rlm_brotli;
408  .common = {
409  .magic = MODULE_MAGIC_INIT,
410  .name = "brotli",
411  .inst_size = sizeof(rlm_brotli_t),
412  .bootstrap = mod_bootstrap,
414  }
415 };
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:444
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define STRINGIFY(x)
Definition: build.h:195
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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:1474
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:626
#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:563
Common header for all CONF_* types.
Definition: cf_priv.h:49
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition: dcursor.h:434
#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:208
Test enumeration values.
Definition: dict_test.h:92
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
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:294
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:185
#define RERROR(fmt,...)
Definition: log.h:298
talloc_free(reap)
@ FR_TYPE_INT32
32 Bit signed integer.
Definition: merged_model.c:105
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
Definition: merged_model.c:115
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned char uint8_t
Definition: merged_model.c:30
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
static const conf_parser_t config[]
Definition: base.c:188
#define REDEBUG(fmt,...)
Definition: radclient.h:52
static const conf_parser_t module_decompress_config[]
Definition: rlm_brotli.c:81
static int block_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition: rlm_brotli.c:154
module_rlm_t rlm_brotli
Definition: rlm_brotli.c:407
static void brotli_talloc_free(UNUSED void *uctx, void *to_free)
Definition: rlm_brotli.c:115
static const conf_parser_t module_compress_config[]
Definition: rlm_brotli.c:72
bool large_window
non-standard "large", window size.
Definition: rlm_brotli.c:62
int quality
Default quality to use when compressing data.
Definition: rlm_brotli.c:49
static xlat_arg_parser_t const brotli_xlat_compress_args[]
Definition: rlm_brotli.c:171
fr_table_num_sorted_t const brotli_mode[]
Definition: rlm_brotli.c:65
static xlat_arg_parser_t const brotli_xlat_decompress_args[]
Definition: rlm_brotli.c:280
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: rlm_brotli.c:382
static int window_bits_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition: rlm_brotli.c:137
rlm_brotli_decompress_t decompress
Decompression settings.
Definition: rlm_brotli.c:61
static int quality_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Definition: rlm_brotli.c:120
size_t max_size
Maximum amount we attempt to decode.
Definition: rlm_brotli.c:56
size_t brotli_mode_len
Definition: rlm_brotli.c:70
BrotliEncoderMode mode
Default mode to use when compressing data.
Definition: rlm_brotli.c:48
bool block_bits_is_set
Whether block_bits has been set.
Definition: rlm_brotli.c:52
int block_bits
Default block bits to use when compressing data.
Definition: rlm_brotli.c:51
int window_bits
Default window bits to use when compressing data.
Definition: rlm_brotli.c:50
rlm_brotli_compress_t compress
Compression settings.
Definition: rlm_brotli.c:60
static _Thread_local TALLOC_CTX * brotli_pool
Thread-local pool for brotli state.
Definition: rlm_brotli.c:94
static const conf_parser_t module_config[]
Definition: rlm_brotli.c:86
static TALLOC_CTX * brotli_pool_get(void)
Definition: rlm_brotli.c:97
static void * brotli_talloc_alloc(void *uctx, size_t size)
Definition: rlm_brotli.c:109
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
Functions which we wish were included in the standard talloc distribution.
#define talloc_get_type_abort_const
Definition: talloc.h:270
bool required
Argument must be present, and non-empty.
Definition: xlat.h:146
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition: xlat.h:365
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
Definition for a single argument consumend by an xlat function.
Definition: xlat.h:145
static fr_slen_t parent
Definition: pair.h:844
#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:4320
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:4353
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition: value.h:608
#define fr_value_box_list_foreach(_list_head, _iter)
Definition: value.h:199
static size_t char ** out
Definition: value.h:984
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition: xlat_ctx.h:45
An xlat calling ctx.
Definition: xlat_ctx.h:42
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition: xlat_func.c:360
xlat_t * xlat_func_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function for a module.
Definition: xlat_func.c:274