2 * parser.c Parse various things
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2013 Alan DeKok <aland@freeradius.org>
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/parser.h>
27 #include <freeradius-devel/rad_assert.h>
31 #define PW_CAST_BASE (1850)
33 static const FR_NAME_NUMBER allowed_return_codes[] = {
47 * This file shouldn't use any functions from the server core.
50 size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *c)
54 char *end = buffer + bufsize - 1;
58 *(p++) = '!'; /* FIXME: only allow for child? */
62 case COND_TYPE_EXISTS:
63 rad_assert(c->data.vpt != NULL);
65 len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
66 c->cast->type, "??"));
70 len = radius_tmpl2str(p, end - p, c->data.vpt);
75 rad_assert(c->data.map != NULL);
77 *(p++) = '['; /* for extra-clear debugging */
80 len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
81 c->cast->type, "??"));
85 len = radius_map2str(p, end - p, c->data.map);
93 rad_assert(c->data.child != NULL);
95 len = fr_cond_sprint(p, end - p, c->data.child);
101 strlcpy(buffer, "true", bufsize);
102 return strlen(buffer);
104 case COND_TYPE_FALSE:
105 strlcpy(buffer, "false", bufsize);
106 return strlen(buffer);
113 if (c->next_op == COND_NONE) {
114 rad_assert(c->next == NULL);
119 if (c->next_op == COND_AND) {
120 strlcpy(p, " && ", end - p);
123 } else if (c->next_op == COND_OR) {
124 strlcpy(p, " || ", end - p);
137 * Cast a literal vpt to a value_data_t
139 static int cast_vpt(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
144 rad_assert(vpt->type == VPT_TYPE_LITERAL);
146 vp = pairalloc(vpt, da);
147 if (!vp) return false;
149 if (!pairparsevalue(vp, vpt->name)) {
154 vpt->length = vp->length;
155 vpt->vpd = data = talloc(vpt, value_data_t);
156 if (!vpt->vpd) return false;
158 vpt->type = VPT_TYPE_DATA;
161 if (vp->da->flags.is_pointer) {
162 data->ptr = talloc_steal(vpt, vp->data.ptr);
165 memcpy(data, &vp->data, sizeof(*data));
173 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
174 FR_TOKEN *op, char const **error)
176 char const *p = start;
184 *op = T_DOUBLE_QUOTED_STRING;
188 *op = T_SINGLE_QUOTED_STRING;
192 *op = T_BACK_QUOTED_STRING;
196 *op = T_OP_REG_EQ; /* a bit of a hack. */
201 *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
202 if (!*out) return -1;
216 *error = "End of string after escape";
241 *error = "Unterminated string";
245 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
246 FR_TOKEN *op, char const **error)
249 char const *p = start;
251 if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
252 return condition_tokenize_string(ctx, start, out, op, error);
256 if (*p == '&') p++; /* special-case &User-Name */
260 * The LHS should really be limited to only a few
261 * things. For now, we allow pretty much anything.
264 *error = "Unexpected escape";
276 * Spaces or special characters delineate the word
278 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
279 (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
283 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
284 *error = "Unexpected start of string";
293 *error = "Empty string is invalid";
297 *out = talloc_array(ctx, char, len + 1);
298 memcpy(*out, start, len);
304 static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda, char const **error)
306 char const *p = start;
310 while (isspace((int) *p)) p++; /* skip spaces before condition */
312 if (*p != '<') return 0;
316 while (*q && *q != '>') q++;
318 cast = fr_substr2int(dict_attr_types, p, PW_TYPE_INVALID, q - p);
319 if (cast == PW_TYPE_INVALID) {
320 *error = "Invalid data type in cast";
324 *pda = dict_attrbyvalue(PW_CAST_BASE + cast, 0);
326 *error = "Cannot cast to this data type";
332 while (isspace((int) *q)) q++; /* skip spaces after cast */
338 * Less code means less bugs
340 #define return_P(_x) *error = _x;goto return_p
341 #define return_0(_x) *error = _x;goto return_0
342 #define return_lhs(_x) *error = _x;goto return_lhs
343 #define return_rhs(_x) *error = _x;goto return_rhs
344 #define return_SLEN goto return_slen
347 /** Tokenize a conditional check
349 * @param[in] ctx for talloc
350 * @param[in] ci for CONF_ITEM
351 * @param[in] start the start of the string to process. Should be "(..."
352 * @param[in] brace look for a closing brace
353 * @param[in] flags do one/two pass
354 * @param[out] pcond pointer to the returned condition structure
355 * @param[out] error the parse error (if any)
356 * @return length of the string skipped, or when negative, the offset to the offending error
358 static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *start, int brace, fr_cond_t **pcond, char const **error, int flags)
361 char const *p = start;
362 char const *lhs_p, *rhs_p;
365 FR_TOKEN op, lhs_type, rhs_type;
367 c = talloc_zero(ctx, fr_cond_t);
369 rad_assert(c != NULL);
371 lhs_type = rhs_type = T_OP_INVALID;
373 while (isspace((int) *p)) p++; /* skip spaces before condition */
376 return_P("Empty condition is invalid");
385 while (isspace((int) *p)) p++; /* skip spaces after negation */
391 return_P("Double negation is invalid");
402 * We've already eaten one layer of
403 * brackets. Go recurse to get more.
405 c->type = COND_TYPE_CHILD;
406 slen = condition_tokenize(c, ci, p, true, &c->data.child, error, flags);
411 if (!c->data.child) {
412 return_P("Empty condition is invalid");
416 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
418 } else { /* it's a bare FOO==BAR */
420 * We didn't see anything special. The condition must be one of
430 return_P("Conditional check cannot begin with a regular expression");
433 slen = condition_tokenize_cast(p, &c->cast, error);
440 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
446 while (isspace((int)*p)) p++; /* skip spaces after LHS */
449 * We may (or not) have an operator
458 * don't skip the brace. We'll look for it later.
467 return_P("No closing brace at end of string");
475 } else if (((p[0] == '&') && (p[1] == '&')) ||
476 ((p[0] == '|') && (p[1] == '|'))) {
480 return_0("Cannot do cast for existence check");
483 c->type = COND_TYPE_EXISTS;
485 c->data.vpt = radius_str2tmpl(c, lhs, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
487 return_P("Failed creating exists");
490 rad_assert(c->data.vpt->type != VPT_TYPE_REGEX);
492 } else { /* it's an operator */
496 * The next thing should now be a comparison operator.
499 c->type = COND_TYPE_MAP;
502 return_P("Invalid text. Expected comparison operator");
509 } else if (p[1] == '~') {
515 } else if (p[1] == '*') {
516 if (lhs_type != T_BARE_WORD) {
517 return_P("Cannot use !* on a string");
524 goto invalid_operator;
533 } else if (p[1] == '~') {
539 } else if (p[1] == '*') {
540 if (lhs_type != T_BARE_WORD) {
541 return_P("Cannot use =* on a string");
549 return_P("Invalid operator");
577 while (isspace((int) *p)) p++; /* skip spaces after operator */
580 return_P("Expected text after operator");
584 * Cannot have a cast on the RHS.
585 * But produce good errors, too.
588 DICT_ATTR const *cast_da;
590 slen = condition_tokenize_cast(p, &cast_da, error);
596 return_P("Unexpected cast");
599 if (c->cast != cast_da) {
600 return_P("Cannot cast to a different data type");
603 return_P("Unnecessary cast");
610 slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
616 * Sanity checks for regexes.
620 return_P("Expected regular expression");
626 if (p[slen] == 'i') {
631 } else if (!regex && (*p == '/')) {
632 return_P("Unexpected regular expression");
635 c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
636 REQUEST_CURRENT, PAIR_LIST_REQUEST,
637 REQUEST_CURRENT, PAIR_LIST_REQUEST);
641 * FIXME: In the future,
643 * know whether this is
645 * it's pass2, then an
646 * unknown attribute is a
649 return_0("Unknown attribute");
651 return_0("Syntax error");
655 * Could have been a reference to an attribute which is registered later.
656 * Mark it as being checked in pass2.
658 if ((lhs_type == T_BARE_WORD) &&
659 (c->data.map->dst->type == VPT_TYPE_LITERAL)) {
660 c->pass2_fixup = PASS2_FIXUP_ATTR;
664 * Save the CONF_ITEM for later.
666 c->data.map->ci = ci;
669 * @todo: check LHS and RHS separately, to
672 if ((c->data.map->src->type == VPT_TYPE_LIST) ||
673 (c->data.map->dst->type == VPT_TYPE_LIST)) {
674 return_0("Cannot use list references in condition");
678 * Check cast type. We can have the RHS
679 * a string if the LHS has a cast. But
680 * if the RHS is an attr, it MUST be the
681 * same type as the LHS.
684 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
685 (c->cast->type != c->data.map->src->da->type)) {
689 if (c->data.map->src->type == VPT_TYPE_REGEX) {
690 return_0("Cannot use cast with regex comparison");
694 * The LHS is a literal which has been cast to a data type.
695 * Cast it to the appropriate data type.
697 if ((c->data.map->dst->type == VPT_TYPE_LITERAL) &&
698 !cast_vpt(c->data.map->dst, c->cast)) {
699 *error = "Failed to parse field";
700 if (lhs) talloc_free(lhs);
701 if (rhs) talloc_free(rhs);
703 return -(lhs_p - start);
707 * The RHS is a literal, and the LHS has been cast to a data
710 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
711 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
712 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
713 return_rhs("Failed to parse field");
717 * Casting to a redundant type means we don't need the cast.
719 * Do this LAST, as the rest of the code above assumes c->cast
722 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
723 (c->cast->type == c->data.map->dst->da->type)) {
729 * Two attributes? They must be of the same type
731 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
732 (c->data.map->dst->type == VPT_TYPE_ATTR) &&
733 (c->data.map->dst->da->type != c->data.map->src->da->type)) {
735 return_0("Attribute comparisons must be of the same attribute type");
739 * Without a cast, we can't compare "foo" to User-Name,
740 * it has to be done the other way around.
742 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
743 (c->data.map->dst->type != VPT_TYPE_ATTR)) {
744 *error = "Cannot use attribute reference on right side of condition";
746 if (lhs) talloc_free(lhs);
747 if (rhs) talloc_free(rhs);
753 * Invalid: User-Name == bob
754 * Valid: User-Name == "bob"
756 * There's no real reason for
757 * this, other than consistency.
759 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
760 (c->data.map->src->type != VPT_TYPE_ATTR) &&
761 (c->data.map->dst->da->type == PW_TYPE_STRING) &&
762 (c->data.map->op != T_OP_CMP_TRUE) &&
763 (c->data.map->op != T_OP_CMP_FALSE) &&
764 (rhs_type == T_BARE_WORD)) {
765 return_rhs("Must have string as value for attribute");
769 * Quotes around non-string
770 * attributes mean that it's
771 * either xlat, or an exec.
773 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
774 (c->data.map->src->type != VPT_TYPE_ATTR) &&
775 (c->data.map->dst->da->type != PW_TYPE_STRING) &&
776 (c->data.map->dst->da->type != PW_TYPE_OCTETS) &&
777 (c->data.map->dst->da->type != PW_TYPE_DATE) &&
778 (rhs_type == T_SINGLE_QUOTED_STRING)) {
779 *error = "Value must be an unquoted string";
781 if (lhs) talloc_free(lhs);
782 if (rhs) talloc_free(rhs);
784 return -(rhs_p - start);
788 * The LHS has been cast to a data type, and the RHS is a
789 * literal. Cast the RHS to the type of the cast.
791 if (c->cast && (c->data.map->src->type == VPT_TYPE_LITERAL) &&
792 !cast_vpt(c->data.map->src, c->cast)) {
793 return_rhs("Failed to parse field");
797 * The LHS is an attribute, and the RHS is a literal. Cast the
798 * RHS to the data type of the LHS.
800 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
801 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
802 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
803 DICT_ATTR const *da = c->data.map->dst->da;
805 if ((da->vendor == 0) &&
806 ((da->attr == PW_AUTH_TYPE) ||
807 (da->attr == PW_AUTZ_TYPE) ||
808 (da->attr == PW_ACCT_TYPE) ||
809 (da->attr == PW_SESSION_TYPE) ||
810 (da->attr == PW_POST_AUTH_TYPE) ||
811 (da->attr == PW_PRE_PROXY_TYPE) ||
812 (da->attr == PW_POST_PROXY_TYPE) ||
813 (da->attr == PW_PRE_ACCT_TYPE) ||
814 (da->attr == PW_RECV_COA_TYPE) ||
815 (da->attr == PW_SEND_COA_TYPE))) {
817 * The types for these attributes are dynamically allocated
818 * by modules.c, so we can't enforce strictness here.
820 c->pass2_fixup = PASS2_FIXUP_TYPE;
823 return_rhs("Failed to parse value for attribute");
830 while (isspace((int) *p)) p++; /* skip spaces after RHS */
832 } /* parse a condition (COND) or FOO OP BAR*/
839 return_P("Unexpected closing brace");
843 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
848 * End of string is now allowed.
852 return_P("No closing brace at end of string");
858 if (!(((p[0] == '&') && (p[1] == '&')) ||
859 ((p[0] == '|') && (p[1] == '|')))) {
860 *error = "Unexpected text after condition";
862 if (lhs) talloc_free(lhs);
863 if (rhs) talloc_free(rhs);
869 * Recurse to parse the next condition.
875 * May still be looking for a closing brace.
877 slen = condition_tokenize(c, ci, p, brace, &c->next, error, flags);
880 if (lhs) talloc_free(lhs);
881 if (rhs) talloc_free(rhs);
883 return slen - (p - start);
889 * Normalize the condition before returning.
891 * We collapse multiple levels of braces to one. Then
892 * convert maps to literals. Then literals to true/false
893 * statements. Then true/false ||/&& followed by other
894 * conditions to just conditions.
896 * Order is important. The more complex cases are
897 * converted to simpler ones, from the most complex cases
898 * to the simplest ones.
903 * (FOO) ... --> FOO ...
905 if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
908 child = talloc_steal(ctx, c->data.child);
909 c->data.child = NULL;
911 child->next = talloc_steal(child, c->next);
914 child->next_op = c->next_op;
917 * Set the negation properly
919 if ((c->negate && !child->negate) ||
920 (!c->negate && child->negate)) {
921 child->negate = true;
923 child->negate = false;
932 * (FOO ...) --> FOO ...
934 * But don't do !(FOO || BAR) --> !FOO || BAR
935 * Because that's different.
937 if ((c->type == COND_TYPE_CHILD) &&
938 !c->next && !c->negate) {
941 child = talloc_steal(ctx, c->data.child);
942 c->data.child = NULL;
950 * Convert maps to literals. Convert one form of map to
951 * a standardized form. This doesn't make any
952 * theoretical difference, but it does mean that the
953 * run-time evaluation has fewer cases to check.
955 if (c->type == COND_TYPE_MAP) do {
957 * !FOO !~ BAR --> FOO =~ BAR
959 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
961 c->data.map->op = T_OP_REG_EQ;
965 * FOO !~ BAR --> !FOO =~ BAR
967 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
969 c->data.map->op = T_OP_REG_EQ;
973 * !FOO != BAR --> FOO == BAR
975 if (c->negate && (c->data.map->op == T_OP_NE)) {
977 c->data.map->op = T_OP_CMP_EQ;
981 * This next one catches "LDAP-Group != foo",
982 * which doesn't work as-is, but this hack fixes
985 * FOO != BAR --> !FOO == BAR
987 if (!c->negate && (c->data.map->op == T_OP_NE)) {
989 c->data.map->op = T_OP_CMP_EQ;
994 * FOO !* BAR --> !FOO
996 if ((c->data.map->op == T_OP_CMP_TRUE) ||
997 (c->data.map->op == T_OP_CMP_FALSE)) {
998 value_pair_tmpl_t *vpt;
1000 vpt = talloc_steal(c, c->data.map->dst);
1001 c->data.map->dst = NULL;
1004 * Invert the negation bit.
1006 if (c->data.map->op == T_OP_CMP_FALSE) {
1007 c->negate = !c->negate;
1010 TALLOC_FREE(c->data.map);
1012 c->type = COND_TYPE_EXISTS;
1014 break; /* it's no longer a map */
1018 * Both are data (IP address, integer, etc.)
1020 * We can do the evaluation here, so that it
1021 * doesn't need to be done at run time
1023 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
1024 (c->data.map->src->type == VPT_TYPE_DATA)) {
1027 rad_assert(c->cast != NULL);
1029 rcode = radius_evaluate_map(NULL, 0, 0, c);
1030 TALLOC_FREE(c->data.map);
1034 c->type = COND_TYPE_TRUE;
1036 c->type = COND_TYPE_FALSE;
1039 break; /* it's no longer a map */
1043 * Both are literal strings. They're not parsed
1044 * as VPT_TYPE_DATA because there's no cast to an
1047 * We can do the evaluation here, so that it
1048 * doesn't need to be done at run time
1050 if ((c->data.map->src->type == VPT_TYPE_LITERAL) &&
1051 (c->data.map->dst->type == VPT_TYPE_LITERAL)) {
1054 rad_assert(c->cast == NULL);
1055 rad_assert(c->regex_i == false);
1057 rcode = radius_evaluate_map(NULL, 0, 0, c);
1059 c->type = COND_TYPE_TRUE;
1061 c->type = COND_TYPE_FALSE;
1065 * Free map after using it above.
1067 TALLOC_FREE(c->data.map);
1073 * Existence checks. We short-circuit static strings,
1076 if (c->type == COND_TYPE_EXISTS) {
1077 switch (c->data.vpt->type) {
1085 * 'true' and 'false' are special strings
1086 * which mean themselves.
1088 * For integers, 0 is false, all other
1089 * integers are true.
1091 * For strings, '' and "" are false.
1092 * 'foo' and "foo" are true.
1094 * The str2tmpl function takes care of
1095 * marking "%{foo}" as VPT_TYPE_XLAT, so
1096 * the strings here are fixed at compile
1099 * `exec` and "%{...}" are left alone.
1101 * Bare words must be module return
1104 case VPT_TYPE_LITERAL:
1105 if ((strcmp(c->data.vpt->name, "true") == 0) ||
1106 (strcmp(c->data.vpt->name, "1") == 0)) {
1107 c->type = COND_TYPE_TRUE;
1108 TALLOC_FREE(c->data.vpt);
1110 } else if ((strcmp(c->data.vpt->name, "false") == 0) ||
1111 (strcmp(c->data.vpt->name, "0") == 0)) {
1112 c->type = COND_TYPE_FALSE;
1113 TALLOC_FREE(c->data.vpt);
1115 } else if (!*c->data.vpt->name) {
1116 c->type = COND_TYPE_FALSE;
1117 TALLOC_FREE(c->data.vpt);
1119 } else if ((lhs_type == T_SINGLE_QUOTED_STRING) ||
1120 (lhs_type == T_DOUBLE_QUOTED_STRING)) {
1121 c->type = COND_TYPE_TRUE;
1122 TALLOC_FREE(c->data.vpt);
1124 } else if (lhs_type == T_BARE_WORD) {
1128 for (q = c->data.vpt->name;
1131 if (!isdigit((int) *q)) {
1137 * It's all digits, and therefore
1141 c->type = COND_TYPE_TRUE;
1142 TALLOC_FREE(c->data.vpt);
1146 rcode = fr_str2int(allowed_return_codes,
1147 c->data.vpt->name, 0);
1149 return_0("Expected a module return code");
1154 * Else lhs_type==T_OP_INVALID, and this
1155 * node was made by promoting a child
1156 * which had already been normalized.
1161 return_0("Cannot use data here");
1164 return_0("Internal sanity check failed");
1171 if (c->type == COND_TYPE_TRUE) {
1174 c->type = COND_TYPE_FALSE;
1181 if (c->type == COND_TYPE_FALSE) {
1184 c->type = COND_TYPE_TRUE;
1189 * true && FOO --> FOO
1191 if ((c->type == COND_TYPE_TRUE) &&
1192 (c->next_op == COND_AND)) {
1195 next = talloc_steal(ctx, c->next);
1204 * false && FOO --> false
1206 if ((c->type == COND_TYPE_FALSE) &&
1207 (c->next_op == COND_AND)) {
1208 talloc_free(c->next);
1210 c->next_op = COND_NONE;
1214 * false || FOO --> FOO
1216 if ((c->type == COND_TYPE_FALSE) &&
1217 (c->next_op == COND_OR)) {
1220 next = talloc_steal(ctx, c->next);
1229 * true || FOO --> true
1231 if ((c->type == COND_TYPE_TRUE) &&
1232 (c->next_op == COND_OR)) {
1233 talloc_free(c->next);
1235 c->next_op = COND_NONE;
1238 if (lhs) talloc_free(lhs);
1239 if (rhs) talloc_free(rhs);
1245 /** Tokenize a conditional check
1247 * @param[in] ctx for talloc
1248 * @param[in] ci for CONF_ITEM
1249 * @param[in] start the start of the string to process. Should be "(..."
1250 * @param[out] head the parsed condition structure
1251 * @param[out] error the parse error (if any)
1252 * @param[in] flags do one/two pass
1253 * @return length of the string skipped, or when negative, the offset to the offending error
1255 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *start, fr_cond_t **head, char const **error, int flags)
1257 return condition_tokenize(ctx, ci, start, false, head, error, flags);
1263 bool fr_condition_walk(fr_cond_t *c, bool (*callback)(void *, fr_cond_t *), void *ctx)
1267 * Process this one, exit on error.
1269 if (!callback(ctx, c)) return false;
1272 case COND_TYPE_INVALID:
1275 case COND_TYPE_EXISTS:
1277 case COND_TYPE_TRUE:
1278 case COND_TYPE_FALSE:
1281 case COND_TYPE_CHILD:
1283 * Walk over the child.
1285 if (!fr_condition_walk(c->data.child, callback, ctx)) {
1293 if (c->next_op == COND_NONE) break;
1296 * process the next sibling