The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sqlippool.c
Go to the documentation of this file.
1/*
2 * This program 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: 874af11c30ed5c65b4d5907cae9a6c243bfb35e3 $
19 * @file rlm_sqlippool.c
20 * @brief Allocates an IPv4 address from pools stored in SQL.
21 *
22 * @copyright 2002 Globe.Net Communications Limited
23 * @copyright 2006 The FreeRADIUS server project
24 * @copyright 2006 Suntel Communications
25 */
26RCSID("$Id: 874af11c30ed5c65b4d5907cae9a6c243bfb35e3 $")
27
28#define LOG_PREFIX inst->name
29
30#include <rlm_sql.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/radius/radius.h>
33#include <freeradius-devel/unlang/function.h>
34#include <freeradius-devel/unlang/action.h>
35#include <freeradius-devel/unlang/module.h>
36
37#include <ctype.h>
38
39/*
40 * Define a structure for our module configuration.
41 */
42typedef struct {
43 char const *name;
44 char const *sql_name;
45
46 rlm_sql_t const *sql;
48
49/** Call environment used by module alloc method
50 */
51typedef struct {
52 fr_value_box_t pool_name; //!< Name of pool address will be allocated from.
53 tmpl_t *pool_name_tmpl; //!< Tmpl used to expand pool_name
54 fr_value_box_t requested_address; //!< IP address being requested by client.
55 tmpl_t *allocated_address_attr; //!< Attribute to populate with allocated IP.
56 fr_value_box_t allocated_address; //!< Existing value for allocated IP.
57 fr_value_box_t begin; //!< SQL query to begin transaction.
58 tmpl_t *existing; //!< tmpl to expand as query for finding the existing IP.
59 tmpl_t *requested; //!< tmpl to expand as query for finding the requested IP.
60 tmpl_t *find; //!< tmpl to expand as query for finding an unused IP.
61 tmpl_t *update; //!< tmpl to expand as query for updating the found IP.
62 tmpl_t *pool_check; //!< tmpl to expand as query for checking for existence of the pool.
63 fr_value_box_t commit; //!< SQL query to commit transaction.
65
66/** Call environment used by all other module methods
67 */
68typedef struct {
69 fr_value_box_t free; //!< SQL query to clear other offered IPs. Only used in "update" method.
70 fr_value_box_t update; //!< SQL query to update an IP record.
72
73/** Current step in IP allocation state machine
74 */
75typedef enum {
76 IPPOOL_ALLOC_BEGIN_RUN, //!< Run the "begin" query
77 IPPOOL_ALLOC_EXISTING, //!< Expanding the "existing" query
78 IPPOOL_ALLOC_EXISTING_RUN, //!< Run the "existing" query
79 IPPOOL_ALLOC_REQUESTED, //!< Expanding the "requested" query
80 IPPOOL_ALLOC_REQUESTED_RUN, //!< Run the "requested" query
81 IPPOOL_ALLOC_FIND, //!< Expanding the "find" query
82 IPPOOL_ALLOC_FIND_RUN, //!< Run the "find" query
83 IPPOOL_ALLOC_NO_ADDRESS, //!< No address was found
84 IPPOOL_ALLOC_POOL_CHECK, //!< Expanding the "pool_check" query
85 IPPOOL_ALLOC_POOL_CHECK_RUN, //!< Run the "pool_check" query
86 IPPOOL_ALLOC_MAKE_PAIR, //!< Make the pair.
87 IPPOOL_ALLOC_UPDATE, //!< Expanding the "update" query
88 IPPOOL_ALLOC_UPDATE_RUN, //!< Run the "update" query
89 IPPOOL_ALLOC_COMMIT_RUN, //!< RUn the "commit" query
91
92/** Resume context for IP allocation
93 */
94typedef struct {
95 request_t *request; //!< Current request.
96 ippool_alloc_status_t status; //!< Status of the allocation.
97 ippool_alloc_call_env_t *env; //!< Call environment for the allocation.
98 trunk_t *trunk; //!< Trunk connection for queries.
99 rlm_sql_t const *sql; //!< SQL module instance.
100 fr_value_box_list_t values; //!< Where to put the expanded queries ready for execution.
101 fr_value_box_t *query; //!< Current query being run.
102 fr_sql_query_t *query_ctx; //!< Query context for allocation queries.
103 rlm_rcode_t rcode; //!< Result code to return after running "commit".
105
106/** Resume context for IP update / release
107 */
108typedef struct {
109 request_t *request; //!< Current request.
110 ippool_common_call_env_t *env; //!< Call environment for the update.
111 rlm_sql_t const *sql; //!< SQL module instance.
112 fr_sql_query_t *query_ctx; //!< Query context for allocation queries.
114
116 { FR_CONF_OFFSET("sql_module_instance", rlm_sqlippool_t, sql_name), .dflt = "sql" },
117
119};
120
121static int _sql_escape_uxtx_free(void *uctx)
122{
123 return talloc_free(uctx);
124}
125
126static void *sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
127{
128 static _Thread_local rlm_sql_escape_uctx_t *t_ctx;
129
130 if (unlikely(t_ctx == NULL)) {
132
133 MEM(ctx = talloc_zero(NULL, rlm_sql_escape_uctx_t));
135 }
136 t_ctx->sql = uctx;
137
138 return t_ctx;
139}
140
141/*
142 * Process the results of an SQL query expected to return a single row
143 */
144static int sqlippool_result_process(char *out, int outlen, fr_sql_query_t *query_ctx)
145{
146 unlang_result_t p_result;
147 int rlen, retval = 0;
148 rlm_sql_row_t row;
149 request_t *request = query_ctx->request;
150
151 *out = '\0';
152
153 query_ctx->inst->fetch_row(&p_result, query_ctx->request, query_ctx);
154 if (query_ctx->rcode < 0) {
155 REDEBUG("Failed fetching query_result");
156 goto finish;
157 }
158
159 row = query_ctx->row;
160 if (!row) {
161 RDEBUG2("SQL query did not return any results");
162 goto finish;
163 }
164
165 if (!row[0]) {
166 REDEBUG("The first column of the result was NULL");
167 goto finish;
168 }
169
170 rlen = strlen(row[0]);
171 if (rlen >= outlen) {
172 REDEBUG("The first column of the result was too long (%d)", rlen);
173 goto finish;
174 }
175
176 strcpy(out, row[0]);
177 retval = rlen;
178
179finish:
180 query_ctx->inst->driver->sql_finish_select_query(query_ctx, &query_ctx->inst->config);
181 return retval;
182}
183
184/*
185 * Do any per-module initialization that is separate to each
186 * configured instance of the module. e.g. set up connections
187 * to external databases, read configuration files, set up
188 * dictionary entries, etc.
189 *
190 * If configuration information is given in the config section
191 * that must be referenced in later calls, store a handle to it
192 * in *instance otherwise put a null pointer there.
193 */
194static int mod_instantiate(module_inst_ctx_t const *mctx)
195{
197 rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlippool_t);
198 CONF_SECTION *conf = mctx->mi->conf;
199
200 inst->name = talloc_asprintf(inst, "%s - %s", mctx->mi->name, inst->sql_name);
201
202 sql = module_rlm_static_by_name(NULL, inst->sql_name);
203 if (!sql) {
204 cf_log_err(conf, "failed to find sql instance named %s",
205 inst->sql_name);
206 return -1;
207 }
208
209 inst->sql = (rlm_sql_t *) sql->data;
210
211 if (strcmp(talloc_get_name(inst->sql), "rlm_sql_t") != 0) {
212 cf_log_err(conf, "Module \"%s\" is not an instance of the rlm_sql module",
213 inst->sql_name);
214 return -1;
215 }
216
217 return 0;
218}
219
220/** Release SQL pool connections when alloc context is freed.
221 */
223{
224 if (!to_free->sql->sql_escape_arg) (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
225 return 0;
226}
227
228#define REPEAT_MOD_ALLOC_RESUME if (unlang_module_yield(request, mod_alloc_resume, NULL, 0, mctx->rctx) == UNLANG_ACTION_FAIL) RETURN_UNLANG_FAIL
229#define SUBMIT_QUERY(_query_str, _new_status, _type, _function) do { \
230 alloc_ctx->status = _new_status; \
231 REPEAT_MOD_ALLOC_RESUME; \
232 query_ctx->query_str = _query_str; \
233 query_ctx->type = _type; \
234 query_ctx->status = SQL_QUERY_PREPARED; \
235 alloc_ctx->query = query; \
236 return unlang_function_push_with_result(p_result, request, sql->_function, NULL, NULL, 0, UNLANG_SUB_FRAME, query_ctx); \
237} while (0)
238
239/** Resume function called after each IP allocation query is expanded
240 *
241 * Executes the query and, if appropriate, pushes the next tmpl for expansion
242 *
243 * Following the final (successful) query, the destination attribute is populated.
244 *
245 * @param p_result Result of IP allocation.
246 * @param mctx Current allocation context.
247 * @param request Current request.
248 * @return One of the UNLANG_ACTION_* values.
249 */
251{
252 ippool_alloc_ctx_t *alloc_ctx = talloc_get_type_abort(mctx->rctx, ippool_alloc_ctx_t);
253 ippool_alloc_call_env_t *env = alloc_ctx->env;
254 int allocation_len = 0;
255 char allocation[FR_MAX_STRING_LEN];
256 rlm_sql_t const *sql = alloc_ctx->sql;
257 fr_value_box_t *query = fr_value_box_list_pop_head(&alloc_ctx->values);
258 fr_sql_query_t *query_ctx = alloc_ctx->query_ctx;
259
260 /*
261 * If a previous async call returned one of the "failure" results just return.
262 */
263 switch (p_result->rcode) {
266
267 default:
268 break;
269 }
270
271 switch (alloc_ctx->status) {
273 if ((env->begin.type == FR_TYPE_STRING) &&
274 env->begin.vb_length) sql->driver->sql_finish_query(query_ctx, &query_ctx->inst->config);
275
276 /*
277 * The first call of this function will always land here, whether or not a "begin" query is actually run.
278 *
279 * Having (possibly) run the "begin" query, establish which tmpl needs expanding
280 *
281 * If there is a query for finding the existing IP expand that first
282 */
283 if (env->existing) {
284 alloc_ctx->status = IPPOOL_ALLOC_EXISTING;
286 if (unlang_tmpl_push(alloc_ctx, NULL, &alloc_ctx->values, request, env->existing, NULL, UNLANG_SUB_FRAME) < 0) {
287 error:
288 talloc_free(alloc_ctx);
290 }
292 }
293 goto expand_requested;
294
296 if (query && query->vb_length) SUBMIT_QUERY(query->vb_strvalue, IPPOOL_ALLOC_EXISTING_RUN, SQL_QUERY_SELECT, select);
297 goto expand_requested;
298
300 TALLOC_FREE(alloc_ctx->query);
301 if (query_ctx->rcode != RLM_SQL_OK) goto error;
302
303 allocation_len = sqlippool_result_process(allocation, sizeof(allocation), query_ctx);
304 if (allocation_len > 0) goto make_pair;
305
306 /*
307 * If there's a requested address and associated query, expand that
308 */
309 expand_requested:
310 if (env->requested && (env->requested_address.type != FR_TYPE_NULL)) {
311 alloc_ctx->status = IPPOOL_ALLOC_REQUESTED;
313 if (unlang_tmpl_push(alloc_ctx, NULL, &alloc_ctx->values, request, env->requested, NULL, UNLANG_SUB_FRAME) < 0) goto error;
315 }
316 goto expand_find;
317
319 if (query && query->vb_length) SUBMIT_QUERY(query->vb_strvalue, IPPOOL_ALLOC_REQUESTED_RUN, SQL_QUERY_SELECT, select);
320
321 goto expand_find;
322
324 TALLOC_FREE(alloc_ctx->query);
325 if (query_ctx->rcode != RLM_SQL_OK) goto error;
326
327 allocation_len = sqlippool_result_process(allocation, sizeof(allocation), query_ctx);
328 if (allocation_len > 0) goto make_pair;
329
330 expand_find:
331 /*
332 * Neither "existing" nor "requested" found an address, expand "find" query
333 */
334 alloc_ctx->status = IPPOOL_ALLOC_FIND;
336 if (unlang_tmpl_push(alloc_ctx, NULL, &alloc_ctx->values, request, env->find, NULL, UNLANG_SUB_FRAME) < 0) goto error;
338
340 if (!query) RETURN_UNLANG_FAIL;
341 SUBMIT_QUERY(query->vb_strvalue, IPPOOL_ALLOC_FIND_RUN, SQL_QUERY_SELECT, select);
342
344 TALLOC_FREE(alloc_ctx->query);
345 if (query_ctx->rcode != RLM_SQL_OK) goto error;
346
347 allocation_len = sqlippool_result_process(allocation, sizeof(allocation), query_ctx);
348
349 if (allocation_len > 0) goto make_pair;
350
351 /*
352 * Nothing found
353 */
354 if ((env->commit.type == FR_TYPE_STRING) &&
355 env->commit.vb_length) SUBMIT_QUERY(env->commit.vb_strvalue, IPPOOL_ALLOC_NO_ADDRESS, SQL_QUERY_OTHER, query);
357
359 if ((env->commit.type == FR_TYPE_STRING) &&
360 env->commit.vb_length) sql->driver->sql_finish_query(query_ctx, &query_ctx->inst->config);
361
362 /*
363 * Should we perform pool-check?
364 */
365 if (env->pool_check) {
366 alloc_ctx->status = IPPOOL_ALLOC_POOL_CHECK;
368 if (unlang_tmpl_push(alloc_ctx, NULL, &alloc_ctx->values, request, env->pool_check, NULL, UNLANG_SUB_FRAME) < 0) goto error;
370 }
371 no_address:
372 RWDEBUG("IP address could not be allocated");
374
376 {
377 tmpl_t ip_rhs;
378 map_t ip_map;
379
380 make_pair:
381 /*
382 * See if we can create the VP from the returned data. If not,
383 * error out. If so, add it to the list.
384 */
385 ip_map = (map_t) {
387 .op = T_OP_SET,
388 .rhs = &ip_rhs
389 };
390
391 tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0, NULL);
392 fr_value_box_bstrndup_shallow(&ip_map.rhs->data.literal, NULL, allocation, allocation_len, false);
393 if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) {
394 alloc_ctx->rcode = RLM_MODULE_FAIL;
395
396 REDEBUG("Invalid IP address [%s] returned from database query.", allocation);
397 goto finish;
398 }
399
400 RDEBUG2("Allocated IP %s", allocation);
401 alloc_ctx->rcode = RLM_MODULE_UPDATED;
402
403 /*
404 * If we have an update query expand it
405 */
406 if (env->update) {
407 alloc_ctx->status = IPPOOL_ALLOC_UPDATE;
409 if (unlang_tmpl_push(alloc_ctx, NULL, &alloc_ctx->values, request, env->update, NULL, UNLANG_SUB_FRAME) < 0) goto error;
411 }
412
413 goto finish;
414 }
415
417 /*
418 * Ok, so the allocate-find query found nothing ...
419 * Let's check if the pool exists at all
420 */
421 if (query && query->vb_length) SUBMIT_QUERY(query->vb_strvalue, IPPOOL_ALLOC_POOL_CHECK_RUN, SQL_QUERY_SELECT, select);
422 goto no_address;
423
425 TALLOC_FREE(alloc_ctx->query);
426 allocation_len = sqlippool_result_process(allocation, sizeof(allocation), query_ctx);
427
428 if (allocation_len) {
429 /*
430 * Pool exists after all... So,
431 * the failure to allocate the IP
432 * address was most likely due to
433 * the depletion of the pool. In
434 * that case, we should return
435 * NOTFOUND
436 */
437 RWDEBUG("Pool \"%pV\" appears to be full", &env->pool_name);
439 }
440
441 /*
442 * Pool doesn't exist in the table. It
443 * may be handled by some other instance of
444 * sqlippool, so we should just ignore this
445 * allocation failure and return NOOP
446 */
447 RWDEBUG("IP address could not be allocated as no pool exists with the name \"%pV\"",
448 &env->pool_name);
450
452 if (query && query->vb_length) SUBMIT_QUERY(query->vb_strvalue, IPPOOL_ALLOC_UPDATE_RUN, SQL_QUERY_OTHER, query);
453
454 goto finish;
455
457 TALLOC_FREE(alloc_ctx->query);
458 if (env->update) sql->driver->sql_finish_query(query_ctx, &query_ctx->inst->config);
459
460 finish:
461 if ((env->commit.type == FR_TYPE_STRING) &&
462 env->commit.vb_length) SUBMIT_QUERY(env->commit.vb_strvalue, IPPOOL_ALLOC_COMMIT_RUN, SQL_QUERY_OTHER, query);
463
465
467 {
468 rlm_rcode_t rcode = alloc_ctx->rcode;
469 talloc_free(alloc_ctx);
470 RETURN_UNLANG_RCODE(rcode);
471 }
472 }
473
474 /*
475 * All return paths are handled within the switch statement.
476 */
477 fr_assert(0);
479}
480
481/** Initiate the allocation of an IP address from the pool.
482 *
483 * Based on configured queries and attributes which exist, determines the first
484 * query tmpl to expand.
485 *
486 * @param p_result Result of the allocation (if it fails).
487 * @param mctx Module context.
488 * @param request Current request.
489 * @return One of the UNLANG_ACTION_* values.
490 */
491static unlang_action_t CC_HINT(nonnull) mod_alloc(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
492{
493 rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlippool_t);
494 ippool_alloc_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_alloc_call_env_t);
495 rlm_sql_t const *sql = inst->sql;
496 ippool_alloc_ctx_t *alloc_ctx = NULL;
497 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(sql->mi)->data, rlm_sql_thread_t);
498
499 /*
500 * If the allocated IP attribute already exists, do nothing
501 */
502 if (env->allocated_address.type) {
503 RDEBUG2("%s already exists (%pV)", env->allocated_address_attr->name, &env->allocated_address);
505 }
506
507 if (env->pool_name.type == FR_TYPE_NULL) {
508 RDEBUG2("No %s defined", env->pool_name_tmpl->name);
510 }
511
512 MEM(alloc_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ippool_alloc_ctx_t));
513 *alloc_ctx = (ippool_alloc_ctx_t) {
514 .env = env,
515 .trunk = thread->trunk,
516 .sql = inst->sql,
517 .request = request,
518 };
519 talloc_set_destructor(alloc_ctx, sqlippool_alloc_ctx_free);
520
521 /*
522 * Allocate a query_ctx which will be used for all queries in the allocation.
523 * Since they typically form an SQL transaction, they all need to be on the same
524 * connection, and use the same trunk request if using trunks.
525 */
526 MEM(alloc_ctx->query_ctx = sql->query_alloc(alloc_ctx, sql, request, thread->trunk, "", SQL_QUERY_OTHER));
527
528 fr_value_box_list_init(&alloc_ctx->values);
529 if (unlikely(unlang_module_yield(request, mod_alloc_resume, NULL, 0, alloc_ctx) != UNLANG_ACTION_YIELD)) {
530 talloc_free(alloc_ctx);
532 }
533
534 if ((env->begin.type == FR_TYPE_STRING) && env->begin.vb_length) {
535 alloc_ctx->query_ctx->query_str = env->begin.vb_strvalue;
536 return unlang_function_push_with_result(/* mod_alloc_resume looks at frame result */ p_result,
537 request,
538 sql->query,
539 NULL,
540 NULL, 0,
542 alloc_ctx->query_ctx);
543 }
544
546}
547
548/** Resume function called after mod_common "update" query has completed
549 */
551{
552 ippool_common_ctx_t *common_ctx = talloc_get_type_abort(mctx->rctx, ippool_common_ctx_t);
553 fr_sql_query_t *query_ctx = common_ctx->query_ctx;
554 rlm_sql_t const *sql = common_ctx->sql;
555 int affected = 0;
556
557 switch (p_result->rcode) {
560
561 default:
562 break;
563 }
564
565 affected = sql->driver->sql_affected_rows(query_ctx, &sql->config);
566
567 talloc_free(common_ctx);
568
569 if (affected > 0) RETURN_UNLANG_UPDATED;
571}
572
573/** Resume function called after mod_common "free" query has completed
574 */
576{
577 ippool_common_ctx_t *common_ctx = talloc_get_type_abort(mctx->rctx, ippool_common_ctx_t);
578 fr_sql_query_t *query_ctx = common_ctx->query_ctx;
579 rlm_sql_t const *sql = common_ctx->sql;
580
581 switch (p_result->rcode) {
584
585 default:
586 break;
587 }
588 if (common_ctx->env->update.type != FR_TYPE_STRING) RETURN_UNLANG_NOOP;
589
590 sql->driver->sql_finish_query(query_ctx, &sql->config);
591
592 if (unlikely(unlang_module_yield(request, mod_common_update_resume, NULL, 0, common_ctx) != UNLANG_ACTION_YIELD)) {
593 talloc_free(common_ctx);
595 }
596
597 common_ctx->query_ctx->query_str = common_ctx->env->update.vb_strvalue;
598 query_ctx->status = SQL_QUERY_PREPARED;
599 return unlang_function_push_with_result(/* mod_common_update_resume uses frame rcode */p_result,
600 request,
601 sql->query,
602 NULL,
603 NULL, 0,
605 query_ctx);
606}
607
608/** Common function used by module methods which perform an optional "free" then "update"
609 * - update
610 * - release
611 * - bulk_release
612 * - mark
613 */
614static unlang_action_t CC_HINT(nonnull) mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
615{
616 rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlippool_t);
617 ippool_common_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_common_call_env_t);
618 rlm_sql_t const *sql = inst->sql;
619 rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(sql->mi)->data, rlm_sql_thread_t);
620 ippool_common_ctx_t *common_ctx = NULL;
621
622 if ((env->free.type != FR_TYPE_STRING) && (env->update.type != FR_TYPE_STRING)) RETURN_UNLANG_NOOP;
623
624 MEM(common_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ippool_common_ctx_t));
625 *common_ctx = (ippool_common_ctx_t) {
626 .request = request,
627 .env = env,
628 .sql = sql,
629 };
630
631 MEM(common_ctx->query_ctx = sql->query_alloc(common_ctx, sql, request, thread->trunk, "", SQL_QUERY_OTHER));
632
633 /*
634 * An optional query which can be used to tidy up before updates
635 * primarily intended for multi-server setups sharing a common database
636 * allowing for tidy up of multiple offered addresses in a DHCP context.
637 */
638 if (env->free.type == FR_TYPE_STRING) {
639 common_ctx->query_ctx->query_str = env->free.vb_strvalue;
640 if (unlikely(unlang_module_yield(request, mod_common_free_resume, NULL, 0, common_ctx) != UNLANG_ACTION_YIELD)) {
641 talloc_free(common_ctx);
643 }
644 return unlang_function_push_with_result(/* mod_common_free_resume looks at frame result */ NULL,
645 request,
646 sql->query,
647 NULL,
648 NULL, 0,
650 common_ctx->query_ctx);
651 }
652
653 common_ctx->query_ctx->query_str = env->update.vb_strvalue;
654
655 if (unlikely(unlang_module_yield(request, mod_common_update_resume, NULL, 0, common_ctx) != UNLANG_ACTION_YIELD)) {
656 talloc_free(common_ctx);
658 }
659 return unlang_function_push_with_result(/* mod_common_update_resume looks at frame result */ p_result,
660 request,
661 sql->query,
662 NULL,
663 NULL, 0,
665 common_ctx->query_ctx);
666}
667
668/** Call SQL module box_escape_func to escape tainted values
669 */
670static int sqlippool_box_escape(fr_value_box_t *vb, void *uctx)
671{
672 rlm_sql_escape_uctx_t *ctx = talloc_get_type_abort(uctx, rlm_sql_escape_uctx_t);
673
674 /*
675 * @todo - this function both checks vb->safe_for, AND sets vb->safe_for on escaping. :(
676 */
677 return ctx->sql->box_escape.func(vb, uctx);
678}
679
680/** Custom parser for sqlippool call env
681 *
682 * Needed as the escape function needs to reference
683 * the correct instance of the SQL module since escaping functions
684 * are dependent on the driver used by a given module instance.
685 */
686static int sqlippool_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
687 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
688{
691 rlm_sql_t const *sql;
692 tmpl_t *parsed_tmpl;
693 CONF_PAIR const *to_parse = cf_item_to_pair(ci);
694 tmpl_rules_t our_rules = *t_rules;
695
696 /*
697 * Lookup the sql module instance.
698 */
699 sql_inst = module_rlm_static_by_name(NULL, inst->sql_name);
700 if (!sql_inst) return -1;
701 sql = talloc_get_type_abort(sql_inst->data, rlm_sql_t);
702
703 /*
704 * Set the sql module instance data as the uctx for escaping
705 * and use the same "safe_for" as the sql module.
706 */
707 our_rules.escape.uctx.func.uctx = sql;
710
711 if (tmpl_afrom_substr(ctx, &parsed_tmpl,
714 &our_rules) < 0) return -1;
715 *(void **)out = parsed_tmpl;
716 return 0;
717}
718
719#define QUERY_ESCAPE .pair.escape = { \
720 .box_escape = { \
721 .func = sqlippool_box_escape, \
722 .safe_for = FR_VALUE_BOX_SAFE_FOR_NONE, \
723 .always_escape = true, \
724 }, \
725 .mode = TMPL_ESCAPE_PRE_CONCAT, \
726 .uctx = { .func = { .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC }, \
727}, .pair.func = sqlippool_call_env_parse
728
731 .env = (call_env_parser_t[]) {
733 ippool_alloc_call_env_t, pool_name, pool_name_tmpl),
734 .pair.dflt = "control.IP-Pool.Name", .pair.dflt_quote = T_BARE_WORD },
736 ippool_alloc_call_env_t, requested_address) },
737 { FR_CALL_ENV_PARSE_OFFSET("allocated_address_attr", FR_TYPE_VOID,
739 ippool_alloc_call_env_t, allocated_address, allocated_address_attr) },
742 .pair.dflt = "START TRANSACTION", .pair.dflt_quote = T_SINGLE_QUOTED_STRING },
755 .pair.dflt = "COMMIT", .pair.dflt_quote = T_SINGLE_QUOTED_STRING },
757 }
758};
759
770
779
788
797
798/*
799 * The module name should be the only globally exported symbol.
800 * That is, everything else should be 'static'.
801 *
802 * If the module needs to temporarily modify it's instantiation
803 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
804 * The server will then take care of ensuring that the module
805 * is single-threaded.
806 */
809 .common = {
810 .magic = MODULE_MAGIC_INIT,
811 .name = "sqlippool",
812 .inst_size = sizeof(rlm_sqlippool_t),
814 .instantiate = mod_instantiate
815 },
816 .method_group = {
817 .bindings = (module_method_binding_t[]){
818 /*
819 * RADIUS specific
820 */
821 { .section = SECTION_NAME("recv", "Access-Request"), .method = mod_alloc, .method_env = &sqlippool_alloc_method_env },
822 { .section = SECTION_NAME("accounting", "Start"), .method = mod_common, .method_env = &sqlippool_update_method_env },
823 { .section = SECTION_NAME("accounting", "Alive"), .method = mod_common, .method_env = &sqlippool_update_method_env },
824 { .section = SECTION_NAME("accounting", "Stop"), .method = mod_common, .method_env = &sqlippool_release_method_env },
825 { .section = SECTION_NAME("accounting", "Accounting-On"), .method = mod_common, .method_env = &sqlippool_bulk_release_method_env },
826 { .section = SECTION_NAME("accounting", "Accounting-Off"), .method = mod_common, .method_env = &sqlippool_bulk_release_method_env },
827
828 /*
829 * DHCPv4
830 */
831 { .section = SECTION_NAME("recv", "Discover"), .method = mod_alloc, .method_env = &sqlippool_alloc_method_env },
832 { .section = SECTION_NAME("recv", "Request"), .method = mod_common, .method_env = &sqlippool_update_method_env },
833 { .section = SECTION_NAME("recv", "Confirm"), .method = mod_common, .method_env = &sqlippool_update_method_env },
834 { .section = SECTION_NAME("recv", "Rebind"), .method = mod_common, .method_env = &sqlippool_update_method_env },
835 { .section = SECTION_NAME("recv", "Renew"), .method = mod_common, .method_env = &sqlippool_update_method_env },
836 { .section = SECTION_NAME("recv", "Release"), .method = mod_common, .method_env = &sqlippool_release_method_env },
837 { .section = SECTION_NAME("recv", "Decline"), .method = mod_common, .method_env = &sqlippool_mark_method_env },
838
839 /*
840 * Generic
841 */
842 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_common, .method_env = &sqlippool_update_method_env },
843 { .section = SECTION_NAME("send", CF_IDENT_ANY),.method = mod_alloc, .method_env = &sqlippool_alloc_method_env },
844
845 /*
846 * Named methods matching module operations
847 */
848 { .section = SECTION_NAME("allocate", NULL), .method = mod_alloc, .method_env = &sqlippool_alloc_method_env },
849 { .section = SECTION_NAME("update", NULL), .method = mod_common, .method_env = &sqlippool_update_method_env },
850 { .section = SECTION_NAME("renew", NULL), .method = mod_common, .method_env = &sqlippool_update_method_env },
851 { .section = SECTION_NAME("release", NULL), .method = mod_common, .method_env = &sqlippool_release_method_env },
852 { .section = SECTION_NAME("bulk-release", NULL), .method = mod_common, .method_env = &sqlippool_bulk_release_method_env },
853 { .section = SECTION_NAME("mark", NULL),.method = mod_common,.method_env = &sqlippool_mark_method_env },
854
856 }
857 }
858};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
strcpy(log_entry->msg, buffer)
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:220
#define RCSID(id)
Definition build.h:512
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
Definition call_env.h:365
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition call_env.h:76
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
Definition call_env.h:86
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
Definition call_env.h:92
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition call_env.h:389
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#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:280
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:72
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1625
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:665
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1581
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define CF_IDENT_ANY
Definition cf_util.h:75
#define MEM(x)
Definition debug.h:46
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define unlang_function_push_with_result(_result_p, _request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack that produces a result.
Definition function.h:144
free(array)
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1685
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
#define RWDEBUG(fmt,...)
Definition log.h:373
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition map.c:1604
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1884
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VOID
User data.
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
module_instance_t * module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name)
Definition module_rlm.c:810
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
#define RETURN_UNLANG_UPDATED
Definition rcode.h:70
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:79
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:68
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Prototypes and functions for the SQL module.
trunk_t * trunk
Trunk connection for this thread.
Definition rlm_sql.h:114
fr_sql_query_status_t status
Status of the query.
Definition rlm_sql.h:144
rlm_sql_t const * inst
Module instance for this query.
Definition rlm_sql.h:137
char const * query_str
Query string to run.
Definition rlm_sql.h:142
request_t * request
Request this query relates to.
Definition rlm_sql.h:138
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ SQL_QUERY_SELECT
Definition rlm_sql.h:120
@ SQL_QUERY_OTHER
Definition rlm_sql.h:121
char ** rlm_sql_row_t
Definition rlm_sql.h:59
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:146
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:145
rlm_sql_t const * sql
Definition rlm_sql.h:197
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:128
fr_value_box_t requested_address
IP address being requested by client.
#define REPEAT_MOD_ALLOC_RESUME
tmpl_t * pool_check
tmpl to expand as query for checking for existence of the pool.
#define QUERY_ESCAPE
static const call_env_method_t sqlippool_bulk_release_method_env
static int _sql_escape_uxtx_free(void *uctx)
static int sqlippool_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Custom parser for sqlippool call env.
fr_value_box_t allocated_address
Existing value for allocated IP.
rlm_sql_t const * sql
fr_sql_query_t * query_ctx
Query context for allocation queries.
fr_value_box_t free
SQL query to clear other offered IPs. Only used in "update" method.
static unlang_action_t mod_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Common function used by module methods which perform an optional "free" then "update".
static unlang_action_t mod_alloc(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Initiate the allocation of an IP address from the pool.
fr_value_box_list_t values
Where to put the expanded queries ready for execution.
static const call_env_method_t sqlippool_release_method_env
rlm_rcode_t rcode
Result code to return after running "commit".
trunk_t * trunk
Trunk connection for queries.
static const call_env_method_t sqlippool_update_method_env
rlm_sql_t const * sql
SQL module instance.
static int sqlippool_result_process(char *out, int outlen, fr_sql_query_t *query_ctx)
char const * sql_name
request_t * request
Current request.
ippool_alloc_call_env_t * env
Call environment for the allocation.
ippool_common_call_env_t * env
Call environment for the update.
static const call_env_method_t sqlippool_alloc_method_env
static unlang_action_t mod_common_free_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after mod_common "free" query has completed.
fr_value_box_t * query
Current query being run.
request_t * request
Current request.
static const call_env_method_t sqlippool_mark_method_env
char const * name
static unlang_action_t mod_alloc_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resume function called after each IP allocation query is expanded.
static int sqlippool_box_escape(fr_value_box_t *vb, void *uctx)
Call SQL module box_escape_func to escape tainted values.
tmpl_t * pool_name_tmpl
Tmpl used to expand pool_name.
fr_sql_query_t * query_ctx
Query context for allocation queries.
fr_value_box_t commit
SQL query to commit transaction.
tmpl_t * existing
tmpl to expand as query for finding the existing IP.
static void * sql_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
#define SUBMIT_QUERY(_query_str, _new_status, _type, _function)
tmpl_t * requested
tmpl to expand as query for finding the requested IP.
fr_value_box_t pool_name
Name of pool address will be allocated from.
static int sqlippool_alloc_ctx_free(ippool_alloc_ctx_t *to_free)
Release SQL pool connections when alloc context is freed.
fr_value_box_t update
SQL query to update an IP record.
fr_value_box_t begin
SQL query to begin transaction.
tmpl_t * find
tmpl to expand as query for finding an unused IP.
static unlang_action_t mod_common_update_resume(unlang_result_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request)
Resume function called after mod_common "update" query has completed.
tmpl_t * allocated_address_attr
Attribute to populate with allocated IP.
ippool_alloc_status_t status
Status of the allocation.
module_rlm_t rlm_sqlippool
ippool_alloc_status_t
Current step in IP allocation state machine.
@ IPPOOL_ALLOC_POOL_CHECK_RUN
Run the "pool_check" query.
@ IPPOOL_ALLOC_FIND_RUN
Run the "find" query.
@ IPPOOL_ALLOC_MAKE_PAIR
Make the pair.
@ IPPOOL_ALLOC_POOL_CHECK
Expanding the "pool_check" query.
@ IPPOOL_ALLOC_REQUESTED
Expanding the "requested" query.
@ IPPOOL_ALLOC_COMMIT_RUN
RUn the "commit" query.
@ IPPOOL_ALLOC_REQUESTED_RUN
Run the "requested" query.
@ IPPOOL_ALLOC_EXISTING_RUN
Run the "existing" query.
@ IPPOOL_ALLOC_UPDATE_RUN
Run the "update" query.
@ IPPOOL_ALLOC_FIND
Expanding the "find" query.
@ IPPOOL_ALLOC_EXISTING
Expanding the "existing" query.
@ IPPOOL_ALLOC_UPDATE
Expanding the "update" query.
@ IPPOOL_ALLOC_NO_ADDRESS
No address was found.
@ IPPOOL_ALLOC_BEGIN_RUN
Run the "begin" query.
static int mod_instantiate(module_inst_ctx_t const *mctx)
static conf_parser_t module_config[]
tmpl_t * update
tmpl to expand as query for updating the found IP.
rlm_sql_t const * sql
SQL module instance.
Call environment used by module alloc method.
Resume context for IP allocation.
Call environment used by all other module methods.
Resume context for IP update / release.
#define FR_SBUFF_IN(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
struct map_s map_t
Definition map.h:33
char const * name
Instance name e.g. user_database.
Definition module.h:357
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * data
Thread specific instance data.
Definition module.h:374
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:503
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Module instance data.
Definition module.h:287
Named methods exported by a module.
Definition module.h:174
tmpl_escape_t escape
How escaping should be handled during evaluation.
Definition tmpl.h:353
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:351
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:138
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 *t_rules))
Convert an arbitrary string into a tmpl_t.
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules))
Initialise a tmpl without copying the input name string.
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
Value pair map.
Definition map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
int(* sql_affected_rows)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition rlm_sql.h:209
sql_rcode_t(* sql_finish_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition rlm_sql.h:217
sql_rcode_t(* sql_finish_select_query)(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Definition rlm_sql.h:218
unlang_function_with_result_t fetch_row
Definition rlm_sql.h:243
unlang_function_with_result_t query
Definition rlm_sql.h:241
fr_value_box_escape_t box_escape
Definition rlm_sql.h:238
fr_sql_query_t *(* query_alloc)(TALLOC_CTX *ctx, rlm_sql_t const *inst, request_t *request, trunk_t *trunk, char const *query_str, fr_sql_query_type_t type)
Definition rlm_sql.h:245
rlm_sql_config_t config
Definition rlm_sql.h:228
rlm_sql_driver_t const * driver
Driver's exported interface.
Definition rlm_sql.h:235
module_instance_t const * mi
Module instance data for thread lookups.
Definition rlm_sql.h:250
void * sql_escape_arg
Instance specific argument to be passed to escape function.
Definition rlm_sql.h:239
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_asprintf
Definition talloc.h:144
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:136
int unlang_tmpl_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args, bool top_frame)
Push a tmpl onto the stack for evaluation.
Definition tmpl.c:276
fr_value_box_escape_t box_escape
How to escape when returned from evaluation.
Definition tmpl_escape.h:81
struct tmpl_escape_t::@75 uctx
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_BARE_WORD
Definition token.h:118
@ T_OP_SET
Definition token.h:82
Main trunk management handle.
Definition trunk.c:215
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:611
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:4910
fr_value_box_safe_for_t safe_for
Definition value.h:678
#define FR_MAX_STRING_LEN
Definition value.h:30
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:162
int nonnull(2, 5))
fr_value_box_escape_func_t func
Definition value.h:677
static size_t char ** out
Definition value.h:1030