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