The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
compile.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
5 * (at 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: 780edef03df828ba86507c5c7bf4b291ac63e9db $
19 *
20 * @file unlang/compile.c
21 * @brief Functions to convert configuration sections into unlang structures.
22 *
23 * @copyright 2006-2016 The FreeRADIUS server project
24 */
25RCSID("$Id: 780edef03df828ba86507c5c7bf4b291ac63e9db $")
26
27#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
28
29#include <freeradius-devel/server/virtual_servers.h>
30
31#include <freeradius-devel/server/cf_file.h>
32#include <freeradius-devel/server/main_config.h>
33#include <freeradius-devel/server/map_proc.h>
34#include <freeradius-devel/server/modpriv.h>
35#include <freeradius-devel/server/module_rlm.h>
36
37
38#include <freeradius-devel/unlang/xlat_priv.h>
39
40#include "catch_priv.h"
41#include "call_priv.h"
42#include "caller_priv.h"
43#include "condition_priv.h"
44#include "foreach_priv.h"
45#include "load_balance_priv.h"
46#include "map_priv.h"
47#include "module_priv.h"
48#include "parallel_priv.h"
49#include "subrequest_priv.h"
50#include "switch_priv.h"
51#include "edit_priv.h"
52#include "timeout_priv.h"
53#include "limit_priv.h"
54#include "transaction_priv.h"
55#include "try_priv.h"
56#include "mod_action.h"
57
58#define UNLANG_IGNORE ((unlang_t *) -1)
59
60static unsigned int unlang_number = 1;
61
62/*
63 * For simplicity, this is just array[unlang_number]. Once we
64 * call unlang_thread_instantiate(), the "unlang_number" above MUST
65 * NOT change.
66 */
68
69/*
70 * Until we know how many instructions there are, we can't
71 * allocate an array. So we have to put the instructions into an
72 * RB tree.
73 */
75
76/* Here's where we recognize all of our keywords: first the rcodes, then the
77 * actions */
79 { L("..."), RLM_MODULE_NOT_SET },
80 { L("disallow"), RLM_MODULE_DISALLOW },
81 { L("fail"), RLM_MODULE_FAIL },
82 { L("handled"), RLM_MODULE_HANDLED },
83 { L("invalid"), RLM_MODULE_INVALID },
84 { L("noop"), RLM_MODULE_NOOP },
85 { L("notfound"), RLM_MODULE_NOTFOUND },
86 { L("ok"), RLM_MODULE_OK },
87 { L("reject"), RLM_MODULE_REJECT },
88 { L("timeout"), RLM_MODULE_TIMEOUT },
89 { L("updated"), RLM_MODULE_UPDATED }
90};
92
93typedef struct {
94 virtual_server_t const *vs; //!< Virtual server we're compiling in the context of.
95 ///< This shouldn't change during the compilation of
96 ///< a single unlang section.
97 char const *section_name1;
98 char const *section_name2;
102
103/*
104 * When we switch to a new unlang ctx, we use the new component
105 * name and number, but we use the CURRENT actions.
106 */
107static inline CC_HINT(always_inline)
109{
110 int i;
111
112 *dst = *src;
113
114 /*
115 * Ensure that none of the actions are RETRY.
116 */
117 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
118 if (dst->actions.actions[i] == MOD_ACTION_RETRY) dst->actions.actions[i] = MOD_PRIORITY_MIN;
119 }
120 memset(&dst->actions.retry, 0, sizeof(dst->actions.retry)); \
121}
122
123#define UPDATE_CTX2 compile_copy_context(&unlang_ctx2, unlang_ctx)
124
125
127
128static char const unlang_spaces[] = " ";
129
130static inline CC_HINT(always_inline) int unlang_attr_rules_verify(tmpl_attr_rules_t const *rules)
131{
132 if (!fr_cond_assert_msg(rules->dict_def, "No protocol dictionary set")) return -1;
133 if (!fr_cond_assert_msg(rules->dict_def != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
134 if (!fr_cond_assert_msg(!rules->allow_foreign, "rules->attr.allow_foreign must be false")) return -1;
135
136 return 0;
137}
138
139static inline CC_HINT(always_inline) int unlang_rules_verify(tmpl_rules_t const *rules)
140{
141 if (!fr_cond_assert_msg(!rules->at_runtime, "rules->at_runtime must be false")) return -1;
142 return unlang_attr_rules_verify(&rules->attr);
143}
144
145#if 0
146#define ATTR_RULES_VERIFY(_rules) if (unlang_attr_rules_verify(_rules) < 0) return NULL;
147#endif
148#define RULES_VERIFY(_rules) do { if (unlang_rules_verify(_rules) < 0) return NULL; } while (0)
149
150static bool pass2_fixup_tmpl(UNUSED TALLOC_CTX *ctx, tmpl_t **vpt_p, CONF_ITEM const *ci, fr_dict_t const *dict)
151{
152 tmpl_t *vpt = *vpt_p;
153
155
156 /*
157 * We may now know the correct dictionary
158 * where we didn't before...
159 */
160 if (!vpt->rules.attr.dict_def) tmpl_set_dict_def(vpt, dict);
161
162 /*
163 * Fixup any other tmpl types
164 */
165 if (tmpl_resolve(vpt, &(tmpl_res_rules_t){ .dict_def = dict, .force_dict_def = (dict != NULL)}) < 0) {
166 cf_log_perr(ci, NULL);
167 return false;
168 }
169
170 return true;
171}
172
173/** Fixup ONE map (recursively)
174 *
175 * This function resolves most things. Most notable it CAN leave the
176 * RHS unresolved, for use in `map` sections.
177 */
178static bool pass2_fixup_map(map_t *map, tmpl_rules_t const *rules, fr_dict_attr_t const *parent)
179{
180 RULES_VERIFY(rules);
181
182 if (tmpl_is_data_unresolved(map->lhs)) {
183 if (!pass2_fixup_tmpl(map, &map->lhs, map->ci, rules->attr.dict_def)) {
184 return false;
185 }
186 }
187
188 /*
189 * Enforce parent-child relationships in nested maps.
190 */
191 if (parent) {
192 if ((map->op != T_OP_EQ) && (!map->parent || (map->parent->op != T_OP_SUB_EQ))) {
193 cf_log_err(map->ci, "Invalid operator \"%s\" in nested map section. "
194 "Only '=' is allowed",
195 fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
196 return false;
197 }
198 }
199
200 if (map->rhs) {
201 if (tmpl_is_data_unresolved(map->rhs)) {
203
204 if (!pass2_fixup_tmpl(map, &map->rhs, map->ci, rules->attr.dict_def)) {
205 return false;
206 }
207 }
208 }
209
210 /*
211 * Sanity check sublists.
212 */
213 if (!map_list_empty(&map->child)) {
214 fr_dict_attr_t const *da;
215 map_t *child;
216
217 if (!tmpl_is_attr(map->lhs)) {
218 cf_log_err(map->ci, "Sublists can only be assigned to a known attribute");
219 return false;
220 }
221
222 da = tmpl_attr_tail_da(map->lhs);
223
224 /*
225 * Resolve all children.
226 */
227 for (child = map_list_next(&map->child, NULL);
228 child != NULL;
229 child = map_list_next(&map->child, child)) {
230 if (!pass2_fixup_map(child, rules, da)) {
231 return false;
232 }
233 }
234 }
235
236 return true;
237}
238
239/*
240 * Do all kinds of fixups and checks for update sections.
241 */
242static bool pass2_fixup_update(unlang_group_t *g, tmpl_rules_t const *rules)
243{
245 map_t *map = NULL;
246
247 RULES_VERIFY(rules);
248
249 while ((map = map_list_next(&gext->map, map))) {
250 /*
251 * Mostly fixup the map, but maybe leave the RHS
252 * unresolved.
253 */
254 if (!pass2_fixup_map(map, rules, NULL)) return false;
255
256 /*
257 * Check allowed operators, and ensure that the
258 * RHS is resolved.
259 */
260 if (cf_item_is_pair(map->ci) && (unlang_fixup_update(map, NULL) < 0)) return false;
261 }
262
263 return true;
264}
265
266/*
267 * Compile the RHS of map sections to xlat_exp_t
268 */
270{
272 map_t *map = NULL;
273
274 RULES_VERIFY(rules);
275
276 /*
277 * Do most fixups on the maps. Leaving the RHS as
278 * unresolved, so that the `map` function can interpret
279 * the RHS as a reference to a json string, SQL column
280 * name, etc.
281 */
282 while ((map = map_list_next(&gext->map, map))) {
283 if (!pass2_fixup_map(map, rules, NULL)) return false;
284 }
285
286 /*
287 * Map sections don't need a VPT.
288 */
289 if (!gext->vpt) return true;
290
291 return pass2_fixup_tmpl(map_list_head(&gext->map)->ci, &gext->vpt,
292 cf_section_to_item(g->cs), rules->attr.dict_def);
293}
294
295static void unlang_dump(unlang_t *instruction, int depth)
296{
297 unlang_t *c;
299 map_t *map;
300 char buffer[1024];
301
302 for (c = instruction; c != NULL; c = c->next) {
303 switch (c->type) {
304 case UNLANG_TYPE_NULL:
306 case UNLANG_TYPE_MAX:
307 fr_assert(0);
308 break;
309
311 DEBUG("%.*s%s", depth, unlang_spaces, c->debug_name);
312 break;
313
315 {
317
318 DEBUG("%.*s%s", depth, unlang_spaces, m->mmc.mi->name);
319 }
320 break;
321
322 case UNLANG_TYPE_MAP:
324 {
325 unlang_map_t *gext;
326
327 DEBUG("%.*s%s {", depth, unlang_spaces, c->debug_name);
328
330 gext = unlang_group_to_map(g);
331 map = NULL;
332 while ((map = map_list_next(&gext->map, map))) {
333 map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
334 DEBUG("%.*s%s", depth + 1, unlang_spaces, buffer);
335 }
336
337 DEBUG("%.*s}", depth, unlang_spaces);
338 }
339 break;
340
341 case UNLANG_TYPE_EDIT:
342 {
343 unlang_edit_t *edit;
344
345 edit = unlang_generic_to_edit(c);
346 map = NULL;
347 while ((map = map_list_next(&edit->maps, map))) {
348 if (!map->rhs) continue; /* @todo - fixme */
349
350 map_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), map);
351 DEBUG("%.*s%s", depth + 1, unlang_spaces, buffer);
352 }
353
354 DEBUG("%.*s}", depth, unlang_spaces);
355 }
356 break;
357
358 case UNLANG_TYPE_CALL:
360 case UNLANG_TYPE_CASE:
363 case UNLANG_TYPE_ELSE:
366 case UNLANG_TYPE_IF:
377 case UNLANG_TYPE_TRY:
378 case UNLANG_TYPE_CATCH: /* @todo - print out things we catch, too */
380 DEBUG("%.*s%s {", depth, unlang_spaces, c->debug_name);
381 unlang_dump(g->children, depth + 1);
382 DEBUG("%.*s}", depth, unlang_spaces);
383 break;
384
389 case UNLANG_TYPE_TMPL:
390 case UNLANG_TYPE_XLAT:
391 DEBUG("%.*s%s", depth, unlang_spaces, c->debug_name);
392 break;
393 }
394 }
395}
396
397/** Validate and fixup a map that's part of an map section.
398 *
399 * @param map to validate.
400 * @param ctx data to pass to fixup function (currently unused).
401 * @return 0 if valid else -1.
402 */
403static int unlang_fixup_map(map_t *map, UNUSED void *ctx)
404{
405 switch (map->lhs->type) {
406 case TMPL_TYPE_ATTR:
408 case TMPL_TYPE_XLAT:
409 break;
410
411 default:
412 cf_log_err(map->ci, "Left side of map must be an attribute "
413 "or an xlat (that expands to an attribute), not a %s",
414 tmpl_type_to_str(map->lhs->type));
415 return -1;
416 }
417
418 switch (map->rhs->type) {
421 case TMPL_TYPE_DATA:
422 case TMPL_TYPE_XLAT:
423 case TMPL_TYPE_ATTR:
424 case TMPL_TYPE_EXEC:
425 break;
426
427 default:
428 cf_log_err(map->ci, "Right side of map must be an attribute, literal, xlat or exec, got type %s",
429 tmpl_type_to_str(map->rhs->type));
430 return -1;
431 }
432
433 if (!fr_assignment_op[map->op] && !fr_comparison_op[map->op]) {
434 cf_log_err(map->ci, "Invalid operator \"%s\" in map section. "
435 "Only assignment or filter operators are allowed",
436 fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
437 return -1;
438 }
439
440 return 0;
441}
442
443
444/** Validate and fixup a map that's part of an update section.
445 *
446 * @param map to validate.
447 * @param ctx data to pass to fixup function (currently unused).
448 * @return
449 * - 0 if valid.
450 * - -1 not valid.
451 */
452int unlang_fixup_update(map_t *map, void *ctx)
453{
454 CONF_PAIR *cp = cf_item_to_pair(map->ci);
455
456 if (!ctx) {
457 /*
458 * Fixup RHS attribute references to change NUM_UNSPEC to NUM_ALL.
459 */
460 switch (map->rhs->type) {
461 case TMPL_TYPE_ATTR:
463 break;
464
465 default:
466 break;
467 }
468 }
469
470 /*
471 * Lots of sanity checks for insane people...
472 */
473
474 /*
475 * Depending on the attribute type, some operators are disallowed.
476 */
477 if (tmpl_is_attr(map->lhs)) {
478 /*
479 * What exactly where you expecting to happen here?
480 */
481 if (tmpl_attr_tail_da_is_leaf(map->lhs) &&
482 tmpl_is_list(map->rhs)) {
483 cf_log_err(map->ci, "Can't copy list into an attribute");
484 return -1;
485 }
486
487 if (!fr_assignment_op[map->op] && !fr_comparison_op[map->op] && !fr_binary_op[map->op]) {
488 cf_log_err(map->ci, "Invalid operator \"%s\" in update section. "
489 "Only assignment or filter operators are allowed",
490 fr_table_str_by_value(fr_tokens_table, map->op, "<INVALID>"));
491 return -1;
492 }
493
494 if (fr_comparison_op[map->op] && (map->op != T_OP_CMP_FALSE)) {
495 cf_log_warn(cp, "Please use the 'filter' keyword for attribute filtering");
496 }
497 }
498
499 /*
500 * If the map has a unary operator there's no further
501 * processing we need to, as RHS is unused.
502 */
503 if (map->op == T_OP_CMP_FALSE) return 0;
504
505 if (!tmpl_is_data_unresolved(map->rhs)) return 0;
506
507 /*
508 * If LHS is an attribute, and RHS is a literal, we can
509 * preparse the information into a TMPL_TYPE_DATA.
510 *
511 * Unless it's a unary operator in which case we
512 * ignore map->rhs.
513 */
514 if (tmpl_is_attr(map->lhs) && tmpl_is_data_unresolved(map->rhs)) {
515 fr_type_t type = tmpl_attr_tail_da(map->lhs)->type;
516
517 /*
518 * @todo - allow passing octets to
519 * FR_TYPE_STRUCT, which can then decode them as
520 * data? That would be rather powerful.
521 */
523
524 /*
525 * It's a literal string, just copy it.
526 * Don't escape anything.
527 */
528 if (tmpl_cast_in_place(map->rhs, type, tmpl_attr_tail_da(map->lhs)) < 0) {
529 cf_log_perr(map->ci, "Cannot convert RHS value (%s) to LHS attribute type (%s)",
532 return -1;
533 }
534
535 return 0;
536 } /* else we can't precompile the data */
537
538 if (!tmpl_is_xlat(map->lhs)) {
539 fr_assert(0);
540 cf_log_err(map->ci, "Cannot determine what update action to perform");
541 return -1;
542 }
543
544 return 0;
545}
546
547
549{
551 unlang_t *c;
552 TALLOC_CTX *ctx;
553
554 ctx = parent;
555 if (!ctx) ctx = cs;
556
557 /*
558 * All the groups have a common header
559 */
560 g = (unlang_group_t *)_talloc_zero_pooled_object(ctx, ext->len, ext->type_name,
561 ext->pool_headers, ext->pool_len);
562 if (!g) return NULL;
563
564 g->children = NULL;
565 g->tail = &g->children;
566 g->cs = cs;
567
569 c->parent = parent;
570 c->type = ext->type;
571 c->ci = CF_TO_ITEM(cs);
572
573 return g;
574}
575
577{
578 int i;
579
580 /*
581 * Note that we do NOT copy over the default retries, as
582 * that would result in every subsection doing it's own
583 * retries. That is not what we want. Instead, we want
584 * the retries to apply only to the _current_ section.
585 */
586
587 /*
588 * Children of "redundant" and "redundant-load-balance"
589 * have RETURN for all actions except fail. But THEIR children are normal.
590 */
591 if (c->parent &&
593 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
594 switch (i) {
595 case RLM_MODULE_FAIL:
597 if (!c->actions.actions[i]) {
598 c->actions.actions[i] = 1;
599 }
600 continue;
601
602 default:
603 if (!c->actions.actions[i]) c->actions.actions[i] = MOD_ACTION_RETURN;
604 break;
605 }
606 }
607
608 return;
609 }
610
611 /*
612 * Set the default actions, if they haven't already been
613 * set.
614 */
615 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
616 if (!c->actions.actions[i]) {
617 c->actions.actions[i] = unlang_ctx->actions.actions[i];
618 }
619 }
620}
621
623{
625
626 /*
627 * map <module-name> <arg>
628 */
629 if (gext->vpt) {
630 char quote;
631 size_t quoted_len;
632 char *quoted_str;
633
634 switch (cf_section_argv_quote(g->cs, 0)) {
636 quote = '"';
637 break;
638
640 quote = '\'';
641 break;
642
644 quote = '`';
645 break;
646
647 default:
648 quote = '\0';
649 break;
650 }
651
652 quoted_len = fr_snprint_len(gext->vpt->name, gext->vpt->len, quote);
653 quoted_str = talloc_array(g, char, quoted_len);
654 fr_snprint(quoted_str, quoted_len, gext->vpt->name, gext->vpt->len, quote);
655
656 g->self.name = talloc_typed_asprintf(g, "map %s %s", cf_section_name2(g->cs), quoted_str);
657 g->self.debug_name = g->self.name;
658 talloc_free(quoted_str);
659
660 return 0;
661 }
662
663 g->self.name = talloc_typed_asprintf(g, "map %s", cf_section_name2(g->cs));
664 g->self.debug_name = g->self.name;
665
666 return 0;
667}
668
670{
672 int rcode;
673
675 unlang_map_t *gext;
676
677 unlang_t *c;
678 CONF_SECTION *modules;
679 char const *tmpl_str;
680
681 tmpl_t *vpt = NULL;
682
683 map_proc_t *proc;
684 map_proc_inst_t *proc_inst;
685
686 char const *name2 = cf_section_name2(cs);
687
688 tmpl_rules_t t_rules;
689
690 static unlang_ext_t const map_ext = {
692 .len = sizeof(unlang_map_t),
693 .type_name = "unlang_map_t"
694 };
695
696 /*
697 * The RHS is NOT resolved in the context of the LHS.
698 */
699 t_rules = *(unlang_ctx->rules);
700 t_rules.attr.disallow_rhs_resolve = true;
701 RULES_VERIFY(&t_rules);
702
703 modules = cf_section_find(cf_root(cs), "modules", NULL);
704 if (!modules) {
705 cf_log_err(cs, "'map' sections require a 'modules' section");
706 return NULL;
707 }
708
709 proc = map_proc_find(name2);
710 if (!proc) {
711 cf_log_err(cs, "Failed to find map processor '%s'", name2);
712 return NULL;
713 }
715
716 g = group_allocate(parent, cs, &map_ext);
717 if (!g) return NULL;
718
719 gext = unlang_group_to_map(g);
720
721 /*
722 * If there's a third string, it's the map src.
723 *
724 * Convert it into a template.
725 */
726 tmpl_str = cf_section_argv(cs, 0); /* AFTER name1, name2 */
727 if (tmpl_str) {
729
731
732 /*
733 * Try to parse the template.
734 */
735 (void) tmpl_afrom_substr(gext, &vpt,
736 &FR_SBUFF_IN(tmpl_str, talloc_array_length(tmpl_str) - 1),
737 type,
738 NULL,
739 &t_rules);
740 if (!vpt) {
741 cf_log_perr(cs, "Failed parsing map");
742 error:
743 talloc_free(g);
744 return NULL;
745 }
746
747 /*
748 * Limit the allowed template types.
749 */
750 switch (vpt->type) {
752 case TMPL_TYPE_ATTR:
754 case TMPL_TYPE_XLAT:
756 case TMPL_TYPE_EXEC:
758 case TMPL_TYPE_DATA:
759 break;
760
761 default:
763 cf_log_err(cs, "Invalid third argument for map");
764 return NULL;
765 }
766 }
767
768 /*
769 * This looks at cs->name2 to determine which list to update
770 */
771 map_list_init(&gext->map);
772 rcode = map_afrom_cs(gext, &gext->map, cs, unlang_ctx->rules, &t_rules, unlang_fixup_map, NULL, 256);
773 if (rcode < 0) return NULL; /* message already printed */
774 if (map_list_empty(&gext->map)) {
775 cf_log_err(cs, "'map' sections cannot be empty");
776 goto error;
777 }
778
779
780 /*
781 * Call the map's instantiation function to validate
782 * the map and perform any caching required.
783 */
784 proc_inst = map_proc_instantiate(gext, proc, cs, vpt, &gext->map);
785 if (!proc_inst) {
786 cf_log_err(cs, "Failed instantiating map function '%s'", name2);
787 goto error;
788 }
790
791 gext->vpt = vpt;
792 gext->proc_inst = proc_inst;
793
795
796 /*
797 * Cache the module in the unlang_group_t struct.
798 *
799 * Ensure that the module has a "map" entry in its module
800 * header? Or ensure that the map is registered in the
801 * "bootstrap" phase, so that it's always available here.
802 */
803 if (!pass2_fixup_map_rhs(g, unlang_ctx->rules)) goto error;
804
806
807 return c;
808}
809
810static int edit_section_alloc(CONF_SECTION *parent, CONF_SECTION **child, char const *name1, fr_token_t op)
811{
812 CONF_SECTION *cs;
813
814 cs = cf_section_alloc(parent, parent, name1, NULL);
815 if (!cs) return -1;
816
818
819 if (child) *child = cs;
820
821 return 0;
822}
823
824static int edit_pair_alloc(CONF_SECTION *cs, CONF_PAIR *original, char const *attr, fr_token_t op, char const *value, fr_token_t list_op)
825{
826 CONF_PAIR *cp;
827 fr_token_t rhs_quote;
828
829 if (original) {
830 rhs_quote = cf_pair_value_quote(original);
831 } else {
832 rhs_quote = T_BARE_WORD;
833 }
834
835 cp = cf_pair_alloc(cs, attr, value, op, T_BARE_WORD, rhs_quote);
836 if (!cp) return -1;
837
838 if (!original) return 0;
839
840 cf_filename_set(cp, cf_filename(original));
841 cf_lineno_set(cp, cf_lineno(original));
842
843 if (fr_debug_lvl >= 3) {
844 if (list_op == T_INVALID) {
845 cf_log_err(original, "%s %s %s --> %s %s %s",
846 cf_pair_attr(original), fr_tokens[cf_pair_operator(original)], cf_pair_value(original),
847 attr, fr_tokens[op], value);
848 } else {
849 if (*attr == '&') attr++;
850 cf_log_err(original, "%s %s %s --> %s %s { %s %s %s }",
851 cf_pair_attr(original), fr_tokens[cf_pair_operator(original)], cf_pair_value(original),
852 cf_section_name1(cs), fr_tokens[list_op], attr, fr_tokens[op], value);
853 }
854 } else if (fr_debug_lvl >= 2) {
855 if (list_op == T_INVALID) {
856 cf_log_err(original, "--> %s %s %s",
857 attr, fr_tokens[op], value);
858 } else {
859 cf_log_err(original, "--> %s %s { %s %s %s }",
860 cf_section_name1(cs), fr_tokens[list_op], attr, fr_tokens[op], value);
861 }
862 }
863
864 return 0;
865}
866
867/*
868 * Convert "update" to "edit" using evil spells and sorcery.
869 */
871{
872 char const *name2 = cf_section_name2(cs);
873 CONF_ITEM *ci;
874 CONF_SECTION *group;
876 char list_buffer[32];
877 char value_buffer[256];
878 char attr_buffer[256];
879 char const *list;
880
882
883 /*
884 * Wrap it all in a group, no matter what. Because of
885 * limitations in the cf_pair_alloc() API.
886 */
887 group = cf_section_alloc(g->cs, g->cs, "group", NULL);
888 if (!group) return NULL;
889
890 (void) cf_item_remove(g->cs, group); /* was added at the end */
891 cf_item_insert_after(g->cs, cs, group);
892
893 /*
894 * Hoist this out of the loop, and make sure it never has a '&' prefix.
895 */
896 if (name2) {
897 if (*name2 == '&') name2++;
898 snprintf(list_buffer, sizeof(list_buffer), "%s", name2);
899 } else {
900 snprintf(list_buffer, sizeof(list_buffer), "%s", tmpl_list_name(unlang_ctx->rules->attr.list_def, "<INVALID>"));
901
902 }
903
904 /*
905 * Loop over the entries, rewriting them.
906 */
907 for (ci = cf_item_next(cs, NULL);
908 ci != NULL;
909 ci = cf_item_next(cs, ci)) {
910 CONF_PAIR *cp;
911 CONF_SECTION *child;
912 int rcode;
913 fr_token_t op;
914 char const *attr, *value, *end;
915
916 if (cf_item_is_section(ci)) {
917 cf_log_err(ci, "Cannot specify subsections for 'update'");
918 return NULL;
919 }
920
921 if (!cf_item_is_pair(ci)) continue;
922
923 cp = cf_item_to_pair(ci);
924
925 attr = cf_pair_attr(cp);
926 value = cf_pair_value(cp);
927 op = cf_pair_operator(cp);
928
929 fr_assert(attr);
931
932 list = list_buffer;
933
934 if (*attr == '&') attr++;
935
936 end = strchr(attr, '.');
937 if (!end) end = attr + strlen(attr);
938
939 /*
940 * Separate out the various possibilities for the "name", which could be a list, an
941 * attribute name, or a list followed by an attribute name.
942 *
943 * Note that even if we have "update request { ....}", the v3 parser allowed the contents
944 * of the "update" section to still specify parent / lists. Which makes parsing it all
945 * annoying.
946 *
947 * The good news is that all we care about is whether or not there's a parent / list ref.
948 * We don't care what that ref is.
949 */
950 {
952
953 /*
954 * Allow for a "parent" or "outer" reference. There may be multiple
955 * "parent.parent", so we keep processing them until we get a list reference.
956 */
958
959 /*
960 * Catch one more case where the behavior is different.
961 *
962 * &request += &config[*]
963 */
964 if ((cf_pair_value_quote(cp) == T_BARE_WORD) && (*value == '&') &&
965 (strchr(value, '.') == NULL) && (strchr(value, '[') != NULL)) {
966 char const *p = strchr(value, '[');
967
968 cf_log_err(cp, "Cannot do array assignments for lists. Just use '%s %s %.*s'",
969 list, fr_tokens[op], (int) (p - value), value);
970 return NULL;
971 }
972
973 goto attr_is_list;
974
975 /*
976 * Doesn't have a parent ref, maybe it's a list ref?
977 */
978 } else if (tmpl_attr_list_from_substr(&tmpl_list, &FR_SBUFF_IN(attr, (end - attr))) > 0) {
979 char *p;
980
981 attr_is_list:
982 snprintf(attr_buffer, sizeof(attr_buffer), "%s", attr);
983 list = attr_buffer;
984 attr = NULL;
985
986 p = strchr(attr_buffer, '.');
987 if (p) {
988 *(p++) = '\0';
989 attr = p;
990 }
991 }
992 }
993
994 switch (op) {
995 /*
996 * FOO !* ANY
997 *
998 * The RHS doesn't matter, so we ignore it.
999 */
1000 case T_OP_CMP_FALSE:
1001 if (!attr) {
1002 /*
1003 * Set list to empty value.
1004 */
1005 rcode = edit_section_alloc(group, NULL, list, T_OP_SET);
1006
1007 } else {
1008 if (strchr(attr, '[') == NULL) {
1009 snprintf(value_buffer, sizeof(value_buffer), "%s[*]", attr);
1010 } else {
1011 snprintf(value_buffer, sizeof(value_buffer), "%s", attr);
1012 }
1013
1014 rcode = edit_pair_alloc(group, cp, list, T_OP_SUB_EQ, value_buffer, T_INVALID);
1015 }
1016 break;
1017
1018 case T_OP_SET:
1019 /*
1020 * Must be a list-to-list operation
1021 */
1022 if (!attr) {
1023 list_op:
1024 rcode = edit_pair_alloc(group, cp, list, op, value, T_INVALID);
1025 break;
1026 }
1027 goto pair_op;
1028
1029 case T_OP_EQ:
1030 /*
1031 * Allow &list = "foo"
1032 */
1033 if (!attr) {
1034 if (!value) {
1035 cf_log_err(cp, "Missing value");
1036 return NULL;
1037 }
1038
1039 rcode = edit_pair_alloc(group, cp, list, op, value, T_INVALID);
1040 break;
1041 }
1042
1043 pair_op:
1044 fr_assert(*attr != '&');
1045 if (snprintf(value_buffer, sizeof(value_buffer), "%s.%s", list, attr) < 0) {
1046 cf_log_err(cp, "RHS of update too long to convert to edit automatically");
1047 return NULL;
1048 }
1049
1050 rcode = edit_pair_alloc(group, cp, value_buffer, op, value, T_INVALID);
1051 break;
1052
1053 case T_OP_ADD_EQ:
1054 case T_OP_PREPEND:
1055 if (!attr) goto list_op;
1056
1057 rcode = edit_section_alloc(group, &child, list, op);
1058 if (rcode < 0) break;
1059
1060 rcode = edit_pair_alloc(child, cp, attr, T_OP_EQ, value, op);
1061 break;
1062
1063 /*
1064 * Remove matching attributes
1065 */
1066 case T_OP_SUB_EQ:
1067 op = T_OP_CMP_EQ;
1068
1069 filter:
1070 if (!attr) {
1071 cf_log_err(cp, "Invalid operator for list assignment");
1072 return NULL;
1073 }
1074
1075 rcode = edit_section_alloc(group, &child, list, T_OP_SUB_EQ);
1076 if (rcode < 0) break;
1077
1078 if (strchr(attr, '[') != 0) {
1079 cf_log_err(cp, "Cannot do filtering with array indexes");
1080 return NULL;
1081 }
1082
1083 rcode = edit_pair_alloc(child, cp, attr, op, value, T_OP_SUB_EQ);
1084 break;
1085
1086 /*
1087 * Keep matching attributes, i.e. remove non-matching ones.
1088 */
1089 case T_OP_CMP_EQ:
1090 op = T_OP_NE;
1091 goto filter;
1092
1093 case T_OP_NE:
1094 op = T_OP_CMP_EQ;
1095 goto filter;
1096
1097 case T_OP_LT:
1098 op = T_OP_GE;
1099 goto filter;
1100
1101 case T_OP_LE:
1102 op = T_OP_GT;
1103 goto filter;
1104
1105 case T_OP_GT:
1106 op = T_OP_LE;
1107 goto filter;
1108
1109 case T_OP_GE:
1110 op = T_OP_LT;
1111 goto filter;
1112
1113 default:
1114 cf_log_err(cp, "Unsupported operator - cannot auto-convert to edit section");
1115 return NULL;
1116 }
1117
1118 if (rcode < 0) {
1119 cf_log_err(cp, "Failed converting entry");
1120 return NULL;
1121 }
1122 }
1123
1124 return UNLANG_IGNORE;
1125}
1126
1128{
1130 int rcode;
1131
1132 unlang_group_t *g;
1133 unlang_map_t *gext;
1134
1135 unlang_t *c;
1136 char const *name2 = cf_section_name2(cs);
1137
1138 tmpl_rules_t t_rules;
1139
1140 static unlang_ext_t const update_ext = {
1142 .len = sizeof(unlang_map_t),
1143 .type_name = "unlang_map_t"
1144 };
1145
1146 if (main_config_migrate_option_get("forbid_update")) {
1147 cf_log_err(cs, "The use of 'update' sections is forbidden by the server configuration");
1148 return NULL;
1149 }
1150
1151 /*
1152 * If we're migrating "update" sections to edit, then go
1153 * do that now.
1154 */
1155 if (main_config_migrate_option_get("rewrite_update")) {
1157 }
1158
1159 /*
1160 * We allow unknown attributes here.
1161 */
1162 t_rules = *(unlang_ctx->rules);
1163 t_rules.attr.allow_unknown = true;
1164 t_rules.attr.allow_wildcard = true;
1165 RULES_VERIFY(&t_rules);
1166
1167 g = group_allocate(parent, cs, &update_ext);
1168 if (!g) return NULL;
1169
1170 gext = unlang_group_to_map(g);
1171
1172 /*
1173 * This looks at cs->name2 to determine which list to update
1174 */
1175 map_list_init(&gext->map);
1176 rcode = map_afrom_cs(gext, &gext->map, cs, &t_rules, &t_rules, unlang_fixup_update, NULL, 128);
1177 if (rcode < 0) return NULL; /* message already printed */
1178 if (map_list_empty(&gext->map)) {
1179 cf_log_err(cs, "'update' sections cannot be empty");
1180 error:
1181 talloc_free(g);
1182 return NULL;
1183 }
1184
1186 if (name2) {
1187 c->name = name2;
1188 c->debug_name = talloc_typed_asprintf(c, "update %s", name2);
1189 } else {
1190 c->name = "update";
1191 c->debug_name = c->name;
1192 }
1193
1194 if (!pass2_fixup_update(g, unlang_ctx->rules)) goto error;
1195
1197
1198 return c;
1199}
1200
1201#define T(_x) [T_OP_ ## _x] = true
1202
1203static const bool edit_list_sub_op[T_TOKEN_LAST] = {
1204 T(NE),
1205 T(GE),
1206 T(GT),
1207 T(LE),
1208 T(LT),
1209 T(CMP_EQ),
1210};
1211
1212/** Validate and fixup a map that's part of an edit section.
1213 *
1214 * @param map to validate.
1215 * @param ctx data to pass to fixup function (currently unused).
1216 * @return 0 if valid else -1.
1217 *
1218 * @todo - this is only called for CONF_PAIR maps, not for
1219 * CONF_SECTION. So when we parse nested maps, there's no validation
1220 * done of the CONF_SECTION. In order to fix this, we need to have
1221 * map_afrom_cs() call the validation function for the CONF_SECTION
1222 * *before* recursing.
1223 */
1224static int unlang_fixup_edit(map_t *map, void *ctx)
1225{
1226 CONF_PAIR *cp = cf_item_to_pair(map->ci);
1227 fr_dict_attr_t const *da;
1228 fr_dict_attr_t const *parent = NULL;
1229 map_t *parent_map = ctx;
1230
1231 fr_assert(parent_map);
1232#ifdef STATIC_ANALYZER
1233 if (!parent_map) return -1;
1234#endif
1235
1236 fr_assert(tmpl_is_attr(parent_map->lhs));
1237
1238 if (parent_map && (parent_map->op == T_OP_SUB_EQ)) {
1239 if (!edit_list_sub_op[map->op]) {
1240 cf_log_err(cp, "Invalid operator '%s' for right-hand side list. It must be a comparison operator", fr_tokens[map->op]);
1241 return -1;
1242 }
1243
1244 } else if (map->op != T_OP_EQ) {
1245 cf_log_err(cp, "Invalid operator '%s' for right-hand side list. It must be '='", fr_tokens[map->op]);
1246 return -1;
1247 }
1248
1249 /*
1250 * map_afrom_cs() will build its tree recursively, and call us for each child map.
1251 */
1252 if (map->parent && (map->parent != parent_map)) parent_map = map->parent;
1253
1254 parent = tmpl_attr_tail_da(parent_map->lhs);
1255
1256 switch (map->lhs->type) {
1257 case TMPL_TYPE_ATTR:
1258 da = tmpl_attr_tail_da(map->lhs);
1259 if (!da->flags.internal && parent && (parent->type != FR_TYPE_GROUP) &&
1260 (da->parent != parent)) {
1261 /* FIXME - Broken check, doesn't work for key attributes */
1262 cf_log_err(cp, "Invalid location for %s - it is not a child of %s",
1263 da->name, parent->name);
1264 return 0;
1265 }
1266 break;
1267
1269 case TMPL_TYPE_XLAT:
1270 break;
1271
1272 default:
1273 cf_log_err(map->ci, "Left side of map must be an attribute "
1274 "or an xlat (that expands to an attribute), not a %s",
1275 tmpl_type_to_str(map->lhs->type));
1276 return -1;
1277 }
1278
1279 fr_assert(map->rhs);
1280
1281 switch (map->rhs->type) {
1284 case TMPL_TYPE_XLAT:
1285 case TMPL_TYPE_DATA:
1286 case TMPL_TYPE_ATTR:
1287 case TMPL_TYPE_EXEC:
1288 break;
1289
1290 default:
1291 cf_log_err(map->ci, "Right side of map must be an attribute, literal, xlat or exec, got type %s",
1292 tmpl_type_to_str(map->rhs->type));
1293 return -1;
1294 }
1295
1296 return 0;
1297}
1298
1299/** Compile one edit section.
1300 */
1302{
1303 unlang_edit_t *edit;
1304 unlang_t *c, *out = UNLANG_IGNORE;
1305 map_t *map;
1306 char const *name;
1307 fr_token_t op;
1308 ssize_t slen;
1309 fr_dict_attr_t const *parent_da;
1310 int num;
1311
1312 tmpl_rules_t t_rules;
1313
1314 name = cf_section_name2(cs);
1315 if (name) {
1316 cf_log_err(cs, "Unexpected name2 '%s' for editing list %s ", name, cf_section_name1(cs));
1317 return NULL;
1318 }
1319
1320 op = cf_section_name2_quote(cs);
1321 if ((op == T_INVALID) || !fr_list_assignment_op[op]) {
1322 cf_log_err(cs, "Invalid operator '%s' for editing list %s.", fr_tokens[op], cf_section_name1(cs));
1323 return NULL;
1324 }
1325
1326 if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
1327 cf_log_err(cs, "Invalid operator \"%s\".",
1328 fr_table_str_by_value(fr_tokens_table, op, "<INVALID>"));
1329 return NULL;
1330 }
1331
1332 /*
1333 * We allow unknown attributes here.
1334 */
1335 t_rules = *(unlang_ctx->rules);
1336 t_rules.attr.allow_unknown = true;
1337 RULES_VERIFY(&t_rules);
1338
1339 edit = talloc_zero(parent, unlang_edit_t);
1340 if (!edit) return NULL;
1341
1342 c = out = unlang_edit_to_generic(edit);
1343 c->parent = parent;
1344 c->next = NULL;
1345 c->name = cf_section_name1(cs);
1346 c->debug_name = c->name;
1348 c->ci = CF_TO_ITEM(cs);
1349
1350 map_list_init(&edit->maps);
1351
1353
1354 /*
1355 * Allocate the map and initialize it.
1356 */
1357 MEM(map = talloc_zero(parent, map_t));
1358 map->op = op;
1359 map->ci = cf_section_to_item(cs);
1360 map_list_init(&map->child);
1361
1362 name = cf_section_name1(cs);
1363
1364 slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, name, &t_rules);
1365 if (slen <= 0) {
1366 cf_log_err(cs, "Failed parsing list reference %s - %s", name, fr_strerror());
1367 fail:
1368 talloc_free(edit);
1369 return NULL;
1370 }
1371
1372 /*
1373 * Can't assign to [*] or [#]
1374 */
1375 num = tmpl_attr_tail_num(map->lhs);
1376 if ((num == NUM_ALL) || (num == NUM_COUNT)) {
1377 cf_log_err(cs, "Invalid array reference in %s", name);
1378 goto fail;
1379 }
1380
1381 /*
1382 * If the DA isn't structural, then it can't have children.
1383 */
1384 parent_da = tmpl_attr_tail_da(map->lhs);
1385 if (fr_type_is_structural(parent_da->type)) {
1386 map_t *child;
1387
1388 /*
1389 * Don't update namespace for &reply += { ... }
1390 *
1391 * Do update namespace for &reply.foo += { ... }
1392 *
1393 * Don't update if the LHS is an internal group.
1394 */
1395 if ((tmpl_attr_num_elements(map->lhs) > 1) && (t_rules.attr.list_def != parent_da) &&
1396 !((parent_da->type == FR_TYPE_GROUP) && parent_da->flags.internal)) {
1397 t_rules.attr.namespace = parent_da;
1398 }
1399
1400 if (map_afrom_cs_edit(map, &map->child, cs, &t_rules, &t_rules, unlang_fixup_edit, map, 256) < 0) {
1401 goto fail;
1402 }
1403
1404 /*
1405 * As a set of fixups... we can't do array references in -=
1406 */
1407 if (map->op == T_OP_SUB_EQ) {
1408 for (child = map_list_head(&map->child); child != NULL; child = map_list_next(&map->child, child)) {
1409 if (!tmpl_is_attr(child->lhs)) continue;
1410
1411 if (tmpl_attr_tail_num(child->lhs) != NUM_UNSPEC) {
1412 cf_log_err(child->ci, "Cannot use array references and values when deleting from a list");
1413 goto fail;
1414 }
1415
1416 /*
1417 * The edit code doesn't do this correctly, so we just forbid it.
1418 */
1419 if ((tmpl_attr_num_elements(child->lhs) - tmpl_attr_num_elements(map->lhs)) > 1) {
1420 cf_log_err(child->ci, "List deletion must operate directly on the final child");
1421 goto fail;
1422 }
1423
1424 /*
1425 * We don't do list comparisons either.
1426 */
1427 if (fr_type_is_structural(tmpl_attr_tail_da(child->lhs)->type)) {
1428 cf_log_err(child->ci, "List deletion cannot operate on lists");
1429 goto fail;
1430 }
1431 }
1432 }
1433 } else {
1434 /*
1435 * &foo := { a, b, c }
1436 */
1437 if (map_list_afrom_cs(map, &map->child, cs, &t_rules, NULL, NULL, 256) < 0) {
1438 goto fail;
1439 }
1440
1441 if ((map->op != T_OP_SET) && !map_list_num_elements(&map->child)) {
1442 cf_log_err(cs, "Cannot use operator '%s' for assigning empty list to '%s' data type.",
1443 fr_tokens[map->op], fr_type_to_str(parent_da->type));
1444 goto fail;
1445 }
1446 }
1447 /*
1448 * Do basic sanity checks and resolving.
1449 */
1450 if (!pass2_fixup_map(map, unlang_ctx->rules, NULL)) goto fail;
1451
1452 /*
1453 * Check operators, and ensure that the RHS has been
1454 * resolved.
1455 */
1456// if (unlang_fixup_update(map, NULL) < 0) goto fail;
1457
1458 map_list_insert_tail(&edit->maps, map);
1459
1460 return out;
1461}
1462
1463/** Compile one edit pair
1464 *
1465 */
1467{
1468 unlang_edit_t *edit;
1469 unlang_t *c = NULL, *out = UNLANG_IGNORE;
1470 map_t *map;
1471 int num;
1472
1473 tmpl_rules_t t_rules;
1474 fr_token_t op;
1475
1476 /*
1477 * We allow unknown attributes here.
1478 */
1479 t_rules = *(unlang_ctx->rules);
1480 t_rules.attr.allow_unknown = true;
1481 RULES_VERIFY(&t_rules);
1482
1483 edit = talloc_zero(parent, unlang_edit_t);
1484 if (!edit) return NULL;
1485
1486 c = out = unlang_edit_to_generic(edit);
1487 c->parent = parent;
1488 c->next = NULL;
1489 c->name = cf_pair_attr(cp);
1490 c->debug_name = c->name;
1492 c->ci = CF_TO_ITEM(cp);
1493
1494 map_list_init(&edit->maps);
1495
1497
1498 op = cf_pair_operator(cp);
1499 if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
1500 cf_log_err(cp, "Invalid operator \"%s\".",
1501 fr_table_str_by_value(fr_tokens_table, op, "<INVALID>"));
1502 return NULL;
1503 }
1504
1505 /*
1506 * Convert this particular map.
1507 */
1508 if (map_afrom_cp(edit, &map, map_list_tail(&edit->maps), cp, &t_rules, NULL, true) < 0) {
1509 fail:
1510 talloc_free(edit);
1511 return NULL;
1512 }
1513
1514 /*
1515 * @todo - we still want to do fixups on the RHS?
1516 */
1517 if (tmpl_is_attr(map->lhs)) {
1518 /*
1519 * Can't assign to [*] or [#]
1520 */
1521 num = tmpl_attr_tail_num(map->lhs);
1522 if ((num == NUM_ALL) || (num == NUM_COUNT)) {
1523 cf_log_err(cp, "Invalid array reference in %s", map->lhs->name);
1524 goto fail;
1525 }
1526
1527 if ((map->op == T_OP_SUB_EQ) && fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type) &&
1528 tmpl_is_attr(map->rhs) && tmpl_attr_tail_da(map->rhs)->flags.local) {
1529 cf_log_err(cp, "Cannot delete local variable %s", map->rhs->name);
1530 goto fail;
1531 }
1532 }
1533
1534 /*
1535 * Do basic sanity checks and resolving.
1536 */
1537 if (!pass2_fixup_map(map, unlang_ctx->rules, NULL)) goto fail;
1538
1539 /*
1540 * Check operators, and ensure that the RHS has been
1541 * resolved.
1542 */
1543 if (unlang_fixup_update(map, c) < 0) goto fail;
1544
1545 map_list_insert_tail(&edit->maps, map);
1546
1547 return out;
1548}
1549
1550static int define_local_variable(CONF_ITEM *ci, unlang_variable_t *var, tmpl_rules_t *t_rules, fr_type_t type, char const *name,
1551 fr_dict_attr_t const *ref);
1552
1553#define debug_braces(_type) (unlang_ops[_type].flag & UNLANG_OP_FLAG_DEBUG_BRACES)
1554
1555/** Compile a variable definition.
1556 *
1557 * Definitions which are adjacent to one another are automatically merged
1558 * into one larger variable definition.
1559 */
1561{
1562 unlang_variable_t *var;
1564 char const *attr, *value;
1565 unlang_group_t *group;
1566
1568
1569 /*
1570 * Enforce locations for local variables.
1571 */
1572 switch (parent->type) {
1573 case UNLANG_TYPE_CASE:
1574 case UNLANG_TYPE_ELSE:
1575 case UNLANG_TYPE_ELSIF:
1577 case UNLANG_TYPE_GROUP:
1578 case UNLANG_TYPE_IF:
1580 case UNLANG_TYPE_LIMIT:
1581 case UNLANG_TYPE_POLICY:
1586 break;
1587
1588 default:
1589 cf_log_err(cp, "Local variables cannot be used here");
1590 return -1;
1591 }
1592
1593 /*
1594 * The variables exist in the parent block.
1595 */
1597 if (group->variables) {
1598 var = group->variables;
1599
1600 } else {
1601 group->variables = var = talloc_zero(parent, unlang_variable_t);
1602 if (!var) return -1;
1603
1604 var->dict = fr_dict_protocol_alloc(unlang_ctx->rules->attr.dict_def);
1605 if (!var->dict) {
1606 talloc_free(var);
1607 return -1;
1608 }
1609 var->root = fr_dict_root(var->dict);
1610
1611 var->max_attr = 1;
1612
1613 /*
1614 * Initialize the new rules, and point them to the parent rules.
1615 *
1616 * Then replace the parse rules with our rules, and our dictionary.
1617 */
1618 *t_rules = *unlang_ctx->rules;
1619 t_rules->parent = unlang_ctx->rules;
1620
1621 t_rules->attr.dict_def = var->dict;
1622 t_rules->attr.namespace = NULL;
1623
1624 unlang_ctx->rules = t_rules;
1625 }
1626
1627 attr = cf_pair_attr(cp); /* data type */
1628 value = cf_pair_value(cp); /* variable name */
1629
1631 if (type == FR_TYPE_NULL) {
1633 cf_log_err(cp, "Invalid data type '%s'", attr);
1634 return -1;
1635 }
1636
1637 /*
1638 * Leaf and group are OK. TLV, Vendor, Struct, VSA, etc. are not.
1639 */
1640 if (!(fr_type_is_leaf(type) || (type == FR_TYPE_GROUP))) goto invalid_type;
1641
1642 return define_local_variable(cf_pair_to_item(cp), var, t_rules, type, value, NULL);
1643}
1644
1645/*
1646 * Compile action && rcode for later use.
1647 */
1649{
1650 int action;
1651 char const *attr, *value;
1652
1653 attr = cf_pair_attr(cp);
1654 value = cf_pair_value(cp);
1655 if (!value) return 0;
1656
1657 if (!strcasecmp(value, "return"))
1658 action = MOD_ACTION_RETURN;
1659
1660 else if (!strcasecmp(value, "break"))
1661 action = MOD_ACTION_RETURN;
1662
1663 else if (!strcasecmp(value, "reject"))
1664 action = MOD_ACTION_REJECT;
1665
1666 else if (!strcasecmp(value, "retry"))
1667 action = MOD_ACTION_RETRY;
1668
1669 else if (strspn(value, "0123456789")==strlen(value)) {
1670 action = atoi(value);
1671
1672 if (!action || (action > MOD_PRIORITY_MAX)) {
1673 cf_log_err(cp, "Priorities MUST be between 1 and 64.");
1674 return 0;
1675 }
1676
1677 } else {
1678 cf_log_err(cp, "Unknown action '%s'.\n",
1679 value);
1680 return 0;
1681 }
1682
1683 if (strcasecmp(attr, "default") != 0) {
1684 int rcode;
1685
1686 rcode = fr_table_value_by_str(mod_rcode_table, attr, -1);
1687 if (rcode < 0) {
1688 cf_log_err(cp,
1689 "Unknown module rcode '%s'.",
1690 attr);
1691 return 0;
1692 }
1693 actions->actions[rcode] = action;
1694
1695 } else { /* set all unset values to the default */
1696 int i;
1697
1698 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1699 if (!actions->actions[i]) actions->actions[i] = action;
1700 }
1701 }
1702
1703 return 1;
1704}
1705
1707{
1708 CONF_ITEM *csi;
1709 CONF_SECTION *cs;
1710
1711 cs = cf_item_to_section(ci);
1712 for (csi=cf_item_next(cs, NULL);
1713 csi != NULL;
1714 csi=cf_item_next(cs, csi)) {
1715 CONF_PAIR *cp;
1716 char const *name, *value;
1717
1718 if (cf_item_is_section(csi)) {
1719 cf_log_err(csi, "Invalid subsection in 'retry' configuration.");
1720 return false;
1721 }
1722
1723 if (!cf_item_is_pair(csi)) continue;
1724
1725 cp = cf_item_to_pair(csi);
1726 name = cf_pair_attr(cp);
1727 value = cf_pair_value(cp);
1728
1729 if (!value) {
1730 cf_log_err(csi, "Retry configuration must specify a value");
1731 return false;
1732 }
1733
1734#define CLAMP(_name, _field, _limit) do { \
1735 if (!fr_time_delta_ispos(actions->retry._field)) { \
1736 cf_log_err(csi, "Invalid value for '" STRINGIFY(_name) " = %s' - value must be positive", \
1737 value); \
1738 return false; \
1739 } \
1740 if (fr_time_delta_cmp(actions->retry._field, fr_time_delta_from_sec(_limit)) > 0) { \
1741 cf_log_err(csi, "Invalid value for '" STRINGIFY(_name) " = %s' - value must be less than " STRINGIFY(_limit) "s", \
1742 value); \
1743 return false; \
1744 } \
1745 } while (0)
1746
1747 /*
1748 * We don't use conf_parser_t here for various
1749 * magical reasons.
1750 */
1751 if (strcmp(name, "initial_rtx_time") == 0) {
1752 if (fr_time_delta_from_str(&actions->retry.irt, value, strlen(value), FR_TIME_RES_SEC) < 0) {
1753 error:
1754 cf_log_err(csi, "Failed parsing '%s = %s' - %s",
1755 name, value, fr_strerror());
1756 return false;
1757 }
1758 CLAMP(initial_rtx_time, irt, 2);
1759
1760 } else if (strcmp(name, "max_rtx_time") == 0) {
1761 if (fr_time_delta_from_str(&actions->retry.mrt, value, strlen(value), FR_TIME_RES_SEC) < 0) goto error;
1762
1763 CLAMP(max_rtx_time, mrt, 10);
1764
1765 } else if (strcmp(name, "max_rtx_count") == 0) {
1766 unsigned long v = strtoul(value, 0, 0);
1767
1768 if (v > 10) {
1769 cf_log_err(csi, "Invalid value for 'max_rtx_count = %s' - value must be between 0 and 10",
1770 value);
1771 return false;
1772 }
1773
1774 actions->retry.mrc = v;
1775
1776 } else if (strcmp(name, "max_rtx_duration") == 0) {
1777 if (fr_time_delta_from_str(&actions->retry.mrd, value, strlen(value), FR_TIME_RES_SEC) < 0) goto error;
1778
1779 CLAMP(max_rtx_duration, mrd, 20);
1780
1781 } else {
1782 cf_log_err(csi, "Invalid item '%s' in 'retry' configuration.", name);
1783 return false;
1784 }
1785 }
1786
1787 return true;
1788}
1789
1790bool unlang_compile_actions(unlang_mod_actions_t *actions, CONF_SECTION *action_cs, bool module_retry)
1791{
1792 int i;
1793 bool disallow_retry_action = false;
1794 CONF_ITEM *csi;
1795 CONF_SECTION *cs;
1796
1797 /*
1798 * Over-ride the default return codes of the module.
1799 */
1800 cs = cf_item_to_section(cf_section_to_item(action_cs));
1801 for (csi=cf_item_next(cs, NULL);
1802 csi != NULL;
1803 csi=cf_item_next(cs, csi)) {
1804 char const *name;
1805 CONF_PAIR *cp;
1806
1807 if (cf_item_is_section(csi)) {
1808 CONF_SECTION *subcs = cf_item_to_section(csi);
1809
1810 name = cf_section_name1(subcs);
1811
1812 /*
1813 * Look for a "retry" section.
1814 */
1815 if (name && (strcmp(name, "retry") == 0) && !cf_section_name2(subcs)) {
1816 if (!compile_retry_section(actions, csi)) return false;
1817 continue;
1818 }
1819
1820 cf_log_err(csi, "Invalid subsection. Expected 'action = value'");
1821 return false;
1822 }
1823
1824 if (!cf_item_is_pair(csi)) continue;
1825
1826 cp = cf_item_to_pair(csi);
1827
1828 /*
1829 * Allow 'retry = path.to.retry.config'
1830 */
1831 name = cf_pair_attr(cp);
1832 if (strcmp(name, "retry") == 0) {
1833 CONF_ITEM *subci;
1834 char const *value = cf_pair_value(cp);
1835
1836 if (!value) {
1837 cf_log_err(csi, "Missing reference string");
1838 return false;
1839 }
1840
1841 subci = cf_reference_item(cs, cf_root(cf_section_to_item(action_cs)), value);
1842 if (!subci) {
1843 cf_log_perr(csi, "Failed finding reference '%s'", value);
1844 return false;
1845 }
1846
1847 if (!compile_retry_section(actions, subci)) return false;
1848 continue;
1849 }
1850
1851 if (!compile_action_pair(actions, cp)) {
1852 return false;
1853 }
1854 }
1855
1856 if (module_retry) {
1857 if (!fr_time_delta_ispos(actions->retry.irt)) {
1858 cf_log_err(csi, "initial_rtx_time MUST be non-zero for modules which support retries.");
1859 return false;
1860 }
1861 } else {
1862 if (fr_time_delta_ispos(actions->retry.irt)) {
1863 cf_log_err(csi, "initial_rtx_time MUST be zero, as only max_rtx_count and max_rtx_duration are used.");
1864 return false;
1865 }
1866
1867 if (!actions->retry.mrc && !fr_time_delta_ispos(actions->retry.mrd)) {
1868 disallow_retry_action = true;
1869 }
1870 }
1871
1872 /*
1873 * Sanity check that "fail = retry", we actually have a
1874 * retry section.
1875 */
1876 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1877 if (actions->actions[i] != MOD_ACTION_RETRY) continue;
1878
1879 if (module_retry) {
1880 cf_log_err(csi, "Cannot use a '%s = retry' action for a module which has its own retries",
1881 fr_table_str_by_value(mod_rcode_table, i, "<INVALID>"));
1882 return false;
1883 }
1884
1885 if (disallow_retry_action) {
1886 cf_log_err(csi, "max_rtx_count and max_rtx_duration cannot both be zero when using '%s = retry'",
1887 fr_table_str_by_value(mod_rcode_table, i, "<INVALID>"));
1888 return false;
1889 }
1890
1891 if (!fr_time_delta_ispos(actions->retry.irt) &&
1892 !actions->retry.mrc &&
1893 !fr_time_delta_ispos(actions->retry.mrd)) {
1894 cf_log_err(csi, "Cannot use a '%s = retry' action without a 'retry { ... }' section.",
1895 fr_table_str_by_value(mod_rcode_table, i, "<INVALID>"));
1896 return false;
1897 }
1898 }
1899
1900 return true;
1901}
1902
1904{
1905 unlang_group_t *g;
1906 unlang_t *c;
1907
1908 /*
1909 * If we're compiling an empty section, then the
1910 * *interpreter* type is GROUP, even if the *debug names*
1911 * are something else.
1912 */
1913 g = group_allocate(parent, cs, ext);
1914 if (!g) return NULL;
1915
1917 if (!cs) {
1918 c->name = unlang_ops[ext->type].name;
1919 c->debug_name = c->name;
1920
1921 } else {
1922 char const *name2;
1923
1924 name2 = cf_section_name2(cs);
1925 if (!name2) {
1926 c->name = cf_section_name1(cs);
1927 c->debug_name = c->name;
1928 } else {
1929 c->name = name2;
1930 c->debug_name = talloc_typed_asprintf(c, "%s %s", cf_section_name1(cs), name2);
1931 }
1932 }
1933
1935 return c;
1936}
1937
1938
1940
1941/*
1942 * compile 'actions { ... }' inside of another group.
1943 */
1945{
1946 CONF_ITEM *ci, *next;
1947
1948 ci = cf_section_to_item(subcs);
1949
1950 next = cf_item_next(cs, ci);
1951 if (next && (cf_item_is_pair(next) || cf_item_is_section(next))) {
1952 cf_log_err(ci, "'actions' MUST be the last block in a section");
1953 return false;
1954 }
1955
1956 if (cf_section_name2(subcs) != NULL) {
1957 cf_log_err(ci, "Invalid name for 'actions' section");
1958 return false;
1959 }
1960
1961 /*
1962 * Over-riding the actions can be done in certain limited
1963 * situations. In other situations (e.g. "redundant",
1964 * "load-balance"), it doesn't make sense.
1965 *
1966 * Note that this limitation also applies to "retry"
1967 * timers. We can do retries of a "group". We cannot do
1968 * retries of "load-balance", as the "load-balance"
1969 * section already takes care of redundancy.
1970 *
1971 * We may need to loosen that limitation in the future.
1972 */
1973 switch (c->type) {
1974 case UNLANG_TYPE_CASE:
1975 case UNLANG_TYPE_IF:
1976 case UNLANG_TYPE_ELSE:
1977 case UNLANG_TYPE_ELSIF:
1979 case UNLANG_TYPE_GROUP:
1980 case UNLANG_TYPE_LIMIT:
1981 case UNLANG_TYPE_SWITCH:
1984 break;
1985
1986 default:
1987 cf_log_err(ci, "'actions' MUST NOT be in a '%s' block", unlang_ops[c->type].name);
1988 return false;
1989 }
1990
1991 return unlang_compile_actions(&c->actions, subcs, false);
1992}
1993
1994
1995static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ctx_in, bool set_action_defaults)
1996{
1997 CONF_ITEM *ci = NULL;
1998 unlang_t *c, *single;
1999 bool was_if = false;
2000 char const *skip_else = NULL;
2002 unlang_compile_t unlang_ctx2;
2003 tmpl_rules_t t_rules;
2004
2006
2007 /*
2008 * Create our own compilation context which can be edited
2009 * by a variable definition.
2010 */
2011 compile_copy_context(&unlang_ctx2, unlang_ctx_in);
2012 unlang_ctx = &unlang_ctx2;
2013 t_rules = *unlang_ctx_in->rules;
2014
2015 /*
2016 * Loop over the children of this group.
2017 */
2018 while ((ci = cf_item_next(g->cs, ci))) {
2019 if (cf_item_is_data(ci)) continue;
2020
2021 /*
2022 * Sections are keywords, or references to
2023 * modules with updated return codes.
2024 */
2025 if (cf_item_is_section(ci)) {
2026 char const *name = NULL;
2027 CONF_SECTION *subcs = cf_item_to_section(ci);
2028
2029 /*
2030 * Skip precompiled blocks. This is
2031 * mainly for policies.
2032 */
2033 if (cf_data_find(subcs, unlang_group_t, NULL)) continue;
2034
2035 /*
2036 * "actions" apply to the current group.
2037 * It's not a subgroup.
2038 */
2039 name = cf_section_name1(subcs);
2040
2041 /*
2042 * In-line attribute editing. Nothing else in the parse has list assignments, so this must be it.
2043 */
2045 single = compile_edit_section(c, unlang_ctx, subcs);
2046 if (!single) {
2047 talloc_free(c);
2048 return NULL;
2049 }
2050
2051 goto add_child;
2052 }
2053
2054 if (strcmp(name, "actions") == 0) {
2055 if (!compile_action_subsection(c, g->cs, subcs)) {
2056 talloc_free(c);
2057 return NULL;
2058 }
2059
2060 continue;
2061 }
2062
2063 /*
2064 * Special checks for "else" and "elsif".
2065 */
2066 if ((strcmp(name, "else") == 0) || (strcmp(name, "elsif") == 0)) {
2067 /*
2068 * We ran into one without a preceding "if" or "elsif".
2069 * That's not allowed.
2070 */
2071 if (!was_if) {
2072 cf_log_err(ci, "Invalid location for '%s'. There is no preceding "
2073 "'if' or 'elsif' statement", name);
2074 talloc_free(c);
2075 return NULL;
2076 }
2077
2078 /*
2079 * There was a previous "if" or "elsif" which was always taken.
2080 * So we skip this "elsif" or "else".
2081 */
2082 if (skip_else) {
2083 void *ptr;
2084
2085 /*
2086 * And manually free this.
2087 */
2088 ptr = cf_data_remove(subcs, xlat_exp_head_t, NULL);
2089 talloc_free(ptr);
2090
2092
2093 cf_log_debug_prefix(ci, "Skipping contents of '%s' due to previous "
2094 "'%s' being always being taken.",
2095 name, skip_else);
2096 continue;
2097 }
2098 }
2099
2100 /*
2101 * Otherwise it's a real keyword.
2102 */
2103 single = compile_item(c, unlang_ctx, ci);
2104 if (!single) {
2105 cf_log_err(ci, "Failed to parse \"%s\" subsection", cf_section_name1(subcs));
2106 talloc_free(c);
2107 return NULL;
2108 }
2109
2110 goto add_child;
2111
2112 } else if (cf_item_is_pair(ci)) {
2113 CONF_PAIR *cp = cf_item_to_pair(ci);
2114
2115 /*
2116 * Variable definition.
2117 */
2118 if (cf_pair_operator(cp) == T_OP_CMP_TRUE) {
2119 if (compile_variable(c, unlang_ctx, cp, &t_rules) < 0) {
2120 talloc_free(c);
2121 return NULL;
2122 }
2123
2124 single = UNLANG_IGNORE;
2125 goto add_child;
2126 }
2127
2128 if (!cf_pair_value(cp)) {
2129 single = compile_item(c, unlang_ctx, ci);
2130 if (!single) {
2131 cf_log_err(ci, "Invalid keyword \"%s\".", cf_pair_attr(cp));
2132 talloc_free(c);
2133 return NULL;
2134 }
2135
2136 goto add_child;
2137 }
2138
2139 /*
2140 * What remains MUST be an edit pair. At this point, the state of the compiler
2141 * tells us what it is, and we don't really care if there's a leading '&'.
2142 */
2143 single = compile_edit_pair(c, unlang_ctx, cp);
2144 if (!single) {
2145 talloc_free(c);
2146 return NULL;
2147 }
2148
2149 goto add_child;
2150 } else {
2151 cf_log_err(ci, "Asked to compile unknown conf type");
2152 talloc_free(c);
2153 return NULL;
2154 }
2155
2156 add_child:
2157 if (single == UNLANG_IGNORE) continue;
2158
2159 /*
2160 * Do optimizations for "if" and "elsif"
2161 * conditions.
2162 */
2163 switch (single->type) {
2164 case UNLANG_TYPE_ELSIF:
2165 case UNLANG_TYPE_IF:
2166 was_if = true;
2167
2168 {
2169 unlang_group_t *f;
2170 unlang_cond_t *gext;
2171
2172 /*
2173 * Skip else, and/or omit things which will never be run.
2174 */
2175 f = unlang_generic_to_group(single);
2176 gext = unlang_group_to_cond(f);
2177
2178 if (gext->is_truthy) {
2179 if (gext->value) {
2180 skip_else = single->debug_name;
2181 } else {
2182 /*
2183 * The condition never
2184 * matches, so we can
2185 * avoid putting it into
2186 * the unlang tree.
2187 */
2188 talloc_free(single);
2189 continue;
2190 }
2191 }
2192 }
2193 break;
2194
2195 default:
2196 was_if = false;
2197 skip_else = NULL;
2198 break;
2199 }
2200
2201 /*
2202 * unlang_group_t is grown by adding a unlang_t to the end
2203 */
2204 fr_assert(g == talloc_parent(single));
2206 fr_assert(!single->next);
2207
2208 *g->tail = single;
2209 g->tail = &single->next;
2210 g->num_children++;
2211
2212 /*
2213 * If it's not possible to execute statement
2214 * after the current one, then just stop
2215 * processing the children.
2216 */
2217 if (g->self.closed) {
2218 cf_log_warn(ci, "Skipping remaining instructions due to '%s'",
2219 single->name);
2220 break;
2221 }
2222 }
2223
2224 /*
2225 * Set the default actions, if they haven't already been
2226 * set by an "actions" section above.
2227 */
2228 if (set_action_defaults) compile_action_defaults(c, unlang_ctx);
2229
2230 return c;
2231}
2232
2233
2234/*
2235 * Generic "compile a section with more unlang inside of it".
2236 */
2238 unlang_ext_t const *ext)
2239{
2240 unlang_group_t *g;
2241 unlang_t *c;
2242 char const *name1, *name2;
2243
2244 fr_assert(unlang_ctx->rules != NULL);
2245 fr_assert(unlang_ctx->rules->attr.list_def);
2246
2247 /*
2248 * We always create a group, even if the section is empty.
2249 */
2250 g = group_allocate(parent, cs, ext);
2251 if (!g) return NULL;
2252
2254
2255 /*
2256 * Remember the name for printing, etc.
2257 */
2258 name1 = cf_section_name1(cs);
2259 name2 = cf_section_name2(cs);
2260 c->name = name1;
2261
2262 /*
2263 * Make sure to tell the user that we're running a
2264 * policy, and not anything else.
2265 */
2266 if (ext->type == UNLANG_TYPE_POLICY) {
2267 MEM(c->debug_name = talloc_typed_asprintf(c, "policy %s", name1));
2268
2269 } else if (!name2) {
2270 c->debug_name = c->name;
2271
2272 } else {
2273 MEM(c->debug_name = talloc_typed_asprintf(c, "%s %s", name1, name2));
2274 }
2275
2276 return compile_children(g, unlang_ctx, true);
2277}
2278
2279
2281{
2282 static unlang_ext_t const group = {
2284 .len = sizeof(unlang_group_t),
2285 .type_name = "unlang_group_t",
2286 };
2287
2288 if (!cf_item_next(ci, NULL)) return UNLANG_IGNORE;
2289
2291}
2292
2294 { L("case"), 1 },
2295 { L("else"), 1 },
2296 { L("elsif"), 1 },
2297 { L("foreach"), 1 },
2298 { L("group"), 1 },
2299 { L("if"), 1 },
2300 { L("limit"), 1 },
2301 { L("load-balance"), 1 },
2302 { L("redundant"), 1 },
2303 { L("redundant-load-balance"), 1 },
2304 { L("switch"), 1 },
2305 { L("timeout"), 1 },
2306 { L("transaction"), 1 },
2307};
2309
2310/** Limit the operations which can appear in a transaction.
2311 */
2313{
2314 CONF_ITEM *ci = NULL;
2315
2316 while ((ci = cf_item_next(cs, ci)) != NULL) {
2317 char const *name;
2318
2319 if (cf_item_is_section(ci)) {
2320 CONF_SECTION *subcs;
2321
2322 subcs = cf_item_to_section(ci);
2323 name = cf_section_name1(subcs);
2324
2325 if (strcmp(name, "actions") == 0) continue;
2326
2327 /*
2328 * Definitely an attribute editing thing.
2329 */
2330 if (*name == '&') continue;
2331
2333
2335 cf_log_err(ci, "Invalid keyword in 'transaction'");
2336 return false;
2337 }
2338
2339 if (!transaction_ok(subcs)) return false;
2340
2341 continue;
2342
2343 } else if (cf_item_is_pair(ci)) {
2344 CONF_PAIR *cp;
2345
2346 cp = cf_item_to_pair(ci);
2347 name = cf_pair_attr(cp);
2348
2349 /*
2350 * If there's a value then it's not a module call.
2351 */
2352 if (cf_pair_value(cp)) continue;
2353
2354 if (*name == '&') continue;
2355
2356 /*
2357 * Allow rcodes via the "always" module.
2358 */
2360 continue;
2361 }
2362
2363 cf_log_err(ci, "Invalid module reference in 'transaction'");
2364 return false;
2365
2366 } else {
2367 continue;
2368 }
2369 }
2370
2371 return true;
2372}
2373
2375{
2377 unlang_group_t *g;
2378 unlang_t *c;
2379 unlang_compile_t unlang_ctx2;
2380
2381 static unlang_ext_t const transaction = {
2383 .len = sizeof(unlang_transaction_t),
2384 .type_name = "unlang_transaction_t",
2385 };
2386
2387 if (cf_section_name2(cs) != NULL) {
2388 cf_log_err(cs, "Unexpected argument to 'transaction' section");
2389 cf_log_err(ci, DOC_KEYWORD_REF(transaction));
2390 return NULL;
2391 }
2392
2393 /*
2394 * The transaction is empty, ignore it.
2395 */
2396 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
2397
2398 if (!transaction_ok(cs)) return NULL;
2399
2400 /*
2401 * Any failure is return, not continue.
2402 */
2403 compile_copy_context(&unlang_ctx2, unlang_ctx);
2404
2409
2410 g = group_allocate(parent, cs, &transaction);
2411 if (!g) return NULL;
2412
2414 c->debug_name = c->name = cf_section_name1(cs);
2415
2416 if (!compile_children(g, &unlang_ctx2, false)) return NULL;
2417
2418 /*
2419 * The default for a failed transaction is to continue on
2420 * failure.
2421 */
2425
2427
2428 return c;
2429}
2430
2432{
2434 unlang_group_t *g;
2435 unlang_t *c;
2436 CONF_ITEM *next;
2437
2438 static unlang_ext_t const ext = {
2440 .len = sizeof(unlang_try_t),
2441 .type_name = "unlang_try_t",
2442 };
2443
2444 /*
2445 * The transaction is empty, ignore it.
2446 */
2447 if (!cf_item_next(cs, NULL)) {
2448 cf_log_err(cs, "'try' sections cannot be empty");
2449 print_url:
2450 cf_log_err(ci, DOC_KEYWORD_REF(try));
2451 return NULL;
2452 }
2453
2454 if (cf_section_name2(cs) != NULL) {
2455 cf_log_err(cs, "Unexpected argument to 'try' section");
2456 goto print_url;
2457 }
2458
2459 next = cf_item_next(cf_parent(cs), ci);
2460 while (next && cf_item_is_data(next)) next = cf_item_next(cf_parent(cs), next);
2461
2462 if (!next || !cf_item_is_section(next) ||
2463 (strcmp(cf_section_name1(cf_item_to_section(next)), "catch") != 0)) {
2464 cf_log_err(cs, "'try' sections must be followed by a 'catch'");
2465 goto print_url;
2466 }
2467
2468 g = group_allocate(parent, cs, &ext);
2469 if (!g) return NULL;
2470
2472 c->debug_name = c->name = cf_section_name1(cs);
2473
2474 return compile_children(g, unlang_ctx, true);
2475}
2476
2477static int catch_argv(CONF_SECTION *cs, unlang_catch_t *ca, char const *name)
2478{
2479 int rcode;
2480
2482 if (rcode < 0) {
2483 cf_log_err(cs, "Unknown rcode '%s'.", name);
2484 return -1;
2485 }
2486
2487 if (ca->catching[rcode]) {
2488 cf_log_err(cs, "Duplicate rcode '%s'.", name);
2489 return -1;
2490 }
2491
2492 ca->catching[rcode] = true;
2493
2494 return 0;
2495}
2496
2498{
2500 unlang_group_t *g;
2501 unlang_t *c;
2502 unlang_catch_t *ca;
2503 CONF_ITEM *prev;
2504 char const *name;
2505
2506 static unlang_ext_t const ext = {
2508 .len = sizeof(unlang_catch_t),
2509 .type_name = "unlang_catch_t",
2510 };
2511
2512 prev = cf_item_prev(cf_parent(ci), ci);
2513 while (prev && cf_item_is_data(prev)) prev = cf_item_prev(cf_parent(ci), prev);
2514
2515 if (!prev || !cf_item_is_section(prev)) {
2516 fail:
2517 cf_log_err(cs, "Found 'catch' section with no previous 'try'");
2518 cf_log_err(ci, DOC_KEYWORD_REF(catch));
2519 return NULL;
2520 }
2521
2523 fr_assert(name != NULL);
2524
2525 if ((strcmp(name, "try") != 0) && (strcmp(name, "catch") != 0)) {
2526 /*
2527 * The previous thing has to be a section. And it has to
2528 * be either a "try" or a "catch".
2529 */
2530 goto fail;
2531 }
2532
2533 g = group_allocate(parent, cs, &ext);
2534 if (!g) return NULL;
2535
2537
2538 /*
2539 * Want to log what we caught
2540 */
2542
2543 ca = unlang_group_to_catch(g);
2544 if (!cf_section_name2(cs)) {
2545 /*
2546 * No arg2: catch errors
2547 */
2548 ca->catching[RLM_MODULE_REJECT] = true;
2549 ca->catching[RLM_MODULE_FAIL] = true;
2550 ca->catching[RLM_MODULE_INVALID] = true;
2551 ca->catching[RLM_MODULE_DISALLOW] = true;
2552
2553 } else {
2554 int i;
2555
2556 name = cf_section_name2(cs);
2557
2558 if (catch_argv(cs, ca, name) < 0) {
2559 talloc_free(c);
2560 return NULL;
2561 }
2562
2563 for (i = 0; (name = cf_section_argv(cs, i)) != NULL; i++) {
2564 if (catch_argv(cs, ca, name) < 0) {
2565 talloc_free(c);
2566 return NULL;
2567 }
2568 }
2569 }
2570
2571 /*
2572 * @todo - Else parse and limit the things we catch
2573 */
2574
2575 return compile_children(g, unlang_ctx, true);
2576}
2577
2578
2579static int8_t case_cmp(void const *one, void const *two)
2580{
2581 unlang_case_t const *a = (unlang_case_t const *) one; /* may not be talloc'd! See switch.c */
2582 unlang_case_t const *b = (unlang_case_t const *) two; /* may not be talloc'd! */
2583
2584 return fr_value_box_cmp(tmpl_value(a->vpt), tmpl_value(b->vpt));
2585}
2586
2587static uint32_t case_hash(void const *data)
2588{
2589 unlang_case_t const *a = (unlang_case_t const *) data; /* may not be talloc'd! */
2590
2591 return fr_value_box_hash(tmpl_value(a->vpt));
2592}
2593
2594static int case_to_key(uint8_t **out, size_t *outlen, void const *data)
2595{
2596 unlang_case_t const *a = (unlang_case_t const *) data; /* may not be talloc'd! */
2597
2598 return fr_value_box_to_key(out, outlen, tmpl_value(a->vpt));
2599}
2600
2602
2604{
2606 CONF_ITEM *subci;
2607 fr_token_t token;
2608 char const *name1, *name2;
2609 char const *type_name;
2610
2611 unlang_group_t *g;
2612 unlang_switch_t *gext;
2613
2614 unlang_t *c;
2615 ssize_t slen;
2616
2617 tmpl_rules_t t_rules;
2618
2620 fr_htrie_type_t htype;
2621
2622 static unlang_ext_t const switch_ext = {
2624 .len = sizeof(unlang_switch_t),
2625 .type_name = "unlang_switch_t",
2626 .pool_headers = TMPL_POOL_DEF_HEADERS,
2627 .pool_len = TMPL_POOL_DEF_LEN
2628 };
2629
2630 /*
2631 * We allow unknown attributes here.
2632 */
2633 t_rules = *(unlang_ctx->rules);
2634 t_rules.attr.allow_unknown = true;
2635 RULES_VERIFY(&t_rules);
2636
2637 name2 = cf_section_name2(cs);
2638 if (!name2) {
2639 cf_log_err(cs, "You must specify a variable to switch over for 'switch'");
2640 print_url:
2641 cf_log_err(ci, DOC_KEYWORD_REF(switch));
2642 return NULL;
2643 }
2644
2645 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
2646
2647 g = group_allocate(parent, cs, &switch_ext);
2648 if (!g) return NULL;
2649
2650 gext = unlang_group_to_switch(g);
2651
2652 /*
2653 * Create the template. All attributes and xlats are
2654 * defined by now.
2655 *
2656 * The 'case' statements need g->vpt filled out to ensure
2657 * that the data types match.
2658 */
2659 token = cf_section_name2_quote(cs);
2660
2661 if ((token == T_BARE_WORD) && (name2[0] != '%')) {
2662 slen = tmpl_afrom_attr_substr(gext, NULL, &gext->vpt,
2663 &FR_SBUFF_IN(name2, strlen(name2)),
2664 NULL,
2665 &t_rules);
2666 } else {
2667 slen = tmpl_afrom_substr(gext, &gext->vpt,
2668 &FR_SBUFF_IN(name2, strlen(name2)),
2669 token,
2670 NULL,
2671 &t_rules);
2672 }
2673 if (!gext->vpt) {
2674 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'switch'", name2);
2675 talloc_free(g);
2676 return NULL;
2677 }
2678
2680 c->name = "switch";
2681 c->debug_name = talloc_typed_asprintf(c, "switch %s", name2);
2682
2683 /*
2684 * Fixup the template before compiling the children.
2685 * This is so that compile_case() can do attribute type
2686 * checks / casts against us.
2687 */
2688 if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
2689 talloc_free(g);
2690 return NULL;
2691 }
2692
2693 if (tmpl_is_list(gext->vpt)) {
2694 cf_log_err(cs, "Cannot use list for 'switch' statement");
2695 error:
2696 talloc_free(g);
2697 goto print_url;
2698 }
2699
2700 if (tmpl_contains_regex(gext->vpt)) {
2701 cf_log_err(cs, "Cannot use regular expression for 'switch' statement");
2702 goto error;
2703 }
2704
2705 if (tmpl_is_data(gext->vpt)) {
2706 cf_log_err(cs, "Cannot use constant data for 'switch' statement");
2707 goto error;
2708 }
2709
2710 if (tmpl_is_xlat(gext->vpt)) {
2711 xlat_exp_head_t *xlat = tmpl_xlat(gext->vpt);
2712
2713 if (xlat->flags.constant || xlat->flags.pure) {
2714 cf_log_err(cs, "Cannot use constant data for 'switch' statement");
2715 goto error;
2716 }
2717 }
2718
2719
2720 if (tmpl_needs_resolving(gext->vpt)) {
2721 cf_log_err(cs, "Cannot resolve key for 'switch' statement");
2722 goto error;
2723 }
2724
2725 type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
2726 if (type_name) {
2728
2729 /*
2730 * Should have been caught in cf_file.c, process_switch()
2731 */
2734
2735 do_cast:
2736 if (tmpl_cast_set(gext->vpt, type) < 0) {
2737 cf_log_perr(cs, "Failed setting cast type");
2738 goto error;
2739 }
2740
2741 } else {
2742 /*
2743 * Get the return type of the tmpl. If we don't know,
2744 * mash it all to string.
2745 */
2746 type = tmpl_data_type(gext->vpt);
2747 if ((type == FR_TYPE_NULL) || (type == FR_TYPE_VOID)) {
2749 goto do_cast;
2750 }
2751 }
2752
2753 htype = fr_htrie_hint(type);
2754 if (htype == FR_HTRIE_INVALID) {
2755 cf_log_err(cs, "Invalid data type '%s' used for 'switch' statement",
2757 goto error;
2758 }
2759
2760 gext->ht = fr_htrie_alloc(gext, htype,
2764 NULL);
2765 if (!gext->ht) {
2766 cf_log_err(cs, "Failed initializing internal data structures");
2767 talloc_free(g);
2768 return NULL;
2769 }
2770
2771 /*
2772 * Walk through the children of the switch section,
2773 * ensuring that they're all 'case' statements, and then compiling them.
2774 */
2775 for (subci = cf_item_next(cs, NULL);
2776 subci != NULL;
2777 subci = cf_item_next(cs, subci)) {
2778 CONF_SECTION *subcs;
2779 unlang_t *single;
2780 unlang_case_t *case_gext;
2781
2782 if (!cf_item_is_section(subci)) {
2783 if (!cf_item_is_pair(subci)) continue;
2784
2785 cf_log_err(subci, "\"switch\" sections can only have \"case\" subsections");
2786 goto error;
2787 }
2788
2789 subcs = cf_item_to_section(subci); /* can't return NULL */
2790 name1 = cf_section_name1(subcs);
2791
2792 if (strcmp(name1, "case") != 0) {
2793 /*
2794 * We finally support "default" sections for "switch".
2795 */
2796 if (strcmp(name1, "default") == 0) {
2797 if (cf_section_name2(subcs) != 0) {
2798 cf_log_err(subci, "\"default\" sections cannot have a match argument");
2799 goto error;
2800 }
2801 goto handle_default;
2802 }
2803
2804 cf_log_err(subci, "\"switch\" sections can only have \"case\" subsections");
2805 goto error;
2806 }
2807
2808 name2 = cf_section_name2(subcs);
2809 if (!name2) {
2810 handle_default:
2811 if (gext->default_case) {
2812 cf_log_err(subci, "Cannot have two 'default' case statements");
2813 goto error;
2814 }
2815 }
2816
2817 /*
2818 * Compile the subsection.
2819 */
2820 single = compile_case(c, unlang_ctx, subci);
2821 if (!single) goto error;
2822
2823 fr_assert(single->type == UNLANG_TYPE_CASE);
2824
2825 /*
2826 * Remember the "default" section, and insert the
2827 * non-default "case" into the htrie.
2828 */
2829 case_gext = unlang_group_to_case(unlang_generic_to_group(single));
2830 if (!case_gext->vpt) {
2831 gext->default_case = single;
2832
2833 } else if (!fr_htrie_insert(gext->ht, single)) {
2834 single = fr_htrie_find(gext->ht, single);
2835
2836 /*
2837 * @todo - look up the key and get the previous one?
2838 */
2839 cf_log_err(subci, "Failed inserting 'case' statement. Is there a duplicate?");
2840
2841 if (single) cf_log_err(unlang_generic_to_group(single)->cs, "Duplicate may be here.");
2842
2843 goto error;
2844 }
2845
2846 *g->tail = single;
2847 g->tail = &single->next;
2848 g->num_children++;
2849 }
2850
2852
2853 return c;
2854}
2855
2857{
2859 int i;
2860 char const *name2;
2861 unlang_t *c;
2862 unlang_group_t *case_g;
2863 unlang_case_t *case_gext;
2864 tmpl_t *vpt = NULL;
2865 tmpl_rules_t t_rules;
2866
2867 static unlang_ext_t const case_ext = {
2869 .len = sizeof(unlang_case_t),
2870 .type_name = "unlang_case_t",
2871 };
2872
2873 /*
2874 * We allow unknown attributes here.
2875 */
2876 t_rules = *(unlang_ctx->rules);
2877 t_rules.attr.allow_unknown = true;
2878 RULES_VERIFY(&t_rules);
2879
2880 if (!parent || (parent->type != UNLANG_TYPE_SWITCH)) {
2881 cf_log_err(cs, "\"case\" statements may only appear within a \"switch\" section");
2882 cf_log_err(ci, DOC_KEYWORD_REF(case));
2883 return NULL;
2884 }
2885
2886 /*
2887 * case THING means "match THING"
2888 * case means "match anything"
2889 */
2890 name2 = cf_section_name2(cs);
2891 if (name2) {
2892 ssize_t slen;
2893 fr_token_t quote;
2894 unlang_group_t *switch_g;
2895 unlang_switch_t *switch_gext;
2896
2897 switch_g = unlang_generic_to_group(parent);
2898 switch_gext = unlang_group_to_switch(switch_g);
2899
2900 fr_assert(switch_gext->vpt != NULL);
2901
2902 /*
2903 * We need to cast case values to match
2904 * what we're switching over, otherwise
2905 * integers of different widths won't
2906 * match.
2907 */
2908 t_rules.cast = tmpl_expanded_type(switch_gext->vpt);
2909
2910 /*
2911 * Need to pass the attribute from switch
2912 * to tmpl rules so we can convert the
2913 * case string to an integer value.
2914 */
2915 if (tmpl_is_attr(switch_gext->vpt)) {
2916 fr_dict_attr_t const *da = tmpl_attr_tail_da(switch_gext->vpt);
2917 if (da->flags.has_value) t_rules.enumv = da;
2918 }
2919
2920 quote = cf_section_name2_quote(cs);
2921
2922 slen = tmpl_afrom_substr(cs, &vpt,
2923 &FR_SBUFF_IN(name2, strlen(name2)),
2924 quote,
2925 NULL,
2926 &t_rules);
2927 if (!vpt) {
2928 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'case'", name2);
2929 return NULL;
2930 }
2931
2932 /*
2933 * Bare word strings are attribute references
2934 */
2936 fail_attr:
2937 cf_log_err(cs, "arguments to 'case' statements MUST NOT be attribute references.");
2938 goto fail;
2939 }
2940
2942 cf_log_err(cs, "arguments to 'case' statements MUST be static data.");
2943 fail:
2945 return NULL;
2946 }
2947
2948 /*
2949 * References to unresolved attributes are forbidden. They are no longer "bare word
2950 * strings".
2951 */
2952 if ((quote == T_BARE_WORD) && (tmpl_value_type(vpt) == FR_TYPE_STRING)) {
2953 goto fail_attr;
2954 }
2955
2956 } /* else it's a default 'case' statement */
2957
2958 /*
2959 * If we were asked to match something, then we MUST
2960 * match it, even if the section is empty. Otherwise we
2961 * will silently skip the match, and then fall through to
2962 * the "default" statement.
2963 */
2964 c = compile_section(parent, unlang_ctx, cs, &case_ext);
2965 if (!c) {
2967 return NULL;
2968 }
2969
2970 case_g = unlang_generic_to_group(c);
2971 case_gext = unlang_group_to_case(case_g);
2972 case_gext->vpt = talloc_steal(case_gext, vpt);
2973
2974 /*
2975 * Set all of it's codes to return, so that
2976 * when we pick a 'case' statement, we don't
2977 * fall through to processing the next one.
2978 */
2979 for (i = 0; i < RLM_MODULE_NUMCODES; i++) c->actions.actions[i] = MOD_ACTION_RETURN;
2980
2981 return c;
2982}
2983
2985{
2987 char const *name2;
2988 unlang_t *c;
2989 unlang_group_t *g;
2990 unlang_timeout_t *gext;
2992 tmpl_t *vpt = NULL;
2993 fr_token_t token;
2994
2995 static unlang_ext_t const timeout_ext = {
2997 .len = sizeof(unlang_timeout_t),
2998 .type_name = "unlang_timeout_t",
2999 };
3000
3001 /*
3002 * Timeout <time ref>
3003 */
3004 name2 = cf_section_name2(cs);
3005 if (!name2) {
3006 cf_log_err(cs, "You must specify a time value for 'timeout'");
3007 print_url:
3008 cf_log_err(ci, DOC_KEYWORD_REF(timeout));
3009 return NULL;
3010 }
3011
3012 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
3013
3014 g = group_allocate(parent, cs, &timeout_ext);
3015 if (!g) return NULL;
3016
3017 gext = unlang_group_to_timeout(g);
3018
3019 token = cf_section_name2_quote(cs);
3020
3021 if ((token == T_BARE_WORD) && isdigit((uint8_t) *name2)) {
3022 if (fr_time_delta_from_str(&timeout, name2, strlen(name2), FR_TIME_RES_SEC) < 0) {
3023 cf_log_err(cs, "Failed parsing time delta %s - %s",
3024 name2, fr_strerror());
3025 return NULL;
3026 }
3027 } else {
3028 ssize_t slen;
3029 tmpl_rules_t t_rules;
3030
3031 /*
3032 * We don't allow unknown attributes here.
3033 */
3034 t_rules = *(unlang_ctx->rules);
3035 t_rules.attr.allow_unknown = false;
3036 RULES_VERIFY(&t_rules);
3037
3038 slen = tmpl_afrom_substr(gext, &vpt,
3039 &FR_SBUFF_IN(name2, strlen(name2)),
3040 token,
3041 NULL,
3042 &t_rules);
3043 if (!vpt) {
3044 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'timeout'", name2);
3045 talloc_free(g);
3046 return NULL;
3047 }
3048
3049 /*
3050 * Fixup the tmpl so that we know it's somewhat sane.
3051 */
3052 if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
3053 talloc_free(g);
3054 return NULL;
3055 }
3056
3057 if (tmpl_is_list(vpt)) {
3058 cf_log_err(cs, "Cannot use list as argument for 'timeout' statement");
3059 error:
3060 talloc_free(g);
3061 goto print_url;
3062 }
3063
3064 if (tmpl_contains_regex(vpt)) {
3065 cf_log_err(cs, "Cannot use regular expression as argument for 'timeout' statement");
3066 goto error;
3067 }
3068
3069 /*
3070 * Attribute or data MUST be cast to TIME_DELTA.
3071 */
3073 cf_log_perr(cs, "Failed setting cast type");
3074 goto error;
3075 }
3076 }
3077
3078 /*
3079 * Compile the contents of a "timeout".
3080 */
3081 c = compile_section(parent, unlang_ctx, cs, &timeout_ext);
3082 if (!c) return NULL;
3083
3085 gext = unlang_group_to_timeout(g);
3086 gext->timeout = timeout;
3087 gext->vpt = vpt;
3088
3089 return c;
3090}
3091
3093{
3095 char const *name2;
3096 unlang_t *c;
3097 unlang_group_t *g;
3098 unlang_limit_t *gext;
3099 tmpl_t *vpt = NULL;
3100 uint32_t limit = 0;
3101 fr_token_t token;
3102 ssize_t slen;
3103 tmpl_rules_t t_rules;
3104
3105 static unlang_ext_t const limit_ext = {
3107 .len = sizeof(unlang_limit_t),
3108 .type_name = "unlang_limit_t",
3109 };
3110
3111 /*
3112 * limit <number>
3113 */
3114 name2 = cf_section_name2(cs);
3115 if (!name2) {
3116 cf_log_err(cs, "You must specify a value for 'limit'");
3117 print_url:
3118 cf_log_err(ci, DOC_KEYWORD_REF(limit));
3119 return NULL;
3120 }
3121
3122 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
3123
3124 g = group_allocate(parent, cs, &limit_ext);
3125 if (!g) return NULL;
3126
3127 gext = unlang_group_to_limit(g);
3128
3129 token = cf_section_name2_quote(cs);
3130
3131 /*
3132 * We don't allow unknown attributes here.
3133 */
3134 t_rules = *(unlang_ctx->rules);
3135 t_rules.attr.allow_unknown = false;
3136 RULES_VERIFY(&t_rules);
3137
3138 slen = tmpl_afrom_substr(gext, &vpt,
3139 &FR_SBUFF_IN(name2, strlen(name2)),
3140 token,
3141 NULL,
3142 &t_rules);
3143 if (!vpt) {
3144 syntax_error:
3145 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'foreach'", name2);
3146 talloc_free(g);
3147 return NULL;
3148 }
3149
3150 /*
3151 * Fixup the tmpl so that we know it's somewhat sane.
3152 */
3153 if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
3154 talloc_free(g);
3155 return NULL;
3156 }
3157
3158 if (tmpl_is_list(vpt)) {
3159 cf_log_err(cs, "Cannot use list as argument for 'limit' statement");
3160 error:
3161 talloc_free(g);
3162 goto print_url;
3163 }
3164
3165 if (tmpl_contains_regex(vpt)) {
3166 cf_log_err(cs, "Cannot use regular expression as argument for 'limit' statement");
3167 goto error;
3168 }
3169
3170 if (tmpl_is_data(vpt) && (token == T_BARE_WORD)) {
3171 fr_value_box_t box;
3172
3173 if (fr_value_box_cast(NULL, &box, FR_TYPE_UINT32, NULL, tmpl_value(vpt)) < 0) goto syntax_error;
3174
3175 limit = box.vb_uint32;
3176
3177 } else {
3178 /*
3179 * Attribute or data MUST be cast to a 32-bit unsigned number.
3180 */
3181 if (tmpl_cast_set(vpt, FR_TYPE_UINT32) < 0) {
3182 cf_log_perr(cs, "Failed setting cast type");
3183 goto syntax_error;
3184 }
3185 }
3186
3187 /*
3188 * Compile the contents of a "limit".
3189 */
3190 c = compile_section(parent, unlang_ctx, cs, &limit_ext);
3191 if (!c) return NULL;
3192
3194 gext = unlang_group_to_limit(g);
3195 gext->limit = limit;
3196 gext->vpt = vpt;
3197
3198 return c;
3199}
3200
3202{
3204 fr_token_t token;
3205 char const *name2;
3206 char const *type_name, *variable_name;
3208 unlang_t *c;
3209
3210 fr_type_t key_type;
3211 char const *key_name;
3212
3213 unlang_group_t *g;
3214 unlang_foreach_t *gext;
3215
3216 ssize_t slen;
3217 tmpl_t *vpt;
3218 fr_dict_attr_t const *da = NULL;
3219
3220 tmpl_rules_t t_rules;
3221 unlang_compile_t unlang_ctx2;
3222
3223 static unlang_ext_t const foreach_ext = {
3225 .len = sizeof(unlang_foreach_t),
3226 .type_name = "unlang_foreach_t",
3227 .pool_headers = TMPL_POOL_DEF_HEADERS,
3228 .pool_len = TMPL_POOL_DEF_LEN
3229 };
3230
3231 /*
3232 * Ignore empty "foreach" blocks, and don't even sanity check their arguments.
3233 */
3234 if (!cf_item_next(cs, NULL)) {
3235 return UNLANG_IGNORE;
3236 }
3237
3238 /*
3239 * We allow unknown attributes here.
3240 */
3241 t_rules = *(unlang_ctx->rules);
3242 t_rules.attr.allow_unknown = true;
3243 t_rules.attr.allow_wildcard = true;
3244 RULES_VERIFY(&t_rules);
3245
3246 name2 = cf_section_name2(cs);
3247 fr_assert(name2 != NULL); /* checked in cf_file.c */
3248
3249 /*
3250 * Allocate a group for the "foreach" block.
3251 */
3252 g = group_allocate(parent, cs, &foreach_ext);
3253 if (!g) return NULL;
3254
3256
3257 /*
3258 * Create the template. If we fail, AND it's a bare word
3259 * with &Foo-Bar, it MAY be an attribute defined by a
3260 * module. Allow it for now. The pass2 checks below
3261 * will fix it up.
3262 */
3263 token = cf_section_name2_quote(cs);
3264 if (token != T_BARE_WORD) {
3265 cf_log_err(cs, "Data being looped over in 'foreach' must be an attribute reference or dynamic expansion, not a string");
3266 print_ref:
3267 cf_log_err(ci, DOC_KEYWORD_REF(foreach));
3268 error:
3269 talloc_free(g);
3270 return NULL;
3271 }
3272
3273 slen = tmpl_afrom_substr(g, &vpt,
3274 &FR_SBUFF_IN(name2, strlen(name2)),
3275 token,
3276 NULL,
3277 &t_rules);
3278 if (!vpt) {
3279 cf_canonicalize_error(cs, slen, "Failed parsing argument to 'foreach'", name2);
3280 goto error;
3281 }
3282
3283 /*
3284 * If we don't have a negative return code, we must have a vpt
3285 * (mostly to quiet coverity).
3286 */
3287 fr_assert(vpt);
3288
3289 if (tmpl_is_attr(vpt)) {
3291 cf_log_warn(cs, "Attribute reference should be updated to use %s[*]", vpt->name);
3293 }
3294
3295 if (tmpl_attr_tail_num(vpt) != NUM_ALL) {
3296 cf_log_err(cs, "Attribute references must be of the form ...%s[*]", tmpl_attr_tail_da(vpt)->name);
3297 goto print_ref;
3298 }
3299
3300 } else if (!tmpl_contains_xlat(vpt)) {
3301 cf_log_err(cs, "Invalid content in 'foreach (...)', it must be an attribute reference or a dynamic expansion");
3302 goto print_ref;
3303 }
3304
3305 gext = unlang_group_to_foreach(g);
3306 gext->vpt = vpt;
3307
3308 c->name = "foreach";
3309 MEM(c->debug_name = talloc_typed_asprintf(c, "foreach %s", name2));
3310
3311 /*
3312 * Copy over the compilation context. This is mostly
3313 * just to ensure that retry is handled correctly.
3314 * i.e. reset.
3315 */
3316 compile_copy_context(&unlang_ctx2, unlang_ctx);
3317
3318 /*
3319 * Then over-write the new compilation context.
3320 */
3321 unlang_ctx2.section_name1 = "foreach";
3322 unlang_ctx2.section_name2 = name2;
3323 unlang_ctx2.rules = &t_rules;
3324 t_rules.parent = unlang_ctx->rules;
3325
3326 /*
3327 * If we have "type name", then define a local variable of that name.
3328 */
3329 type_name = cf_section_argv(cs, 0); /* AFTER name1, name2 */
3330
3331 key_name = cf_section_argv(cs, 2);
3332 if (key_name) {
3333 key_type = fr_table_value_by_str(fr_type_table, key_name, FR_TYPE_VOID);
3334 } else {
3335 key_type = FR_TYPE_VOID;
3336 }
3337 key_name = cf_section_argv(cs, 3);
3338
3339 if (tmpl_is_xlat(vpt)) {
3340 if (!type_name) {
3341 cf_log_err(cs, "Dynamic expansions MUST specify a data type for the variable");
3342 goto print_ref;
3343 }
3344
3346
3347 /*
3348 * No data type was specified, see if we can get one from the function.
3349 */
3350 if (type == FR_TYPE_NULL) {
3352 if (fr_type_is_leaf(type)) goto get_name;
3353
3354 cf_log_err(cs, "Unable to determine return data type from dynamic expansion");
3355 goto print_ref;
3356 }
3357
3358 if (!fr_type_is_leaf(type)) {
3359 cf_log_err(cs, "Dynamic expansions MUST specify a non-structural data type for the variable");
3360 goto print_ref;
3361 }
3362
3363 if ((key_type != FR_TYPE_VOID) && !fr_type_is_numeric(key_type)) {
3364 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be numeric", fr_type_to_str(key_type));
3365 goto print_ref;
3366 }
3367
3368 goto get_name;
3369 } else {
3371
3372 if ((key_type != FR_TYPE_VOID) && (key_type != FR_TYPE_STRING) && (key_type != FR_TYPE_UINT32)) {
3373 cf_log_err(cs, "Invalid data type '%s' for 'key' variable - it should be 'string' or 'uint32'", fr_type_to_str(key_type));
3374 goto print_ref;
3375 }
3376 }
3377
3378 if (type_name) {
3379 unlang_variable_t *var;
3380
3383
3384 /*
3385 * foreach string foo (&tlv-thing.[*]) { ... }
3386 */
3388 goto get_name;
3389 }
3390
3391 da = tmpl_attr_tail_da(vpt);
3392
3393 if (type == FR_TYPE_NULL) {
3394 type = da->type;
3395
3396 } else if (fr_type_is_leaf(type) != fr_type_is_leaf(da->type)) {
3397 incompatible:
3398 cf_log_err(cs, "Incompatible data types in foreach variable (%s), and reference %s being looped over (%s)",
3399 fr_type_to_str(type), da->name, fr_type_to_str(da->type));
3400 goto print_ref;
3401
3402 } else if (fr_type_is_structural(type) && (type != da->type)) {
3403 goto incompatible;
3404 }
3405
3406 get_name:
3407 variable_name = cf_section_argv(cs, 1);
3408
3409 /*
3410 * Define the local variables.
3411 */
3412 g->variables = var = talloc_zero(g, unlang_variable_t);
3413 if (!var) goto error;
3414
3415 var->dict = fr_dict_protocol_alloc(unlang_ctx->rules->attr.dict_def);
3416 if (!var->dict) goto error;
3417
3418 var->root = fr_dict_root(var->dict);
3419
3420 var->max_attr = 1;
3421
3422 if (define_local_variable(cf_section_to_item(cs), var, &t_rules, type, variable_name, da) < 0) goto error;
3423
3424 t_rules.attr.dict_def = var->dict;
3425 t_rules.attr.namespace = NULL;
3426
3427 /*
3428 * And ensure we have the key.
3429 */
3430 gext->value = fr_dict_attr_by_name(NULL, var->root, variable_name);
3431 fr_assert(gext->value != NULL);
3432
3433 /*
3434 * Define the local key variable. Note that we don't copy any children.
3435 */
3436 if (key_type != FR_TYPE_VOID) {
3437 if (define_local_variable(cf_section_to_item(cs), var, &t_rules, key_type, key_name, NULL) < 0) goto error;
3438
3439 gext->key = fr_dict_attr_by_name(NULL, var->root, key_name);
3440 fr_assert(gext->key != NULL);
3441 }
3442 }
3443
3444 if (!compile_children(g, &unlang_ctx2, true)) return NULL;
3445
3446 return c;
3447}
3448
3450{
3451 unlang_t *unlang;
3452
3453 static unlang_ext_t const break_ext = {
3455 .len = sizeof(unlang_group_t),
3456 .type_name = "unlang_group_t",
3457 };
3458
3459 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
3460 /*
3461 * "break" doesn't go past a return point.
3462 */
3463 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
3464
3465 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_BREAK_POINT) != 0) break;
3466 }
3467
3468 if (!unlang) {
3469 error:
3470 cf_log_err(ci, "Invalid location for 'break' - it can only be used inside 'foreach' or 'switch'");
3471 cf_log_err(ci, DOC_KEYWORD_REF(break));
3472 return NULL;
3473 }
3474
3475 parent->closed = true;
3476
3477 return compile_empty(parent, unlang_ctx, NULL, &break_ext);
3478}
3479
3481{
3482 unlang_t *unlang;
3483
3484 static unlang_ext_t const break_ext = {
3486 .len = sizeof(unlang_group_t),
3487 .type_name = "unlang_group_t",
3488 };
3489
3490 for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
3491 /*
3492 * "continue" doesn't go past a return point.
3493 */
3494 if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
3495
3496 if (unlang->type == UNLANG_TYPE_FOREACH) break;
3497 }
3498
3499 if (!unlang) {
3500 error:
3501 cf_log_err(ci, "Invalid location for 'continue' - it can only be used inside 'foreach'");
3502 cf_log_err(ci, DOC_KEYWORD_REF(break));
3503 return NULL;
3504 }
3505
3506 parent->closed = true;
3507
3508 return compile_empty(parent, unlang_ctx, NULL, &break_ext);
3509}
3510
3511
3513{
3514 unlang_t *subrequest;
3515
3516 static unlang_ext_t const detach_ext = {
3518 .len = sizeof(unlang_group_t),
3519 .type_name = "unlang_group_t",
3520 };
3521
3522 for (subrequest = parent;
3523 subrequest != NULL;
3524 subrequest = subrequest->parent) {
3525 if (subrequest->type == UNLANG_TYPE_SUBREQUEST) break;
3526 }
3527
3528 if (!subrequest) {
3529 cf_log_err(ci, "'detach' can only be used inside of a 'subrequest' section.");
3530 cf_log_err(ci, DOC_KEYWORD_REF(detach));
3531 return NULL;
3532 }
3533
3534 /*
3535 * This really overloads the functionality of
3536 * cf_item_next().
3537 */
3538 if ((parent == subrequest) && !cf_item_next(ci, ci)) {
3539 cf_log_err(ci, "'detach' cannot be used as the last entry in a section, as there is nothing more to do");
3540 return NULL;
3541 }
3542
3543 return compile_empty(parent, unlang_ctx, NULL, &detach_ext);
3544}
3545
3547{
3548 static unlang_ext_t const return_ext = {
3550 .len = sizeof(unlang_group_t),
3551 .type_name = "unlang_group_t",
3552 };
3553
3554 /*
3555 * These types are all parallel, and therefore can have a "return" in them.
3556 */
3557 switch (parent->type) {
3561 break;
3562
3563 default:
3564 parent->closed = true;
3565 break;
3566 }
3567
3568 return compile_empty(parent, unlang_ctx, NULL, &return_ext);
3569}
3570
3572{
3573 CONF_PAIR *cp = cf_item_to_pair(ci);
3574 unlang_t *c;
3575 unlang_tmpl_t *ut;
3576 ssize_t slen;
3577 char const *p = cf_pair_attr(cp);
3578 tmpl_t *vpt;
3579
3580 MEM(ut = talloc_zero(parent, unlang_tmpl_t));
3581 c = unlang_tmpl_to_generic(ut);
3582 c->parent = parent;
3583 c->next = NULL;
3584 c->name = p;
3585 c->debug_name = c->name;
3587 c->ci = CF_TO_ITEM(cp);
3588
3589 RULES_VERIFY(unlang_ctx->rules);
3590 slen = tmpl_afrom_substr(ut, &vpt,
3591 &FR_SBUFF_IN(p, talloc_array_length(p) - 1),
3593 NULL,
3594 unlang_ctx->rules);
3595 if (!vpt) {
3596 cf_canonicalize_error(cp, slen, "Failed parsing expansion", p);
3597 talloc_free(ut);
3598 return NULL;
3599 }
3600 ut->tmpl = vpt; /* const issues */
3601
3603 return c;
3604}
3605
3607 L(""),
3608 L("{"),
3609);
3610
3612 unlang_ext_t const *ext)
3613{
3614 unlang_t *c;
3615
3616 unlang_group_t *g;
3617 unlang_cond_t *gext;
3618
3619 xlat_exp_head_t *head = NULL;
3620 bool is_truthy = false, value = false;
3621 xlat_res_rules_t xr_rules = {
3623 .dict_def = unlang_ctx->rules->attr.dict_def,
3624 },
3625 };
3626
3627 if (!cf_section_name2(cs)) {
3628 cf_log_err(cs, "'%s' without condition", unlang_ops[ext->type].name);
3629 return NULL;
3630 }
3631
3632 /*
3633 * Migration support.
3634 */
3635 {
3636 char const *name2 = cf_section_name2(cs);
3637 ssize_t slen;
3638
3639 tmpl_rules_t t_rules = (tmpl_rules_t) {
3640 .parent = unlang_ctx->rules->parent,
3641 .attr = {
3642 .dict_def = xr_rules.tr_rules->dict_def,
3643 .list_def = request_attr_request,
3644 .allow_unresolved = false,
3645 .allow_unknown = false,
3646 .allow_wildcard = true,
3647 },
3648 .literals_safe_for = unlang_ctx->rules->literals_safe_for,
3649 };
3650
3651 fr_sbuff_parse_rules_t p_rules = { };
3652
3653 p_rules.terminals = &if_terminals;
3654
3655 slen = xlat_tokenize_condition(cs, &head, &FR_SBUFF_IN(name2, strlen(name2)), &p_rules, &t_rules);
3656 if (slen == 0) {
3657 cf_canonicalize_error(cs, slen, "Empty conditions are invalid", name2);
3658 return NULL;
3659 }
3660
3661 if (slen < 0) {
3662 slen++; /* fr_slen_t vs ssize_t */
3663 cf_canonicalize_error(cs, slen, "Failed parsing condition", name2);
3664 return NULL;
3665 }
3666
3667 /*
3668 * Resolve the xlat first.
3669 */
3670 if (xlat_resolve(head, &xr_rules) < 0) {
3671 cf_log_err(cs, "Failed resolving condition - %s", fr_strerror());
3672 return NULL;
3673 }
3674
3676
3678
3679 /*
3680 * If the condition is always false, we don't compile the
3681 * children.
3682 */
3683 if (is_truthy && !value) {
3684 cf_log_debug_prefix(cs, "Skipping contents of '%s' as it is always 'false'",
3685 unlang_ops[ext->type].name);
3686
3687 /*
3688 * Free the children, which frees any xlats,
3689 * conditions, etc. which were defined, but are
3690 * now entirely unused.
3691 *
3692 * However, we still need to cache the conditions, as they will be accessed at run-time.
3693 */
3694 c = compile_empty(parent, unlang_ctx, cs, ext);
3696 } else {
3697 c = compile_section(parent, unlang_ctx, cs, ext);
3698 }
3699 }
3700
3701 if (!c) return NULL;
3703
3705 gext = unlang_group_to_cond(g);
3706
3707 gext->head = head;
3708 gext->is_truthy = is_truthy;
3709 gext->value = value;
3710
3711 return c;
3712}
3713
3715{
3716 static unlang_ext_t const if_ext = {
3718 .len = sizeof(unlang_cond_t),
3719 .type_name = "unlang_cond_t",
3720 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
3721 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2)
3722 };
3723
3725}
3726
3728{
3729 static unlang_ext_t const elsif_ext = {
3731 .len = sizeof(unlang_cond_t),
3732 .type_name = "unlang_cond_t",
3733 .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
3734 .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2)
3735 };
3736
3738}
3739
3741{
3743
3744 static unlang_ext_t const else_ext = {
3746 .len = sizeof(unlang_group_t),
3747 .type_name = "unlang_group_t"
3748 };
3749
3750 if (cf_section_name2(cs)) {
3751 cf_log_err(cs, "'else' cannot have a condition");
3752 return NULL;
3753 }
3754
3755 return compile_section(parent, unlang_ctx, cs, &else_ext);
3756}
3757
3758/*
3759 * redundant, load-balance and parallel have limits on what can
3760 * go in them.
3761 */
3762static bool validate_limited_subsection(CONF_SECTION *cs, char const *name)
3763{
3764 CONF_ITEM *ci;
3765
3766 for (ci=cf_item_next(cs, NULL);
3767 ci != NULL;
3768 ci=cf_item_next(cs, ci)) {
3769 /*
3770 * If we're a redundant, etc. group, then the
3771 * intention is to call modules, rather than
3772 * processing logic. These checks aren't
3773 * *strictly* necessary, but they keep the users
3774 * from doing crazy things.
3775 */
3776 if (cf_item_is_section(ci)) {
3777 CONF_SECTION *subcs = cf_item_to_section(ci);
3778 char const *name1 = cf_section_name1(subcs);
3779
3780 /*
3781 * Allow almost anything except "else"
3782 * statements. The normal processing
3783 * falls through from "if" to "else", and
3784 * we can't do that for redundant and
3785 * load-balance sections.
3786 */
3787 if ((strcmp(name1, "else") == 0) ||
3788 (strcmp(name1, "elsif") == 0)) {
3789 cf_log_err(ci, "%s sections cannot contain a \"%s\" statement",
3790 name, name1);
3791 return false;
3792 }
3793 continue;
3794 }
3795
3796 if (cf_item_is_pair(ci)) {
3797 CONF_PAIR *cp = cf_item_to_pair(ci);
3798
3799 if (cf_pair_operator(cp) == T_OP_CMP_TRUE) return true;
3800
3801 if (cf_pair_value(cp) != NULL) {
3802 cf_log_err(cp, "Unknown keyword '%s', or invalid location", cf_pair_attr(cp));
3803 return false;
3804 }
3805 }
3806 }
3807
3808 return true;
3809}
3810
3811
3813{
3815 unlang_t *c;
3816
3817 static unlang_ext_t const redundant_ext = {
3819 .len = sizeof(unlang_group_t),
3820 .type_name = "unlang_group_t"
3821 };
3822
3823 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
3824
3826 return NULL;
3827 }
3828
3829 c = compile_section(parent, unlang_ctx, cs, &redundant_ext);
3830 if (!c) return NULL;
3831
3832 /*
3833 * We no longer care if "redundant" sections have a name. If they do, it's ignored.
3834 */
3835
3836 return c;
3837}
3838
3840 unlang_ext_t const *ext)
3841{
3842 char const *name2;
3843 unlang_t *c;
3844 unlang_group_t *g;
3846
3847 tmpl_rules_t t_rules;
3848
3849 /*
3850 * We allow unknown attributes here.
3851 */
3852 t_rules = *(unlang_ctx->rules);
3853 t_rules.attr.allow_unknown = true;
3854 RULES_VERIFY(&t_rules);
3855
3856 /*
3857 * No children? Die!
3858 */
3859 if (!cf_item_next(cs, NULL)) {
3860 cf_log_err(cs, "%s sections cannot be empty", unlang_ops[ext->type].name);
3861 return NULL;
3862 }
3863
3864 if (!validate_limited_subsection(cs, cf_section_name1(cs))) return NULL;
3865
3866 c = compile_section(parent, unlang_ctx, cs, ext);
3867 if (!c) return NULL;
3868
3870
3871 /*
3872 * Allow for keyed load-balance / redundant-load-balance sections.
3873 */
3874 name2 = cf_section_name2(cs);
3875
3876 /*
3877 * Inside of the "modules" section, it's a virtual
3878 * module. The name is a module name, not a key.
3879 */
3880 if (name2) {
3881 if (strcmp(cf_section_name1(cf_item_to_section(cf_parent(cs))), "modules") == 0) name2 = NULL;
3882 }
3883
3884 if (name2) {
3886 ssize_t slen;
3887
3888 /*
3889 * Create the template. All attributes and xlats are
3890 * defined by now.
3891 */
3894 slen = tmpl_afrom_substr(gext, &gext->vpt,
3895 &FR_SBUFF_IN(name2, strlen(name2)),
3896 type,
3897 NULL,
3898 &t_rules);
3899 if (!gext->vpt) {
3900 cf_canonicalize_error(cs, slen, "Failed parsing argument", name2);
3901 talloc_free(g);
3902 return NULL;
3903 }
3904
3905 fr_assert(gext->vpt != NULL);
3906
3907 /*
3908 * Fixup the templates
3909 */
3910 if (!pass2_fixup_tmpl(g, &gext->vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
3911 talloc_free(g);
3912 return NULL;
3913 }
3914
3915 switch (gext->vpt->type) {
3916 default:
3917 cf_log_err(cs, "Invalid type in '%s': data will not result in a load-balance key", name2);
3918 talloc_free(g);
3919 return NULL;
3920
3921 /*
3922 * Allow only these ones.
3923 */
3924 case TMPL_TYPE_XLAT:
3925 case TMPL_TYPE_ATTR:
3926 case TMPL_TYPE_EXEC:
3927 break;
3928 }
3929 }
3930
3931 return c;
3932}
3933
3935{
3936 static unlang_ext_t const load_balance_ext = {
3938 .len = sizeof(unlang_load_balance_t),
3939 .type_name = "unlang_load_balance_t"
3940 };
3941
3943}
3944
3945
3947{
3948 static unlang_ext_t const redundant_load_balance_ext = {
3950 .len = sizeof(unlang_load_balance_t),
3951 .type_name = "unlang_load_balance_t"
3952 };
3953
3954 return compile_load_balance_subsection(parent, unlang_ctx, cf_item_to_section(ci), &redundant_load_balance_ext);
3955}
3956
3958{
3960 unlang_t *c;
3961 char const *name2;
3962
3963 unlang_group_t *g;
3964 unlang_parallel_t *gext;
3965
3966 bool clone = true;
3967 bool detach = false;
3968
3969 static unlang_ext_t const parallel_ext = {
3971 .len = sizeof(unlang_parallel_t),
3972 .type_name = "unlang_parallel_t"
3973 };
3974
3975 if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
3976
3977 /*
3978 * Parallel sections can create empty children, if the
3979 * admin demands it. Otherwise, the principle of least
3980 * surprise is to copy the whole request, reply, and
3981 * config items.
3982 */
3983 name2 = cf_section_name2(cs);
3984 if (name2) {
3985 if (strcmp(name2, "empty") == 0) {
3986 clone = false;
3987
3988 } else if (strcmp(name2, "detach") == 0) {
3989 detach = true;
3990
3991 } else {
3992 cf_log_err(cs, "Invalid argument '%s'", name2);
3993 cf_log_err(ci, DOC_KEYWORD_REF(parallel));
3994 return NULL;
3995 }
3996
3997 }
3998
3999 /*
4000 * We can do "if" in parallel with other "if", but we
4001 * cannot do "else" in parallel with "if".
4002 */
4004 return NULL;
4005 }
4006
4007 c = compile_section(parent, unlang_ctx, cs, &parallel_ext);
4008 if (!c) return NULL;
4009
4011 gext = unlang_group_to_parallel(g);
4012 gext->clone = clone;
4013 gext->detach = detach;
4014
4015 return c;
4016}
4017
4019{
4021 char const *name2;
4022
4023 unlang_t *c;
4024
4025 unlang_group_t *g;
4026 unlang_subrequest_t *gext;
4027
4028 unlang_compile_t unlang_ctx2;
4029
4030 tmpl_rules_t t_rules;
4031 fr_dict_autoload_talloc_t *dict_ref = NULL;
4032
4033 fr_dict_t const *dict;
4034 fr_dict_attr_t const *da = NULL;
4035 fr_dict_enum_value_t const *type_enum = NULL;
4036
4037 ssize_t slen;
4038 char *namespace = NULL;
4039 char const *packet_name = NULL;
4040
4041 tmpl_t *vpt = NULL, *src_vpt = NULL, *dst_vpt = NULL;
4042
4043 static unlang_ext_t const subrequest_ext = {
4045 .len = sizeof(unlang_subrequest_t),
4046 .type_name = "unlang_subrequest_t",
4047 .pool_headers = (TMPL_POOL_DEF_HEADERS * 3),
4048 .pool_len = (TMPL_POOL_DEF_LEN * 3)
4049 };
4050
4051 /*
4052 * subrequest { ... }
4053 *
4054 * Create a subrequest which is of the same dictionary
4055 * and packet type as the current request.
4056 *
4057 * We assume that the Packet-Type attribute exists.
4058 */
4059 name2 = cf_section_name2(cs);
4060 if (!name2) {
4061 dict = unlang_ctx->rules->attr.dict_def;
4062 packet_name = name2 = unlang_ctx->section_name2;
4063 goto get_packet_type;
4064 }
4065
4067 cf_log_err(cs, "The arguments to 'subrequest' must be a name or an attribute reference");
4068 print_url:
4069 cf_log_err(ci, DOC_KEYWORD_REF(subrequest));
4070 return NULL;
4071 }
4072
4073 dict = unlang_ctx->rules->attr.dict_def;
4074
4075 /*
4076 * @foo is "dictionary foo", as with references in the dictionaries.
4077 *
4078 * @foo::bar is "dictionary foo, Packet-Type = ::bar"
4079 *
4080 * foo::bar is "dictionary foo, Packet-Type = ::bar"
4081 *
4082 * ::bar is "this dictionary, Packet-Type = ::bar", BUT
4083 * we don't try to parse the new dictionary name, as it
4084 * doesn't exist.
4085 */
4086 if ((name2[0] == '@') ||
4087 ((name2[0] != ':') && (name2[0] != '&') && (strchr(name2 + 1, ':') != NULL))) {
4088 char *q;
4089
4090 if (name2[0] == '@') name2++;
4091
4092 MEM(namespace = talloc_strdup(parent, name2));
4093 q = namespace;
4094
4095 while (fr_dict_attr_allowed_chars[(unsigned int) *q]) {
4096 q++;
4097 }
4098 *q = '\0';
4099
4100 dict = fr_dict_by_protocol_name(namespace);
4101 if (!dict) {
4102 dict_ref = fr_dict_autoload_talloc(NULL, &dict, namespace);
4103 if (!dict_ref) {
4104 cf_log_err(cs, "Unknown namespace in '%s'", name2);
4105 talloc_free(namespace);
4106 return NULL;
4107 }
4108 }
4109
4110 /*
4111 * Skip the dictionary name, and go to the thing
4112 * right after it.
4113 */
4114 name2 += (q - namespace);
4115 TALLOC_FREE(namespace);
4116 }
4117
4118 /*
4119 * @dict::enum is "other dictionary, Packet-Type = ::enum"
4120 * ::enum is this dictionary, "Packet-Type = ::enum"
4121 */
4122 if ((name2[0] == ':') && (name2[1] == ':')) {
4123 packet_name = name2;
4124 goto get_packet_type;
4125 }
4126
4127 /*
4128 * Can't do foo.bar.baz::foo, the enums are only used for Packet-Type.
4129 */
4130 if (strchr(name2, ':') != NULL) {
4131 cf_log_err(cs, "Reference cannot contain enum value in '%s'", name2);
4132 return NULL;
4133 }
4134
4135 /*
4136 * '&' means "attribute reference"
4137 *
4138 * Or, bare word an require_enum_prefix means "attribute reference".
4139 */
4140 slen = tmpl_afrom_attr_substr(parent, NULL, &vpt,
4141 &FR_SBUFF_IN(name2, talloc_array_length(name2) - 1),
4142 NULL, unlang_ctx->rules);
4143 if (slen <= 0) {
4144 cf_log_perr(cs, "Invalid argument to 'subrequest', failed parsing packet-type");
4145 goto print_url;
4146 }
4147
4149
4150 /*
4151 * Anything resembling an integer or string is
4152 * OK. Nothing else makes sense.
4153 */
4154 switch (tmpl_attr_tail_da(vpt)->type) {
4156 case FR_TYPE_STRING:
4157 break;
4158
4159 default:
4161 cf_log_err(cs, "Invalid data type for attribute %s. "
4162 "Must be an integer type or string", name2 + 1);
4163 goto print_url;
4164 }
4165
4166 dict = unlang_ctx->rules->attr.dict_def;
4167 packet_name = NULL;
4168
4169get_packet_type:
4170 /*
4171 * Local attributes cannot be used in a subrequest. They belong to the parent. Local attributes
4172 * are NOT copied to the subrequest.
4173 *
4174 * @todo - maybe we want to copy local variables, too? But there may be multiple nested local
4175 * variables, each with their own dictionary.
4176 */
4177 dict = fr_dict_proto_dict(dict);
4178
4179 /*
4180 * Use dict name instead of "namespace", because "namespace" can be omitted.
4181 */
4182 da = fr_dict_attr_by_name(NULL, fr_dict_root(dict), "Packet-Type");
4183 if (!da) {
4184 cf_log_err(cs, "No such attribute 'Packet-Type' in namespace '%s'", fr_dict_root(dict)->name);
4185 error:
4186 talloc_free(namespace);
4188 talloc_free(dict_ref);
4189 goto print_url;
4190 }
4191
4192 if (packet_name) {
4193 /*
4194 * Allow ::enum-name for packet types
4195 */
4196 if ((packet_name[0] == ':') && (packet_name[1] == ':')) packet_name += 2;
4197
4198 type_enum = fr_dict_enum_by_name(da, packet_name, -1);
4199 if (!type_enum) {
4200 cf_log_err(cs, "No such value '%s' for attribute 'Packet-Type' in namespace '%s'",
4201 packet_name, fr_dict_root(dict)->name);
4202 goto error;
4203 }
4204 }
4205
4206 /*
4207 * No longer needed
4208 */
4209 talloc_free(namespace);
4210
4211 /*
4212 * Source and destination arguments
4213 */
4214 {
4215 char const *dst, *src;
4216
4217 src = cf_section_argv(cs, 0);
4218 if (src) {
4219 RULES_VERIFY(unlang_ctx->rules);
4220
4221 (void) tmpl_afrom_substr(parent, &src_vpt,
4222 &FR_SBUFF_IN(src, talloc_array_length(src) - 1),
4223 cf_section_argv_quote(cs, 0), NULL, unlang_ctx->rules);
4224 if (!src_vpt) {
4225 cf_log_perr(cs, "Invalid argument to 'subrequest', failed parsing src");
4226 goto error;
4227 }
4228
4229 if (!tmpl_contains_attr(src_vpt)) {
4230 cf_log_err(cs, "Invalid argument to 'subrequest' src must be an attr or list, got %s",
4231 tmpl_type_to_str(src_vpt->type));
4232 talloc_free(src_vpt);
4233 goto error;
4234 }
4235
4236 dst = cf_section_argv(cs, 1);
4237 if (dst) {
4238 RULES_VERIFY(unlang_ctx->rules);
4239
4240 (void) tmpl_afrom_substr(parent, &dst_vpt,
4241 &FR_SBUFF_IN(dst, talloc_array_length(dst) - 1),
4242 cf_section_argv_quote(cs, 1), NULL, unlang_ctx->rules);
4243 if (!dst_vpt) {
4244 cf_log_perr(cs, "Invalid argument to 'subrequest', failed parsing dst");
4245 goto error;
4246 }
4247
4248 if (!tmpl_contains_attr(dst_vpt)) {
4249 cf_log_err(cs, "Invalid argument to 'subrequest' dst must be an "
4250 "attr or list, got %s",
4251 tmpl_type_to_str(src_vpt->type));
4252 talloc_free(src_vpt);
4253 talloc_free(dst_vpt);
4254 goto error;
4255 }
4256 }
4257 }
4258 }
4259
4260 if (!cf_item_next(cs, NULL)) {
4262 talloc_free(src_vpt);
4263 talloc_free(dst_vpt);
4264 return UNLANG_IGNORE;
4265 }
4266
4267 t_rules = *unlang_ctx->rules;
4268 t_rules.parent = unlang_ctx->rules;
4269 t_rules.attr.dict_def = dict;
4270 t_rules.attr.allow_foreign = false;
4271
4272 /*
4273 * Copy over the compilation context. This is mostly
4274 * just to ensure that retry is handled correctly.
4275 * i.e. reset.
4276 */
4277 compile_copy_context(&unlang_ctx2, unlang_ctx);
4278
4279 /*
4280 * Then over-write the new compilation context.
4281 */
4282 unlang_ctx2.section_name1 = "subrequest";
4283 unlang_ctx2.section_name2 = name2;
4284 unlang_ctx2.rules = &t_rules;
4285
4286 /*
4287 * Compile the subsection with a *different* default dictionary.
4288 */
4289 c = compile_section(parent, &unlang_ctx2, cs, &subrequest_ext);
4290 if (!c) return NULL;
4291
4292 /*
4293 * Set the dictionary and packet information, which tells
4294 * unlang_subrequest() how to process the request.
4295 */
4298
4299 if (dict_ref) {
4300 /*
4301 * Parent the dictionary reference correctly now that we
4302 * have the section with the dependency. This should
4303 * be fast as dict_ref has no siblings.
4304 */
4305 talloc_steal(gext, dict_ref);
4306 }
4307 if (vpt) gext->vpt = talloc_steal(gext, vpt);
4308
4309 gext->dict = dict;
4310 gext->attr_packet_type = da;
4311 gext->type_enum = type_enum;
4312 gext->src = src_vpt;
4313 gext->dst = dst_vpt;
4314
4315 return c;
4316}
4317
4318
4320{
4322 virtual_server_t const *vs;
4323 unlang_t *c;
4324
4325 unlang_group_t *g;
4326 unlang_call_t *gext;
4327
4329 char const *server;
4330 CONF_SECTION *server_cs;
4331 fr_dict_t const *dict;
4333
4334 static unlang_ext_t const call_ext = {
4336 .len = sizeof(unlang_call_t),
4337 .type_name = "unlang_call_t",
4338 };
4339
4340 server = cf_section_name2(cs);
4341 if (!server) {
4342 cf_log_err(cs, "You MUST specify a server name for 'call <server> { ... }'");
4343 print_url:
4344 cf_log_err(ci, DOC_KEYWORD_REF(call));
4345 return NULL;
4346 }
4347
4349 if (type != T_BARE_WORD) {
4350 cf_log_err(cs, "The arguments to 'call' cannot be a quoted string or a dynamic value");
4351 goto print_url;
4352 }
4353
4354 vs = virtual_server_find(server);
4355 if (!vs) {
4356 cf_log_err(cs, "Unknown virtual server '%s'", server);
4357 return NULL;
4358 }
4359
4360 server_cs = virtual_server_cs(vs);
4361
4362 /*
4363 * The dictionaries are not compatible, forbid it.
4364 */
4365 dict = virtual_server_dict_by_name(server);
4366 if (!dict) {
4367 cf_log_err(cs, "Cannot call virtual server '%s', failed retrieving its namespace",
4368 server);
4369 return NULL;
4370 }
4371 if ((dict != fr_dict_internal()) && fr_dict_internal() &&
4372 unlang_ctx->rules->attr.dict_def && !fr_dict_compatible(unlang_ctx->rules->attr.dict_def, dict)) {
4373 cf_log_err(cs, "Cannot call server %s with namespace '%s' from namespaces '%s' - they have incompatible protocols",
4374 server, fr_dict_root(dict)->name, fr_dict_root(unlang_ctx->rules->attr.dict_def)->name);
4375 return NULL;
4376 }
4377
4378 attr_packet_type = fr_dict_attr_by_name(NULL, fr_dict_root(dict), "Packet-Type");
4379 if (!attr_packet_type) {
4380 cf_log_err(cs, "Cannot call server %s with namespace '%s' - it has no Packet-Type attribute",
4381 server, fr_dict_root(dict)->name);
4382 return NULL;
4383 }
4384
4385 c = compile_section(parent, unlang_ctx, cs, &call_ext);
4386 if (!c) return NULL;
4387
4388 /*
4389 * Set the virtual server name, which tells unlang_call()
4390 * which virtual server to call.
4391 */
4393 gext = unlang_group_to_call(g);
4394 gext->server_cs = server_cs;
4396
4397 return c;
4398}
4399
4400
4402{
4404 unlang_t *c;
4405
4406 unlang_group_t *g;
4407 unlang_caller_t *gext;
4408
4410 char const *name;
4411 fr_dict_t const *dict;
4412 unlang_compile_t unlang_ctx2;
4413 tmpl_rules_t parent_rules, t_rules;
4414
4415 fr_dict_autoload_talloc_t *dict_ref = NULL;
4416
4417 static unlang_ext_t const caller_ext = {
4419 .len = sizeof(unlang_caller_t),
4420 .type_name = "unlang_caller_t",
4421 };
4422
4423 name = cf_section_name2(cs);
4424 if (!name) {
4425 cf_log_err(cs, "You MUST specify a protocol name for 'caller <protocol> { ... }'");
4426 print_url:
4427 cf_log_err(ci, DOC_KEYWORD_REF(caller));
4428 return NULL;
4429 }
4430
4432 if (type != T_BARE_WORD) {
4433 cf_log_err(cs, "The argument to 'caller' cannot be a quoted string or a dynamic value");
4434 goto print_url;
4435 }
4436
4438 if (!dict) {
4439 dict_ref = fr_dict_autoload_talloc(NULL, &dict, name);
4440 if (!dict_ref) {
4441 cf_log_perr(cs, "Unknown protocol '%s'", name);
4442 goto print_url;
4443 }
4444 }
4445
4446 /*
4447 * Create a new parent context with the new dictionary.
4448 */
4449 memcpy(&parent_rules, unlang_ctx->rules, sizeof(parent_rules));
4450 memcpy(&t_rules, unlang_ctx->rules, sizeof(t_rules));
4451 parent_rules.attr.dict_def = dict;
4452 t_rules.parent = &parent_rules;
4453
4454 /*
4455 * We don't want to modify the context we were passed, so
4456 * we just clone it
4457 */
4458 memcpy(&unlang_ctx2, unlang_ctx, sizeof(unlang_ctx2));
4459 unlang_ctx2.rules = &t_rules;
4460 unlang_ctx2.section_name1 = "caller";
4461 unlang_ctx2.section_name2 = name;
4462
4463 c = compile_section(parent, &unlang_ctx2, cs, &caller_ext);
4464 if (!c) {
4465 talloc_free(dict_ref);
4466 return NULL;
4467 }
4468
4469 /*
4470 * Set the virtual server name, which tells unlang_call()
4471 * which virtual server to call.
4472 */
4474 gext = unlang_group_to_caller(g);
4475 gext->dict = dict;
4476
4477 if (dict_ref) {
4478 /*
4479 * Parent the dictionary reference correctly now that we
4480 * have the section with the dependency. This should
4481 * be fast as dict_ref has no siblings.
4482 */
4483 talloc_steal(gext, dict_ref);
4484 }
4485
4486 if (!g->num_children) {
4487 talloc_free(c);
4488 return UNLANG_IGNORE;
4489 }
4490
4491 return c;
4492}
4493
4495 CONF_SECTION *subcs,
4496 bool policy)
4497{
4498 unlang_compile_t unlang_ctx2;
4499 unlang_t *c;
4500
4501 static unlang_ext_t const policy_ext = {
4503 .len = sizeof(unlang_group_t),
4504 .type_name = "unlang_group_t",
4505 };
4506
4507 static unlang_ext_t const group_ext = {
4509 .len = sizeof(unlang_group_t),
4510 .type_name = "unlang_group_t",
4511 };
4512
4513 /*
4514 * module.c takes care of ensuring that this is:
4515 *
4516 * group foo { ...
4517 * load-balance foo { ...
4518 * redundant foo { ...
4519 * redundant-load-balance foo { ...
4520 *
4521 * We can just recurse to compile the section as
4522 * if it was found here.
4523 */
4524 if (cf_section_name2(subcs)) {
4526
4527 if (policy) {
4528 cf_log_err(subcs, "Unexpected second name in policy");
4529 return NULL;
4530 }
4531
4532 c = compile_item(parent, &unlang_ctx2, cf_section_to_item(subcs));
4533
4534 } else {
4536
4537 /*
4538 * We have:
4539 *
4540 * foo { ...
4541 *
4542 * So we compile it like it was:
4543 *
4544 * group foo { ...
4545 */
4546 c = compile_section(parent, &unlang_ctx2, subcs,
4547 policy ? &policy_ext : &group_ext);
4548 }
4549 if (!c) return NULL;
4551
4552 /*
4553 * Return the compiled thing if we can.
4554 */
4555 if (!cf_item_is_section(ci)) return c;
4556
4557 /*
4558 * Else we have a reference to a policy, and that reference
4559 * over-rides the return codes for the policy!
4560 */
4561 if (!unlang_compile_actions(&c->actions, cf_item_to_section(ci), false)) {
4562 talloc_free(c);
4563 return NULL;
4564 }
4565
4566 return c;
4567}
4568
4569/** Load a named module from the virtual module list, or from the "policy" subsection.
4570 *
4571 * If it's "foo.method", look for "foo", and return "method" as the method
4572 * we wish to use, instead of the input component.
4573 *
4574 * @param[in] ci Configuration item to check
4575 * @param[in] real_name Complete name string e.g. foo.authorize.
4576 * @param[in] virtual_name Virtual module name e.g. foo.
4577 * @param[in] method_name Method override (may be NULL) or the method
4578 * name e.g. authorize.
4579 * @param[in] unlang_ctx Unlang context this call is being compiled in.
4580 * @param[out] policy whether or not this thing was a policy
4581 * @return the CONF_SECTION specifying the virtual module.
4582 */
4583static CONF_SECTION *virtual_module_find_cs(CONF_ITEM *ci, UNUSED char const *real_name, char const *virtual_name,
4584 char const *method_name, unlang_compile_t *unlang_ctx, bool *policy)
4585{
4586 CONF_SECTION *cs, *subcs, *conf_root;
4587 CONF_ITEM *loop;
4588 char buffer[256];
4589
4590 *policy = false;
4591 conf_root = cf_root(ci);
4592
4593 /*
4594 * Look for "foo" as a virtual server. If we find it,
4595 * AND there's no method name, we've found the right
4596 * thing.
4597 *
4598 * Found "foo". Load it as "foo", or "foo.method".
4599 *
4600 * Return it to the caller, with the updated method.
4601 */
4602 subcs = module_rlm_virtual_by_name(virtual_name);
4603 if (subcs) goto check_for_loop;
4604
4605 /*
4606 * Look for it in "policy".
4607 *
4608 * If there's no policy section, we can't do anything else.
4609 */
4610 cs = cf_section_find(conf_root, "policy", NULL);
4611 if (!cs) return NULL;
4612
4613 *policy = true;
4614
4615 /*
4616 * "foo.authorize" means "load policy 'foo.authorize' or 'foo'"
4617 * as method "authorize".
4618 *
4619 * And bail out if there's no policy "foo.authorize" or "foo".
4620 */
4621 if (method_name) {
4622 snprintf(buffer, sizeof(buffer), "%s.%s", virtual_name, method_name);
4623 subcs = cf_section_find(cs, buffer, NULL);
4624 if (!subcs) subcs = cf_section_find(cs, virtual_name, NULL);
4625 if (!subcs) return NULL;
4626
4627 goto check_for_loop;
4628 }
4629
4630 /*
4631 * "foo" means "look for foo.name1.name2" first, to allow
4632 * method overrides. If that's not found, look for
4633 * "foo.name1" and if that's not found just look for
4634 * a policy "foo".
4635 */
4636 snprintf(buffer, sizeof(buffer), "%s.%s.%s", virtual_name, unlang_ctx->section_name1, unlang_ctx->section_name2);
4637 subcs = cf_section_find(cs, buffer, NULL);
4638
4639 if (!subcs) {
4640 snprintf(buffer, sizeof(buffer), "%s.%s", virtual_name, unlang_ctx->section_name1);
4641 subcs = cf_section_find(cs, buffer, NULL);
4642 }
4643
4644 if (!subcs) subcs = cf_section_find(cs, virtual_name, NULL);
4645 if (!subcs) return NULL;
4646
4647check_for_loop:
4648 /*
4649 * Check that we're not creating a loop. We may
4650 * be compiling an "sql" module reference inside
4651 * of an "sql" policy. If so, we allow the
4652 * second "sql" to refer to the module.
4653 */
4654 for (loop = cf_parent(ci);
4655 loop && subcs;
4656 loop = cf_parent(loop)) {
4657 if (loop == cf_section_to_item(subcs)) {
4658 return NULL;
4659 }
4660 }
4661
4662 return subcs;
4663}
4664
4666{
4667 unlang_t *c;
4668 unlang_module_t *m;
4669 fr_slen_t slen;
4670
4671 MEM(m = talloc_zero(parent, unlang_module_t));
4672 slen = module_rlm_by_name_and_method(m, &m->mmc,
4673 unlang_ctx->vs,
4674 &(section_name_t){ .name1 = unlang_ctx->section_name1, .name2 = unlang_ctx->section_name2 },
4675 &FR_SBUFF_IN(name, strlen(name)),
4676 unlang_ctx->rules);
4677 if (slen < 0) {
4678 cf_log_perr(ci, "Failed compiling module call");
4679 talloc_free(m);
4680 return NULL;
4681 }
4682
4683 if (m->mmc.rlm->common.dict &&
4684 !fr_dict_compatible(*m->mmc.rlm->common.dict, unlang_ctx->rules->attr.dict_def)) {
4685 cf_log_err(ci, "The '%s' module can only be used within a '%s' namespace.",
4686 m->mmc.rlm->common.name, fr_dict_root(*m->mmc.rlm->common.dict)->name);
4687 cf_log_err(ci, "Please use the 'subrequest' keyword to change namespaces");
4688 cf_log_err(ci, DOC_KEYWORD_REF(subrequest));
4689 talloc_free(m);
4690 return NULL;
4691 }
4692
4694 c->parent = parent;
4695 c->next = NULL;
4696
4697 c->name = talloc_typed_strdup(c, name);
4698 c->debug_name = c->name;
4700 c->ci = ci;
4701
4702 /*
4703 * Set the default actions for this module.
4704 */
4705 c->actions = m->mmc.mi->actions;
4706
4707 /*
4708 * Add in the default actions for this section.
4709 */
4711
4712 /*
4713 * Parse the method environment for this module / method
4714 */
4715 if (m->mmc.mmb.method_env) {
4717
4718 fr_assert_msg(method_env->inst_size, "Method environment for module %s, method %s %s declared, "
4719 "but no inst_size set",
4720 m->mmc.mi->name, unlang_ctx->section_name1, unlang_ctx->section_name2);
4721
4722 if (!unlang_ctx->rules) {
4723 cf_log_err(ci, "Failed compiling %s - no rules", m->mmc.mi->name);
4724 goto error;
4725 }
4727 unlang_ctx->rules, m->mmc.mi->conf,
4728 &(call_env_ctx_t){
4729 .type = CALL_ENV_CTX_TYPE_MODULE,
4730 .mi = m->mmc.mi,
4731 .asked = &m->mmc.asked
4732 });
4733 if (!m->call_env) {
4734 error:
4735 talloc_free(m);
4736 return NULL;
4737 }
4738 }
4739
4740 /*
4741 * If a module reference is a section, then the section
4742 * should contain action over-rides. We add those here.
4743 */
4744 if (cf_item_is_section(ci) &&
4746 (m->mmc.mi->exported->flags & MODULE_TYPE_RETRY) != 0)) goto error;
4747
4748 return c;
4749}
4750
4751typedef unlang_t *(*unlang_op_compile_t)(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci);
4752
4754 { L("call"), (void *) compile_call },
4755 { L("caller"), (void *) compile_caller },
4756 { L("case"), (void *) compile_case },
4757 { L("catch"), (void *) compile_catch },
4758 { L("else"), (void *) compile_else },
4759 { L("elsif"), (void *) compile_elsif },
4760 { L("foreach"), (void *) compile_foreach },
4761 { L("group"), (void *) compile_group },
4762 { L("if"), (void *) compile_if },
4763 { L("limit"), (void *) compile_limit },
4764 { L("load-balance"), (void *) compile_load_balance },
4765 { L("map"), (void *) compile_map },
4766 { L("parallel"), (void *) compile_parallel },
4767 { L("redundant"), (void *) compile_redundant },
4768 { L("redundant-load-balance"), (void *) compile_redundant_load_balance },
4769 { L("subrequest"), (void *) compile_subrequest },
4770 { L("switch"), (void *) compile_switch },
4771 { L("timeout"), (void *) compile_timeout },
4772 { L("transaction"), (void *) compile_transaction },
4773 { L("try"), (void *) compile_try },
4774 { L("update"), (void *) compile_update },
4775};
4777
4779 { L("break"), (void *) compile_break },
4780 { L("continue"), (void *) compile_continue },
4781 { L("detach"), (void *) compile_detach },
4782 { L("return"), (void *) compile_return },
4783};
4785
4786extern int dict_attr_acopy_children(fr_dict_t *dict, fr_dict_attr_t *dst, fr_dict_attr_t const *src);
4787
4789 fr_dict_attr_t const *ref)
4790{
4791 fr_dict_attr_t const *da;
4792 fr_slen_t len;
4793
4794 fr_dict_attr_flags_t flags = {
4795 .internal = true,
4796 .local = true,
4797 };
4798
4799 /*
4800 * No overlap with list names.
4801 */
4803 fail_list:
4804 cf_log_err(ci, "Local variable '%s' cannot be a list reference.", name);
4805 return -1;
4806 }
4807
4808 len = strlen(name);
4809 if (tmpl_attr_list_from_substr(&da, &FR_SBUFF_IN(name, len)) == len) goto fail_list;
4810
4811 /*
4812 * No keyword section names.
4813 */
4815 fail_unlang:
4816 cf_log_err(ci, "Local variable '%s' cannot be an unlang keyword.", name);
4817 return -1;
4818 }
4819
4820 /*
4821 * No simple keyword names.
4822 */
4823 if (fr_table_value_by_str(unlang_pair_keywords, name, NULL) != NULL) goto fail_unlang;
4824
4825 /*
4826 * No protocol names.
4827 */
4828 if (fr_dict_by_protocol_name(name) != NULL) {
4829 cf_log_err(ci, "Local variable '%s' cannot be an existing protocol name.", name);
4830 return -1;
4831 }
4832
4833 /*
4834 * No overlap with attributes in the current dictionary. The lookup in var->root will also check
4835 * the current dictionary, so the check here is really only for better error messages.
4836 */
4837 if (t_rules && t_rules->parent && t_rules->parent->attr.dict_def) {
4838 da = fr_dict_attr_by_name(NULL, fr_dict_root(t_rules->parent->attr.dict_def), name);
4839 if (da) {
4840 cf_log_err(ci, "Local variable '%s' duplicates a dictionary attribute.", name);
4841 return -1;
4842 }
4843 }
4844
4845 /*
4846 * No data types.
4847 */
4849 cf_log_err(ci, "Invalid variable name '%s'.", name);
4850 return -1;
4851 }
4852
4853 /*
4854 * No dups of local variables.
4855 */
4856 da = fr_dict_attr_by_name(NULL, var->root, name);
4857 if (da) {
4858 cf_log_err(ci, "Duplicate variable name '%s'.", name);
4859 return -1;
4860 }
4861
4862 if (fr_dict_attr_add(var->dict, var->root, name, var->max_attr, type, &flags) < 0) {
4863 fail:
4864 cf_log_err(ci, "Failed adding variable '%s' - %s", name, fr_strerror());
4865 return -1;
4866 }
4867 da = fr_dict_attr_by_name(NULL, var->root, name);
4868 fr_assert(da != NULL);
4869
4870 /*
4871 * Copy the children over.
4872 */
4874 fr_fatal_assert(ref != NULL);
4875
4876 if (fr_dict_attr_acopy_local(da, ref) < 0) goto fail;
4877 }
4878
4879 var->max_attr++;
4880
4881 return 0;
4882}
4883
4884/*
4885 * Compile one unlang instruction
4886 */
4888{
4889 char const *name, *p;
4890 CONF_SECTION *cs, *subcs, *modules;
4891 char const *realname;
4892 unlang_compile_t unlang_ctx2;
4893 bool policy;
4894 unlang_op_compile_t compile;
4895 unlang_t *c;
4896 bool ignore_notfound = false;
4897
4898 if (cf_item_is_section(ci)) {
4899 cs = cf_item_to_section(ci);
4900 name = cf_section_name1(cs);
4901
4903 if (compile) {
4904 unlang_op_t *op;
4905
4906 c = compile(parent, unlang_ctx, ci);
4907 allocate_number:
4908 if (!c) return NULL;
4909 if (c == UNLANG_IGNORE) return UNLANG_IGNORE;
4910
4911 c->number = unlang_number++;
4912
4913 /*
4914 * Only insert the per-thread allocation && instantiation if it's used.
4915 */
4916 op = &unlang_ops[c->type];
4917 if (!op->thread_inst_size) return c;
4918
4920 cf_log_err(ci, "Instruction \"%s\" number %u has conflict with previous one.",
4921 c->debug_name, c->number);
4922 talloc_free(c);
4923 return NULL;
4924 }
4925
4926 return c;
4927 }
4928
4929 /*
4930 * Forbid pair keywords as section names, e.g. "break { ... }"
4931 */
4932 if (fr_table_value_by_str(unlang_pair_keywords, name, NULL) != NULL) {
4933 cf_log_err(ci, "Syntax error after keyword '%s' - unexpected '{'", name);
4934 return NULL;
4935 }
4936
4937 /* else it's something like sql { fail = 1 ...} */
4938 goto check_for_module;
4939
4940 } else if (cf_item_is_pair(ci)) {
4941 /*
4942 * Else it's a module reference such as "sql", OR
4943 * one of the few bare keywords that we allow.
4944 */
4945 CONF_PAIR *cp = cf_item_to_pair(ci);
4946
4947 name = cf_pair_attr(cp);
4948
4949 /*
4950 * We cannot have assignments or actions here.
4951 */
4952 if (cf_pair_value(cp) != NULL) {
4953 cf_log_err(ci, "Entry is not a reference to a module");
4954 return NULL;
4955 }
4956
4957 /*
4958 * In-place expansions.
4959 *
4960 * @todo - allow only function calls, not %{...}
4961 *
4962 * @todo don't create a tmpl. Instead, create an
4963 * xlat. This functionality is needed for the in-place language functions via
4964 *
4965 * language {{{
4966 * ...
4967 * }}}
4968 */
4969 if (name[0] == '%') {
4971 goto allocate_number;
4972 }
4973
4974 compile = (unlang_op_compile_t)fr_table_value_by_str(unlang_pair_keywords, name, NULL); /* Cast for -Wpedantic */
4975 if (compile) {
4976 c = compile(parent, unlang_ctx, ci);
4977 goto allocate_number;
4978 }
4979
4980 /*
4981 * Forbid section keywords as pair names, e.g. bare "update"
4982 */
4984 cf_log_err(ci, "Syntax error after keyword '%s' - expected '{'", name);
4985 return NULL;
4986 }
4987
4988 goto check_for_module;
4989 } else {
4990 cf_log_err(ci, "Asked to compile unknown conf type");
4991 return NULL; /* who knows what it is... */
4992 }
4993
4994check_for_module:
4995 /*
4996 * We now have a name. It can be one of two forms. A
4997 * bare module name, or a section named for the module,
4998 * with over-rides for the return codes.
4999 *
5000 * The name can refer to a real module, in the "modules"
5001 * section. In that case, the name will be either the
5002 * first or second name of the sub-section of "modules".
5003 *
5004 * Or, the name can refer to a policy, in the "policy"
5005 * section. In that case, the name will be first of the
5006 * sub-section of "policy".
5007 *
5008 * Or, the name can refer to a "module.method", in which
5009 * case we're calling a different method than normal for
5010 * this section.
5011 *
5012 * Or, the name can refer to a virtual module, in the
5013 * "modules" section. In that case, the name will be
5014 * name2 of the CONF_SECTION.
5015 *
5016 * We try these in sequence, from the bottom up. This is
5017 * so that virtual modules and things in "policy" can
5018 * over-ride calls to real modules.
5019 */
5020
5021
5022 /*
5023 * Try:
5024 *
5025 * policy { ... name { .. } .. }
5026 * policy { ... name.method { .. } .. }
5027 */
5028 p = strrchr(name, '.');
5029 if (!p) {
5030 subcs = virtual_module_find_cs(ci, name, name, NULL, unlang_ctx, &policy);
5031 } else {
5032 char buffer[256];
5033
5034 strlcpy(buffer, name, sizeof(buffer));
5035 buffer[p - name] = '\0';
5036
5037 subcs = virtual_module_find_cs(ci, name,
5038 buffer, buffer + (p - name) + 1, unlang_ctx, &policy);
5039 }
5040
5041 /*
5042 * We've found the thing which defines this "function".
5043 * It MUST be a sub-section.
5044 *
5045 * i.e. it refers to a a subsection in "policy".
5046 */
5047 if (subcs) {
5048 c = compile_function(parent, unlang_ctx, ci, subcs, policy);
5049 goto allocate_number;
5050 }
5051
5052 /*
5053 * Not a function. It must be a real module.
5054 */
5055 modules = cf_section_find(cf_root(ci), "modules", NULL);
5056 if (!modules) {
5057 cf_log_err(ci, "Failed compiling \"%s\" as a module or policy as no modules are enabled", name);
5058 cf_log_err(ci, "Please verify that modules { ... } section is present in the server configuration");
5059 return NULL;
5060 }
5061
5062 realname = name;
5063
5064 /*
5065 * Try to load the optional module.
5066 */
5067 if (*realname == '-') {
5068 ignore_notfound = true;
5069 realname++;
5070 }
5071
5072 /*
5073 * Set the child compilation context BEFORE parsing the
5074 * module name and method. The lookup function will take
5075 * care of returning the appropriate component, name1,
5076 * name2, etc.
5077 */
5079 c = compile_module(parent, &unlang_ctx2, ci, realname);
5080 if (c) goto allocate_number;
5081
5082 if (ignore_notfound) {
5083 cf_log_warn(ci, "Ignoring \"%s\" as the \"%s\" module is not enabled, "
5084 "or the method does not exist", name, realname);
5085 return UNLANG_IGNORE;
5086 }
5087
5088 return NULL;
5089}
5090
5091/** Compile an unlang section for a virtual server
5092 *
5093 * @param[in] vs Virtual server to compile section for.
5094 * @param[in] cs containing the unlang calls to compile.
5095 * @param[in] actions Actions to use for the unlang section.
5096 * @param[in] rules Rules to use for the unlang section.
5097 * @param[out] instruction Pointer to store the compiled unlang section.
5098 * @return
5099 * - 0 on success.
5100 * - -1 on error.
5101 */
5103 CONF_SECTION *cs, unlang_mod_actions_t const * actions, tmpl_rules_t const *rules, void **instruction)
5104{
5105 unlang_t *c;
5106 tmpl_rules_t my_rules;
5107 char const *name1, *name2;
5108 CONF_DATA const *cd;
5109 static unlang_ext_t const group_ext = {
5111 .len = sizeof(unlang_group_t),
5112 .type_name = "unlang_group_t",
5113 };
5114
5115 /*
5116 * Don't compile it twice, and don't print out debug
5117 * messages twice.
5118 */
5119 cd = cf_data_find(cs, unlang_group_t, NULL);
5120 if (cd) {
5121 if (instruction) *instruction = cf_data_value(cd);
5122 return 1;
5123 }
5124
5125 name1 = cf_section_name1(cs);
5126 name2 = cf_section_name2(cs);
5127
5128 if (!name2) name2 = "";
5129
5130 cf_log_debug(cs, "Compiling policies in - %s %s {...}", name1, name2);
5131
5132 /*
5133 * Ensure that all compile functions get valid rules.
5134 */
5135 if (!rules) {
5136 memset(&my_rules, 0, sizeof(my_rules));
5137 rules = &my_rules;
5138 }
5139
5140 c = compile_section(NULL,
5142 .vs = vs,
5143 .section_name1 = cf_section_name1(cs),
5144 .section_name2 = cf_section_name2(cs),
5145 .actions = *actions,
5146 .rules = rules
5147 },
5148 cs, &group_ext);
5149 if (!c) return -1;
5150
5151 if (DEBUG_ENABLED4) unlang_dump(c, 2);
5152
5153 /*
5154 * Associate the unlang with the configuration section,
5155 * and free the unlang code when the configuration
5156 * section is freed.
5157 */
5158 cf_data_add(cs, c, NULL, true);
5159 if (instruction) *instruction = c;
5160
5161 return 0;
5162}
5163
5164
5165/** Check if name is an unlang keyword
5166 *
5167 * @param[in] name to check.
5168 * @return
5169 * - true if it is a keyword.
5170 * - false if it's not a keyword.
5171 */
5173{
5174 if (!name || !*name) return false;
5175
5176 if (fr_table_value_by_str(unlang_section_keywords, name, NULL) != NULL) return true;
5177
5178 return (fr_table_value_by_str(unlang_pair_keywords, name, NULL) != NULL);
5179}
5180
5181/*
5182 * These are really unlang_foo_t, but that's fine...
5183 */
5184static int8_t instruction_cmp(void const *one, void const *two)
5185{
5186 unlang_t const *a = one;
5187 unlang_t const *b = two;
5188
5189 return CMP(a->number, b->number);
5190}
5191
5192
5193void unlang_compile_init(TALLOC_CTX *ctx)
5194{
5196}
5197
5198
5199/** Create thread-specific data structures for unlang
5200 *
5201 */
5202int unlang_thread_instantiate(TALLOC_CTX *ctx)
5203{
5205 unlang_t *instruction;
5206
5207 if (unlang_thread_array) {
5208 fr_strerror_const("already initialized");
5209 return -1;
5210 }
5211
5212 MEM(unlang_thread_array = talloc_zero_array(ctx, unlang_thread_t, unlang_number + 1));
5213// talloc_set_destructor(unlang_thread_array, _unlang_thread_array_free);
5214
5215 /*
5216 * Instantiate each instruction with thread-specific data.
5217 */
5218 for (instruction = fr_rb_iter_init_inorder(&iter, unlang_instruction_tree);
5219 instruction;
5220 instruction = fr_rb_iter_next_inorder(&iter)) {
5221 unlang_op_t *op;
5222
5223 unlang_thread_array[instruction->number].instruction = instruction;
5224
5225 op = &unlang_ops[instruction->type];
5226
5228
5229 /*
5230 * Allocate any thread-specific instance data.
5231 */
5232 MEM(unlang_thread_array[instruction->number].thread_inst = talloc_zero_array(unlang_thread_array, uint8_t, op->thread_inst_size));
5233 talloc_set_name_const(unlang_thread_array[instruction->number].thread_inst, op->thread_inst_type);
5234
5235 if (op->thread_instantiate && (op->thread_instantiate(instruction, unlang_thread_array[instruction->number].thread_inst) < 0)) {
5236 return -1;
5237 }
5238 }
5239
5240 return 0;
5241}
5242
5243/** Get the thread-instance data for an instruction.
5244 *
5245 * @param[in] instruction the instruction to use
5246 * @return a pointer to thread-local data
5247 */
5248void *unlang_thread_instance(unlang_t const *instruction)
5249{
5250 if (!instruction->number || !unlang_thread_array) return NULL;
5251
5252 fr_assert(instruction->number <= unlang_number);
5253
5254 return unlang_thread_array[instruction->number].thread_inst;
5255}
5256
5257#ifdef WITH_PERF
5259{
5260 unlang_thread_t *t;
5261 fr_time_t now;
5262 unlang_t const *instruction = frame->instruction;
5263
5264 if (!instruction->number || !unlang_thread_array) return;
5265
5266 fr_assert(instruction->number <= unlang_number);
5267
5268 t = &unlang_thread_array[instruction->number];
5269
5270 t->use_count++;
5271 t->yielded++; // everything starts off as yielded
5272 now = fr_time();
5273
5274 fr_time_tracking_start(NULL, &frame->tracking, now);
5275 fr_time_tracking_yield(&frame->tracking, fr_time());
5276}
5277
5279{
5280 unlang_t const *instruction = frame->instruction;
5281 unlang_thread_t *t;
5282
5283 if (!instruction->number || !unlang_thread_array) return;
5284
5285 t = &unlang_thread_array[instruction->number];
5286 t->yielded++;
5287 t->running--;
5288
5289 fr_time_tracking_yield(&frame->tracking, fr_time());
5290}
5291
5293{
5294 unlang_t const *instruction = frame->instruction;
5295 unlang_thread_t *t;
5296
5297 if (!instruction->number || !unlang_thread_array) return;
5298
5299 if (frame->tracking.state != FR_TIME_TRACKING_YIELDED) return;
5300
5301 t = &unlang_thread_array[instruction->number];
5302 t->running++;
5303 t->yielded--;
5304
5305 fr_time_tracking_resume(&frame->tracking, fr_time());
5306}
5307
5309{
5310 unlang_t const *instruction = frame->instruction;
5311 unlang_thread_t *t;
5312
5313 if (!instruction || !instruction->number || !unlang_thread_array) return;
5314
5315 fr_assert(instruction->number <= unlang_number);
5316
5317 t = &unlang_thread_array[instruction->number];
5318
5319 if (frame->tracking.state == FR_TIME_TRACKING_YIELDED) {
5320 t->yielded--;
5321 fr_time_tracking_resume(&frame->tracking, fr_time());
5322 } else {
5323 t->running--;
5324 }
5325
5326 fr_time_tracking_end(NULL, &frame->tracking, fr_time());
5327 t->tracking.running_total = fr_time_delta_add(t->tracking.running_total, frame->tracking.running_total);
5328 t->tracking.waiting_total = fr_time_delta_add(t->tracking.waiting_total, frame->tracking.waiting_total);
5329}
5330
5331
5332static void unlang_perf_dump(fr_log_t *log, unlang_t const *instruction, int depth)
5333{
5334 unlang_group_t const *g;
5335 unlang_thread_t *t;
5336 char const *file;
5337 int line;
5338
5339 if (!instruction || !instruction->number) return;
5340
5341 /*
5342 * These are generally pushed onto the stack, and therefore ignored.
5343 */
5344 if (instruction->type == UNLANG_TYPE_TMPL) return;
5345
5346 /*
5347 * Everything else is an unlang_group_t;
5348 */
5349 g = unlang_generic_to_group(instruction);
5350
5351 if (!g->cs) return;
5352
5353 file = cf_filename(g->cs);
5354 line = cf_lineno(g->cs);
5355
5356 if (depth) {
5357 fr_log(log, L_DBG, file, line, "%.*s", depth, unlang_spaces);
5358 }
5359
5360 if (debug_braces(instruction->type)) {
5361 fr_log(log, L_DBG, file, line, "%s { #", instruction->debug_name);
5362 } else {
5363 fr_log(log, L_DBG, file, line, "%s #", instruction->debug_name);
5364 }
5365
5366 t = &unlang_thread_array[instruction->number];
5367
5368 fr_log(log, L_DBG, file, line, "count=%" PRIu64 " cpu_time=%" PRId64 " yielded_time=%" PRId64 ,
5369 t->use_count, fr_time_delta_unwrap(t->tracking.running_total), fr_time_delta_unwrap(t->tracking.waiting_total));
5370
5371 if (g->children) {
5372 unlang_t *child;
5373
5374 for (child = g->children; child != NULL; child = child->next) {
5375 unlang_perf_dump(log, child, depth + 1);
5376 }
5377 }
5378
5379 if (debug_braces(instruction->type)) {
5380 if (depth) {
5381 fr_log(log, L_DBG, file, line, "%.*s", depth, unlang_spaces);
5382 }
5383
5384 fr_log(log, L_DBG, file, line, "}");
5385 }
5386}
5387
5388void unlang_perf_virtual_server(fr_log_t *log, char const *name)
5389{
5390
5392 CONF_SECTION *cs;
5393 CONF_ITEM *ci;
5394 char const *file;
5395 int line;
5396
5397 if (!vs) return;
5398
5399 cs = virtual_server_cs(vs);
5400
5401 file = cf_filename(cs);
5402 line = cf_lineno(cs);
5403
5404 fr_log(log, L_DBG, file, line, " server %s {\n", name);
5405
5406 /*
5407 * Loop over the children of the virtual server, checking for unlang_t;
5408 */
5409 for (ci = cf_item_next(cs, NULL);
5410 ci != NULL;
5411 ci = cf_item_next(cs, ci)) {
5412 char const *name1, *name2;
5413 unlang_t *instruction;
5414 CONF_SECTION *subcs;
5415
5416 if (!cf_item_is_section(ci)) continue;
5417
5418 instruction = (unlang_t *)cf_data_value(cf_data_find(ci, unlang_group_t, NULL));
5419 if (!instruction) continue;
5420
5421 subcs = cf_item_to_section(ci);
5422 name1 = cf_section_name1(subcs);
5423 name2 = cf_section_name2(subcs);
5424 file = cf_filename(ci);
5425 line = cf_lineno(ci);
5426
5427 if (!name2) {
5428 fr_log(log, L_DBG, file, line, " %s {\n", name1);
5429 } else {
5430 fr_log(log, L_DBG, file, line, " %s %s {\n", name1, name2);
5431 }
5432
5433 unlang_perf_dump(log, instruction, 2);
5434
5435 fr_log(log, L_DBG, file, line, " }\n");
5436 }
5437
5438 fr_log(log, L_DBG, file, line, "}\n");
5439}
5440#endif
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
static int invalid_type(fr_type_t type)
Definition calc.c:698
call_env_t * call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method, tmpl_rules_t const *t_rules, CONF_SECTION *cs, call_env_ctx_t const *cec)
Given a call_env_method, parse all call_env_pair_t in the context of a specific call to an xlat or mo...
Definition call_env.c:751
size_t inst_size
Size of per call env.
Definition call_env.h:245
CONF_SECTION * server_cs
Config section of the virtual server being executed.
Definition call_priv.h:39
fr_dict_attr_t const * attr_packet_type
Attribute used to specify packet type and sections run in the server_cs.
Definition call_priv.h:41
static unlang_call_t * unlang_group_to_call(unlang_group_t *g)
Cast a group structure to the call keyword extension.
Definition call_priv.h:48
Entry point into a proto_ module.
Definition call_priv.h:36
static unlang_caller_t * unlang_group_to_caller(unlang_group_t *g)
Cast a group structure to the caller keyword extension.
Definition caller_priv.h:40
fr_dict_t const * dict
Definition caller_priv.h:34
Declarations for the "catch" keyword.
static unlang_catch_t * unlang_group_to_catch(unlang_group_t *g)
Cast a group structure to the transaction keyword extension.
Definition catch_priv.h:42
bool catching[RLM_MODULE_NUMCODES]
Definition catch_priv.h:34
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition cf_file.c:3430
Internal data that is associated with a configuration section.
Definition cf_priv.h:124
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:631
fr_token_t cf_pair_attr_quote(CONF_PAIR const *pair)
Return the value (lhs) quoting of a pair.
Definition cf_util.c:1622
fr_token_t cf_section_argv_quote(CONF_SECTION const *cs, int argc)
Return the quoting for one of the variadic arguments.
Definition cf_util.c:1259
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
void * cf_data_value(CONF_DATA const *cd)
Return the user assigned value of CONF_DATA.
Definition cf_util.c:1762
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
Definition cf_util.c:1278
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
void cf_section_add_name2_quote(CONF_SECTION *cs, fr_token_t token)
Set the quoting of the name2 identifier.
Definition cf_util.c:1241
bool cf_item_is_data(CONF_ITEM const *ci)
Determine if CONF_ITEM is CONF_DATA.
Definition cf_util.c:645
fr_token_t cf_pair_operator(CONF_PAIR const *pair)
Return the operator of a pair.
Definition cf_util.c:1607
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1637
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:617
char const * cf_section_argv(CONF_SECTION const *cs, int argc)
Return variadic argument at the specified index.
Definition cf_util.c:1212
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
fr_token_t cf_section_name2_quote(CONF_SECTION const *cs)
Return the quoting of the name2 identifier.
Definition cf_util.c:1229
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1593
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
Definition cf_util.c:721
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1577
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_lineno(_cf)
Definition cf_util.h:104
#define cf_item_insert_after(_parent, _prev, _child)
Definition cf_util.h:86
#define cf_data_add(_cf, _data, _name, _free)
Definition cf_util.h:255
#define cf_data_find(_cf, _type, _name)
Definition cf_util.h:244
#define cf_log_debug_prefix(_cf, _fmt,...)
Definition cf_util.h:306
#define cf_lineno_set(_ci, _lineno)
Definition cf_util.h:131
#define cf_item_prev(_parent, _curr)
Definition cf_util.h:95
#define cf_data_remove(_cf, _type, _name)
Remove an item from a parent by type and name.
Definition cf_util.h:267
#define cf_root(_cf)
Definition cf_util.h:98
#define cf_section_free_children(_x)
Definition cf_util.h:196
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_item_remove(_parent, _child)
Definition cf_util.h:89
#define cf_item_next(_parent, _curr)
Definition cf_util.h:92
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:296
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
#define CF_TO_ITEM(_cf)
Auto cast from the input type to CONF_ITEM (which is the base type)
Definition cf_util.h:65
#define cf_filename_set(_ci, _filename)
Definition cf_util.h:128
#define cf_filename(_cf)
Definition cf_util.h:107
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:292
static int compile_action_pair(unlang_mod_actions_t *actions, CONF_PAIR *cp)
Definition compile.c:1648
static unlang_t * compile_elsif(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3727
static unlang_t * compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci)
Definition compile.c:4887
bool unlang_compile_is_keyword(const char *name)
Check if name is an unlang keyword.
Definition compile.c:5172
void unlang_compile_init(TALLOC_CTX *ctx)
Definition compile.c:5193
static unlang_t * compile_function(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, CONF_SECTION *subcs, bool policy)
Definition compile.c:4494
static void unlang_dump(unlang_t *instruction, int depth)
Definition compile.c:295
static unlang_t * compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, char const *name)
Definition compile.c:4665
#define RULES_VERIFY(_rules)
Definition compile.c:148
tmpl_rules_t const * rules
Definition compile.c:100
static unlang_t * compile_empty(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext)
Definition compile.c:1903
bool unlang_compile_actions(unlang_mod_actions_t *actions, CONF_SECTION *action_cs, bool module_retry)
Definition compile.c:1790
static unlang_t * compile_redundant_load_balance(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3946
static unlang_t * compile_call(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:4319
static unlang_t * compile_section(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext)
Definition compile.c:2237
static unlang_t * compile_update_to_edit(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
Definition compile.c:870
static int compile_map_name(unlang_group_t *g)
Definition compile.c:622
static fr_table_ptr_sorted_t unlang_pair_keywords[]
Definition compile.c:4778
static unlang_t * compile_load_balance_subsection(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext)
Definition compile.c:3839
static unlang_t * compile_caller(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:4401
static unlang_t * compile_break(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3449
int unlang_thread_instantiate(TALLOC_CTX *ctx)
Create thread-specific data structures for unlang.
Definition compile.c:5202
static unlang_t * compile_redundant(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3812
static uint32_t case_hash(void const *data)
Definition compile.c:2587
static char const unlang_spaces[]
Definition compile.c:128
static bool transaction_ok(CONF_SECTION *cs)
Limit the operations which can appear in a transaction.
Definition compile.c:2312
static unlang_t * compile_else(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3740
static int edit_pair_alloc(CONF_SECTION *cs, CONF_PAIR *original, char const *attr, fr_token_t op, char const *value, fr_token_t list_op)
Definition compile.c:824
static unlang_t * compile_children(unlang_group_t *g, unlang_compile_t *unlang_ctx_in, bool set_action_defaults)
Definition compile.c:1995
static unlang_t * compile_case(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2856
#define UPDATE_CTX2
Definition compile.c:123
static int compile_variable(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_PAIR *cp, tmpl_rules_t *t_rules)
Compile a variable definition.
Definition compile.c:1560
static unlang_t * compile_subrequest(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:4018
static int8_t instruction_cmp(void const *one, void const *two)
Definition compile.c:5184
static bool validate_limited_subsection(CONF_SECTION *cs, char const *name)
Definition compile.c:3762
int unlang_fixup_update(map_t *map, void *ctx)
Validate and fixup a map that's part of an update section.
Definition compile.c:452
static bool compile_retry_section(unlang_mod_actions_t *actions, CONF_ITEM *ci)
Definition compile.c:1706
static int catch_argv(CONF_SECTION *cs, unlang_catch_t *ca, char const *name)
Definition compile.c:2477
static unlang_t * compile_catch(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2497
static unlang_t * compile_return(unlang_t *parent, unlang_compile_t *unlang_ctx, UNUSED CONF_ITEM const *ci)
Definition compile.c:3546
static unlang_t * compile_limit(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3092
char const * section_name2
Definition compile.c:98
#define UNLANG_IGNORE
Definition compile.c:58
static const bool edit_list_sub_op[T_TOKEN_LAST]
Definition compile.c:1203
size_t mod_rcode_table_len
Definition compile.c:91
static int define_local_variable(CONF_ITEM *ci, unlang_variable_t *var, tmpl_rules_t *t_rules, fr_type_t type, char const *name, fr_dict_attr_t const *ref)
Definition compile.c:4788
static unlang_t * compile_update(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:1127
static CONF_SECTION * virtual_module_find_cs(CONF_ITEM *ci, UNUSED char const *real_name, char const *virtual_name, char const *method_name, unlang_compile_t *unlang_ctx, bool *policy)
Load a named module from the virtual module list, or from the "policy" subsection.
Definition compile.c:4583
static unlang_t * compile_if(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3714
static bool pass2_fixup_map_rhs(unlang_group_t *g, tmpl_rules_t const *rules)
Definition compile.c:269
static void compile_action_defaults(unlang_t *c, unlang_compile_t *unlang_ctx)
Definition compile.c:576
static unlang_t * compile_group(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2280
static void compile_copy_context(unlang_compile_t *dst, unlang_compile_t const *src)
Definition compile.c:108
static unlang_t * compile_parallel(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3957
static const fr_sbuff_term_t if_terminals
Definition compile.c:3606
static bool pass2_fixup_update(unlang_group_t *g, tmpl_rules_t const *rules)
Definition compile.c:242
static unlang_t * compile_if_subsection(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext)
Definition compile.c:3611
static unsigned int unlang_number
Definition compile.c:60
static bool pass2_fixup_tmpl(UNUSED TALLOC_CTX *ctx, tmpl_t **vpt_p, CONF_ITEM const *ci, fr_dict_t const *dict)
Definition compile.c:150
static unlang_t * compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3201
static unlang_t * compile_continue(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3480
#define debug_braces(_type)
Definition compile.c:1553
static int unlang_fixup_map(map_t *map, UNUSED void *ctx)
Validate and fixup a map that's part of an map section.
Definition compile.c:403
static int case_to_key(uint8_t **out, size_t *outlen, void const *data)
Definition compile.c:2594
virtual_server_t const * vs
Virtual server we're compiling in the context of.
Definition compile.c:94
static unlang_t * compile_transaction(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2374
static unlang_t * compile_timeout(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2984
static unlang_t * compile_detach(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3512
static int transaction_keywords_len
Definition compile.c:2308
static fr_rb_tree_t * unlang_instruction_tree
Definition compile.c:74
static unlang_group_t * group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_ext_t const *ext)
Definition compile.c:548
static unlang_t * compile_switch(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2603
static int unlang_section_keywords_len
Definition compile.c:4776
unlang_t *(* unlang_op_compile_t)(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:4751
int dict_attr_acopy_children(fr_dict_t *dict, fr_dict_attr_t *dst, fr_dict_attr_t const *src)
Copy the children of an existing attribute.
Definition dict_util.c:1105
static unlang_t * compile_try(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:2431
static fr_table_num_sorted_t transaction_keywords[]
Definition compile.c:2293
fr_table_num_sorted_t const mod_rcode_table[]
Definition compile.c:78
static unlang_t * compile_edit_section(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
Compile one edit section.
Definition compile.c:1301
static unlang_t * compile_load_balance(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:3934
static int edit_section_alloc(CONF_SECTION *parent, CONF_SECTION **child, char const *name1, fr_token_t op)
Definition compile.c:810
static int unlang_pair_keywords_len
Definition compile.c:4784
static int unlang_attr_rules_verify(tmpl_attr_rules_t const *rules)
Definition compile.c:130
static fr_table_ptr_sorted_t unlang_section_keywords[]
Definition compile.c:4753
static int8_t case_cmp(void const *one, void const *two)
Definition compile.c:2579
void * unlang_thread_instance(unlang_t const *instruction)
Get the thread-instance data for an instruction.
Definition compile.c:5248
static int unlang_fixup_edit(map_t *map, void *ctx)
Validate and fixup a map that's part of an edit section.
Definition compile.c:1224
int unlang_compile(virtual_server_t const *vs, CONF_SECTION *cs, unlang_mod_actions_t const *actions, tmpl_rules_t const *rules, void **instruction)
Compile an unlang section for a virtual server.
Definition compile.c:5102
static _Thread_local unlang_thread_t * unlang_thread_array
Definition compile.c:67
static unlang_t * compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
Definition compile.c:669
static bool pass2_fixup_map(map_t *map, tmpl_rules_t const *rules, fr_dict_attr_t const *parent)
Fixup ONE map (recursively)
Definition compile.c:178
char const * section_name1
Definition compile.c:97
#define CLAMP(_name, _field, _limit)
#define T(_x)
Definition compile.c:1201
unlang_mod_actions_t actions
Definition compile.c:99
static unlang_t * compile_tmpl(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci)
Definition compile.c:3571
static bool compile_action_subsection(unlang_t *c, CONF_SECTION *cs, CONF_SECTION *subcs)
Definition compile.c:1944
static unlang_t * compile_edit_pair(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_PAIR *cp)
Compile one edit pair.
Definition compile.c:1466
static int unlang_rules_verify(tmpl_rules_t const *rules)
Definition compile.c:139
xlat_exp_head_t * head
static unlang_cond_t * unlang_group_to_cond(unlang_group_t *g)
Cast a group structure to the cond keyword extension.
#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 fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:156
#define fr_fatal_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:167
#define MEM(x)
Definition debug.h:36
static fr_dict_attr_t const * attr_packet_type
Definition dhcpclient.c:89
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
#define fr_dict_autoload_talloc(_ctx, _dict_out, _proto)
Definition dict.h:872
fr_dict_t const * fr_dict_proto_dict(fr_dict_t const *dict)
Definition dict_util.c:5016
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters that are allowed in dictionary attribute names.
Definition dict_util.c:47
int fr_dict_attr_acopy_local(fr_dict_attr_t const *dst, fr_dict_attr_t const *src)
Definition dict_util.c:1072
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3266
bool fr_dict_compatible(fr_dict_t const *dict1, fr_dict_t const *dict2)
See if two dictionaries have the same end parent.
Definition dict_util.c:2622
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2403
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition dict.h:88
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4654
fr_dict_t const * fr_dict_by_protocol_name(char const *name)
Lookup a protocol by its name.
Definition dict_util.c:2580
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, unsigned int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:1715
fr_dict_t * fr_dict_protocol_alloc(fr_dict_t const *parent)
Allocate a new local dictionary.
Definition dict_util.c:4005
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3439
Values of the encryption flags.
Value of an enumerated attribute.
Definition dict.h:232
Test enumeration values.
Definition dict_test.h:92
Structure used to managed the lifetime of a dictionary.
Definition dict_util.c:4240
map_list_t maps
Head of the map list.
Definition edit_priv.h:33
static unlang_t * unlang_edit_to_generic(unlang_edit_t const *p)
Definition edit_priv.h:45
static unlang_edit_t * unlang_generic_to_edit(unlang_t const *p)
Cast a generic structure to the edit extension.
Definition edit_priv.h:39
static unlang_foreach_t * unlang_group_to_foreach(unlang_group_t *g)
Cast a group structure to the foreach keyword extension.
fr_dict_attr_t const * value
value variable in the foreach loop
fr_dict_attr_t const * key
key variable for the foreach loop
uint32_t(* fr_hash_t)(void const *)
Definition hash.h:36
fr_htrie_t * fr_htrie_alloc(TALLOC_CTX *ctx, fr_htrie_type_t type, fr_hash_t hash_data, fr_cmp_t cmp_data, fr_trie_key_t get_key, fr_free_t free_data)
An abstraction over our internal hashes, rb trees, and prefix tries.
Definition htrie.c:91
fr_htrie_type_t
Definition htrie.h:49
@ FR_HTRIE_INVALID
Definition htrie.h:50
static fr_htrie_type_t fr_htrie_hint(fr_type_t type)
Definition htrie.h:149
static bool fr_htrie_insert(fr_htrie_t *ht, void const *data)
Insert data into a htrie.
Definition htrie.h:112
static void * fr_htrie_find(fr_htrie_t *ht, void const *data)
Find data in a htrie.
Definition htrie.h:104
#define DEBUG_ENABLED4
True if global debug level 1-3 messages are enabled.
Definition log.h:260
int map_afrom_cs_edit(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map for editing.
Definition map.c:1162
int map_afrom_cp(TALLOC_CTX *ctx, map_t **out, map_t *parent, CONF_PAIR *cp, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *input_rhs_rules, bool edit)
Convert CONFIG_PAIR (which may contain refs) to map_t.
Definition map.c:105
int map_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *lhs_rules, tmpl_rules_t const *rhs_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into an attribute map.
Definition map.c:1131
int map_list_afrom_cs(TALLOC_CTX *ctx, map_list_t *out, CONF_SECTION *cs, tmpl_rules_t const *t_rules, map_validate_t validate, void *uctx, unsigned int max)
Convert a config section into a list of { a, b, c, d, ... }.
Definition map.c:1347
ssize_t map_print(fr_sbuff_t *out, map_t const *map)
Print a map to a string.
Definition map.c:2386
unlang_op_t unlang_ops[UNLANG_TYPE_MAX]
Different operations the interpreter can execute.
Definition base.c:31
static TALLOC_CTX * unlang_ctx
Definition base.c:70
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
Definition log.c:581
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:59
uint32_t limit
Definition limit_priv.h:34
static unlang_limit_t * unlang_group_to_limit(unlang_group_t *g)
Cast a group structure to the limit keyword extension.
Definition limit_priv.h:40
tmpl_t * vpt
Definition limit_priv.h:33
static unlang_load_balance_t * unlang_group_to_load_balance(unlang_group_t *g)
Cast a group structure to the load_balance keyword extension.
bool main_config_migrate_option_get(char const *name)
struct map_s map_t
Definition map.h:33
map_proc_inst_t * proc_inst
Definition map_priv.h:35
static unlang_map_t * unlang_group_to_map(unlang_group_t *g)
Cast a group structure to the map keyword extension.
Definition map_priv.h:41
map_list_t map
Head of the map list.
Definition map_priv.h:34
tmpl_t * vpt
Definition map_priv.h:33
map_proc_t * map_proc_find(char const *name)
Find a map processor by name.
Definition map_proc.c:81
map_proc_inst_t * map_proc_instantiate(TALLOC_CTX *ctx, map_proc_t const *proc, CONF_SECTION *cs, tmpl_t const *src, map_list_t const *maps)
Create a new map proc instance.
Definition map_proc.c:204
fr_value_box_safe_for_t map_proc_literals_safe_for(map_proc_t const *proc)
Definition map_proc.c:69
Map processor registration.
Map processor instance.
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
ssize_t fr_slen_t
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
int strcasecmp(char *s1, char *s2)
Definition missing.c:66
Unlang module actions.
@ MOD_ACTION_RETURN
Definition mod_action.h:40
@ MOD_ACTION_REJECT
Definition mod_action.h:41
@ MOD_PRIORITY_MAX
Definition mod_action.h:58
@ MOD_ACTION_RETRY
Definition mod_action.h:42
@ MOD_PRIORITY_MIN
Definition mod_action.h:44
fr_retry_config_t retry
Definition mod_action.h:63
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:62
Declarations for the unlang module interface.
static unlang_t * unlang_module_to_generic(unlang_module_t *p)
static unlang_module_t * unlang_generic_to_module(unlang_t const *p)
Definition module_priv.h:94
module_method_call_t mmc
Everything needed to call a module method.
Definition module_priv.h:38
unlang_t self
Common fields in all unlang_t tree nodes.
Definition module_priv.h:36
call_env_t const * call_env
The per call parsed call environment.
Definition module_priv.h:37
A call to a module method.
Definition module_priv.h:35
fr_slen_t module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out, virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name, tmpl_rules_t const *t_rules)
Find an existing module instance and verify it implements the specified method.
Definition module_rlm.c:555
CONF_SECTION * module_rlm_virtual_by_name(char const *asked_name)
Definition module_rlm.c:796
module_instance_t * mi
The process modules also push module calls onto the stack for execution.
Definition module_rlm.h:63
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
module_method_binding_t mmb
Method we're calling.
Definition module_rlm.h:70
module_rlm_t const * rlm
Cached module_rlm_t.
Definition module_rlm.h:66
Declarations for the unlang "parallel" keyword.
static unlang_parallel_t * unlang_group_to_parallel(unlang_group_t *g)
Cast a group structure to the parallel keyword extension.
bool detach
are we creating the child detached
size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
Escape any non printable or non-UTF8 characters in the input string.
Definition print.c:227
size_t fr_snprint_len(char const *in, ssize_t inlen, char quote)
Find the length of the buffer required to fully escape a string with fr_prints.
Definition print.c:409
static const char * packet_name[]
#define fr_assert(_expr)
Definition rad_assert.h:38
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#define fr_rb_alloc(_ctx, _data_cmp, _data_free)
Allocs a red black tree.
Definition rb.h:223
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:321
The main red black tree structure.
Definition rb.h:73
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:41
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:50
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:51
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:44
fr_dict_attr_t const * request_attr_request
Definition request.c:43
static const call_env_method_t method_env
Definition rlm_detail.c:476
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:192
#define FR_SBUFF_OUT(_start, _len_or_end)
Set of terminal elements.
Section name identifier.
Definition section.h:44
char const * name
Instance name e.g. user_database.
Definition module.h:336
@ MODULE_TYPE_RETRY
can handle retries
Definition module.h:50
module_flags_t flags
Flags that control how a module starts up and how a module is called.
Definition module.h:228
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:330
unlang_mod_actions_t actions
default actions and retries.
Definition module.h:304
fr_dict_t const ** dict
required dictionary for this module.
Definition module.h:199
call_env_method_t const * method_env
Method specific call_env.
Definition module.h:177
module_t * exported
Public module structure.
Definition module.h:277
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:885
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:227
#define TMPL_VERIFY(_vpt)
Definition tmpl.h:961
static char const * tmpl_type_to_str(tmpl_type_t type)
Return a static string containing the type name.
Definition tmpl.h:634
#define tmpl_is_xlat(vpt)
Definition tmpl.h:210
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:219
static bool tmpl_attr_tail_is_unspecified(tmpl_t const *vpt)
Return true if the last attribute reference is "unspecified".
Definition tmpl.h:726
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
#define tmpl_value(_tmpl)
Definition tmpl.h:937
fr_table_num_sorted_t const tmpl_request_ref_table[]
Map keywords to tmpl_request_ref_t values.
#define tmpl_contains_regex(vpt)
Definition tmpl.h:226
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition tmpl.h:347
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define NUM_ALL
Definition tmpl.h:391
fr_dict_attr_t const * enumv
Enumeration attribute used to resolve enum values.
Definition tmpl.h:338
tmpl_rules_t const * parent
for parent / child relationships
Definition tmpl.h:333
#define tmpl_xlat(_tmpl)
Definition tmpl.h:930
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:904
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
@ TMPL_TYPE_ATTR_UNRESOLVED
An attribute reference that we couldn't resolve but looked valid.
Definition tmpl.h:185
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
Definition tmpl.h:142
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
Definition tmpl.h:146
@ TMPL_TYPE_EXEC
Callout to an external script or program.
Definition tmpl.h:150
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:138
@ TMPL_TYPE_DATA_UNRESOLVED
Unparsed literal string.
Definition tmpl.h:179
@ TMPL_TYPE_XLAT_UNRESOLVED
A xlat expansion with unresolved xlat functions or attribute references.
Definition tmpl.h:193
@ TMPL_TYPE_EXEC_UNRESOLVED
An exec with unresolved xlat function or attribute references.
Definition tmpl.h:189
static bool tmpl_attr_tail_da_is_leaf(tmpl_t const *vpt)
Return true if the the last attribute reference is a leaf attribute.
Definition tmpl.h:817
#define NUM_COUNT
Definition tmpl.h:392
#define tmpl_contains_attr(vpt)
Definition tmpl.h:225
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.
fr_type_t tmpl_data_type(tmpl_t const *vpt)
Definition tmpl_eval.c:1335
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:344
static bool tmpl_is_list(tmpl_t const *vpt)
Definition tmpl.h:920
ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, fr_sbuff_t *name, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv))
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
#define tmpl_is_data(vpt)
Definition tmpl.h:206
static fr_slen_t vpt
Definition tmpl.h:1269
fr_dict_t const * dict_def
Alternative default dictionary to use if vpt->rules->dict_def is NULL.
Definition tmpl.h:365
#define TMPL_POOL_DEF_HEADERS
Define manipulation functions for the attribute reference list.
Definition tmpl.h:486
#define NUM_UNSPEC
Definition tmpl.h:390
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition tmpl.h:896
#define tmpl_value_type(_tmpl)
Definition tmpl.h:939
void tmpl_attr_rewrite_leaf_num(tmpl_t *vpt, int16_t num)
Rewrite the leaf's instance number.
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:217
fr_type_t cast
Whether there was an explicit cast.
Definition tmpl.h:340
#define TMPL_POOL_DEF_LEN
How many additional bytes to allocate in a pool for a tmpl_t.
Definition tmpl.h:491
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
struct tmpl_res_rules_s tmpl_res_rules_t
Definition tmpl.h:237
static char const * tmpl_list_name(fr_dict_attr_t const *list, char const *def)
Return the name of a tmpl list or def if list not provided.
Definition tmpl.h:915
@ REQUEST_UNKNOWN
Unknown request.
Definition tmpl.h:97
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
#define tmpl_is_regex_xlat_unresolved(vpt)
Definition tmpl.h:221
void tmpl_set_dict_def(tmpl_t *vpt, fr_dict_t const *dict)
Change the default dictionary in the tmpl's resolution rules.
fr_slen_t tmpl_attr_list_from_substr(fr_dict_attr_t const **da_p, fr_sbuff_t *in)
Parse one a single list reference.
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
int tmpl_cast_set(tmpl_t *vpt, fr_type_t type)
Set a cast for a tmpl.
fr_type_t tmpl_expanded_type(tmpl_t const *vpt)
Return the native data type of the expression.
Definition tmpl_eval.c:203
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:364
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Definition log.h:96
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
map_list_t child
parent map, for nested ones
Definition map.h:89
map_t * parent
Definition map.h:88
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
CONF_ITEM * ci
Config item that the map was created from.
Definition map.h:85
Define entry and head types for tmpl request references.
Definition tmpl.h:272
uint8_t disallow_rhs_resolve
map RHS is NOT immediately resolved in the context of the LHS.
Definition tmpl.h:320
fr_dict_attr_t const * list_def
Default list to use with unqualified attribute reference.
Definition tmpl.h:295
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:312
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
uint8_t allow_wildcard
Allow the special case of .
Definition tmpl.h:309
uint8_t allow_unknown
Allow unknown attributes i.e.
Definition tmpl.h:301
static unlang_subrequest_t * unlang_group_to_subrequest(unlang_group_t *g)
Cast a group structure to the subrequest keyword extension.
tmpl_t * vpt
Value to expand to find the value to place into the packet-type attribute.
fr_dict_attr_t const * attr_packet_type
Packet-type attribute in the subrequest protocol.
tmpl_t * src
Pairs to copy into the subrequest request list.
fr_dict_t const * dict
Dictionary of the subrequest protocol.
tmpl_t * dst
Where to copy pairs from the reply list in the subrequest to.
fr_dict_enum_value_t const * type_enum
Static enumeration value for attr_packet_type.
fr_htrie_t * ht
Definition switch_priv.h:36
static unlang_switch_t * unlang_group_to_switch(unlang_group_t *g)
Cast a group structure to the switch keyword extension.
Definition switch_priv.h:42
unlang_t * default_case
Definition switch_priv.h:34
tmpl_t * vpt
Definition switch_priv.h:57
static unlang_case_t * unlang_group_to_case(unlang_group_t *g)
Cast a group structure to the case keyword extension.
Definition switch_priv.h:63
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
An element in a lexicographically sorted array of name to ptr mappings.
Definition table.h:65
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:514
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
fr_slen_t fr_time_delta_from_str(fr_time_delta_t *out, char const *in, size_t inlen, fr_time_res_t hint)
Create fr_time_delta_t from a string.
Definition time.c:412
static fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:255
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_ispos(_a)
Definition time.h:290
@ FR_TIME_RES_SEC
Definition time.h:50
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ FR_TIME_TRACKING_YIELDED
We're currently tracking time in the yielded state.
static void fr_time_tracking_yield(fr_time_tracking_t *tt, fr_time_t now)
Transition to the yielded state, recording the time we just spent running.
static void fr_time_tracking_end(fr_time_delta_t *predicted, fr_time_tracking_t *tt, fr_time_t now)
End time tracking for this entity.
static void fr_time_tracking_start(fr_time_tracking_t *parent, fr_time_tracking_t *tt, fr_time_t now)
Start time tracking for a tracked entity.
static void fr_time_tracking_resume(fr_time_tracking_t *tt, fr_time_t now)
Track that a request resumed.
fr_time_delta_t timeout
static unlang_timeout_t * unlang_group_to_timeout(unlang_group_t *g)
Cast a group structure to the timeout keyword extension.
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:169
const bool fr_list_assignment_op[T_TOKEN_LAST]
Definition token.c:186
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:34
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:79
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:199
const bool fr_binary_op[T_TOKEN_LAST]
Definition token.c:217
enum fr_token fr_token_t
@ T_OP_SUB_EQ
Definition token.h:70
@ T_INVALID
Definition token.h:39
@ T_SINGLE_QUOTED_STRING
Definition token.h:122
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_BARE_WORD
Definition token.h:120
@ T_OP_EQ
Definition token.h:83
@ T_BACK_QUOTED_STRING
Definition token.h:123
@ T_OP_SET
Definition token.h:84
@ T_OP_NE
Definition token.h:97
@ T_OP_ADD_EQ
Definition token.h:69
@ T_OP_CMP_FALSE
Definition token.h:105
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
@ T_OP_CMP_EQ
Definition token.h:106
@ T_OP_LE
Definition token.h:100
@ T_OP_GE
Definition token.h:98
@ T_OP_GT
Definition token.h:99
@ T_OP_LT
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:85
#define T_TOKEN_LAST
Definition token.h:129
Declarations for unlang transactions.
int(* fr_trie_key_t)(uint8_t **out, size_t *outlen, void const *data)
Definition trie.h:56
Declaration for unlang try.
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
tmpl_res_rules_t const * tr_rules
tmpl resolution rules.
Definition xlat.h:164
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3148
static fr_slen_t e_rules bool xlat_is_truthy(xlat_exp_head_t const *head, bool *out)
Allow callers to see if an xlat is truthy.
Definition xlat_expr.c:3183
static fr_slen_t head
Definition xlat.h:419
uint8_t pure
has no external side effects, true for BOX, LITERAL, and some functions
Definition xlat.h:109
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
uint8_t constant
xlat is just tmpl_attr_tail_data, or XLAT_BOX
Definition xlat.h:113
fr_type_t xlat_data_type(xlat_exp_head_t const *head)
#define unlang_frame_perf_resume(_x)
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
CONF_SECTION * cs
char const * debug_name
Printed in log messages when the node is executed.
char const * type_name
Talloc type name.
void * state
Stack frame specialisations.
unlang_mod_actions_t actions
Priorities, etc. for the various return codes.
unlang_t * parent
Previous node.
char const * thread_inst_type
fr_dict_attr_t const * root
the root of our dictionary
#define unlang_frame_perf_init(_x)
bool closed
whether or not this section is closed to new statements
static unlang_t * unlang_group_to_generic(unlang_group_t const *p)
unlang_t ** tail
pointer to the tail which gets updated
static unlang_t * unlang_tmpl_to_generic(unlang_tmpl_t const *p)
CONF_ITEM * ci
used to generate this item
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
unsigned int number
unique node number
char const * name
Unknown...
@ UNLANG_TYPE_SWITCH
Switch section.
Definition unlang_priv.h:58
@ UNLANG_TYPE_TRANSACTION
transactions for editing lists
Definition unlang_priv.h:74
@ UNLANG_TYPE_FINALLY
run at the end of a virtual server.
Definition unlang_priv.h:77
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition unlang_priv.h:66
@ UNLANG_TYPE_CONTINUE
Break statement (within a UNLANG_TYPE_FOREACH).
Definition unlang_priv.h:62
@ UNLANG_TYPE_UPDATE
Update block.
Definition unlang_priv.h:57
@ UNLANG_TYPE_ELSIF
!Condition && Condition.
Definition unlang_priv.h:56
@ UNLANG_TYPE_ELSE
!Condition.
Definition unlang_priv.h:55
@ UNLANG_TYPE_LOAD_BALANCE
Load balance section.
Definition unlang_priv.h:51
@ UNLANG_TYPE_DETACH
detach a child
Definition unlang_priv.h:69
@ UNLANG_TYPE_GROUP
Grouping section.
Definition unlang_priv.h:49
@ UNLANG_TYPE_POLICY
Policy section.
Definition unlang_priv.h:78
@ UNLANG_TYPE_TMPL
asynchronously expand a tmpl_t
Definition unlang_priv.h:80
@ UNLANG_TYPE_CASE
Case section (within a UNLANG_TYPE_SWITCH).
Definition unlang_priv.h:59
@ UNLANG_TYPE_LIMIT
limit number of requests in a section
Definition unlang_priv.h:73
@ UNLANG_TYPE_BREAK
Break statement (within a UNLANG_TYPE_FOREACH or UNLANG_TYPE_CASE).
Definition unlang_priv.h:61
@ UNLANG_TYPE_TRY
try / catch blocks
Definition unlang_priv.h:75
@ UNLANG_TYPE_CALL
call another virtual server
Definition unlang_priv.h:70
@ UNLANG_TYPE_RETURN
Return statement.
Definition unlang_priv.h:63
@ UNLANG_TYPE_REDUNDANT
exactly like group, but with different default return codes
Definition unlang_priv.h:50
@ UNLANG_TYPE_MAX
Definition unlang_priv.h:82
@ UNLANG_TYPE_IF
Condition.
Definition unlang_priv.h:54
@ UNLANG_TYPE_XLAT
Represents one level of an xlat expansion.
Definition unlang_priv.h:79
@ UNLANG_TYPE_NULL
unlang type not set.
Definition unlang_priv.h:46
@ UNLANG_TYPE_MAP
Mapping section (like UNLANG_TYPE_UPDATE, but uses values from a map_proc_t call).
Definition unlang_priv.h:64
@ UNLANG_TYPE_CALLER
conditionally check parent dictionary type
Definition unlang_priv.h:71
@ UNLANG_TYPE_TIMEOUT
time-based timeouts.
Definition unlang_priv.h:72
@ UNLANG_TYPE_MODULE
Module method.
Definition unlang_priv.h:47
@ UNLANG_TYPE_REDUNDANT_LOAD_BALANCE
Redundant load balance section.
Definition unlang_priv.h:52
@ UNLANG_TYPE_CHILD_REQUEST
a frame at the top of a child's request stack used to signal the parent when the child is complete.
Definition unlang_priv.h:67
@ UNLANG_TYPE_CATCH
catch a previous try
Definition unlang_priv.h:76
@ UNLANG_TYPE_FUNCTION
Internal call to a function or submodule.
Definition unlang_priv.h:48
@ UNLANG_TYPE_EDIT
edit VPs in place. After 20 years!
Definition unlang_priv.h:81
@ UNLANG_TYPE_FOREACH
Foreach section.
Definition unlang_priv.h:60
@ UNLANG_TYPE_PARALLEL
execute statements in parallel
Definition unlang_priv.h:53
unlang_t const * instruction
The unlang node we're evaluating.
size_t len
Total length of the unlang_group_t + specialisation struct.
unlang_type_t type
Keyword.
unlang_variable_t * variables
rarely used, so we don't usually need it
unlang_t const * instruction
instruction which we're executing
char const * name
Name of the operation.
#define unlang_frame_perf_yield(_x)
size_t pool_len
How much additional space to allocate for extensions.
unsigned pool_headers
How much additional space to allocate for chunk headers.
#define unlang_frame_perf_cleanup(_x)
int max_attr
1..N local attributes have been defined
unlang_thread_instantiate_t thread_instantiate
per-thread instantiation function
fr_dict_t * dict
our dictionary
tmpl_t const * tmpl
@ UNLANG_OP_FLAG_RETURN_POINT
Return point.
@ UNLANG_OP_FLAG_BREAK_POINT
Break point.
unlang_type_t type
The specialisation of this node.
unlang_t * children
Children beneath this group.
size_t thread_inst_size
unlang_op_flag_t flag
Flags for this operation.
void * thread_inst
thread-specific instance data
Describes how to allocate an unlang_group_t with additional memory keyword specific data.
Generic representation of a grouping.
An unlang operation.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
A naked xlat.
static fr_slen_t parent
Definition pair.h:845
fr_time_delta_t irt
Initial transmission time.
Definition retry.h:33
fr_time_delta_t mrt
Maximum retransmission time.
Definition retry.h:34
uint32_t mrc
Maximum retransmission count.
Definition retry.h:36
fr_time_delta_t mrd
Maximum retransmission duration.
Definition retry.h:35
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define fr_type_is_structural(_x)
Definition types.h:388
#define fr_type_is_numeric(_x)
Definition types.h:378
#define FR_TYPE_INTEGER_EXCEPT_BOOL
Definition types.h:299
#define fr_type_is_leaf(_x)
Definition types.h:389
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:450
#define DOC_KEYWORD_REF(_x)
Definition version.h:89
uint32_t fr_value_box_hash(fr_value_box_t const *vb)
Hash the contents of a value box.
Definition value.c:6314
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3574
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:722
int fr_value_box_to_key(uint8_t **out, size_t *outlen, fr_value_box_t const *value)
Get a key from a value box.
Definition value.c:2130
static fr_slen_t data
Definition value.h:1288
static size_t char ** out
Definition value.h:1020
fr_dict_t const * virtual_server_dict_by_name(char const *virtual_server)
Return the namespace for the named virtual server.
virtual_server_t const * virtual_server_find(char const *name)
Return virtual server matching the specified name.
CONF_SECTION * virtual_server_cs(virtual_server_t const *vs)
Return the configuration section for a virtual server.
xlat_flags_t flags
Flags that control resolution and evaluation.
Definition xlat_priv.h:190
static bool is_truthy(xlat_exp_t *node, bool *out)