Pass2 for attributes in existence checks
[freeradius.git] / src / main / parser.c
index fcfd16e..e1e5581 100644 (file)
@@ -293,7 +293,7 @@ static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda,
 #ifdef WITH_ASCEND_BINARY
        case PW_TYPE_ABINARY:
 #endif
-       case PW_TYPE_COMBO_IP:
+       case PW_TYPE_IP_ADDR:
        case PW_TYPE_TLV:
        case PW_TYPE_EXTENDED:
        case PW_TYPE_LONG_EXTENDED:
@@ -340,7 +340,8 @@ static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda,
  *  @param[out] error the parse error (if any)
  *  @return length of the string skipped, or when negative, the offset to the offending error
  */
-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)
+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)
 {
        ssize_t slen;
        char const *p = start;
@@ -471,13 +472,30 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
 
                        c->data.vpt = radius_str2tmpl(c, lhs, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
                        if (!c->data.vpt) {
-                               return_P("Failed creating exists");
+                               /*
+                                *      If strings are T_BARE_WORD and they start with '&',
+                                *      then they refer to attributes which have not yet been
+                                *      defined.  Create the template(s) as literals, and
+                                *      fix them up in pass2.
+                                */
+                               if ((*lhs != '&') ||
+                                   (lhs_type != T_BARE_WORD)) {
+                                       return_P("Failed creating exists");
+                               }
+                               c->data.vpt = radius_str2tmpl(c, lhs + 1, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                               if (!c->data.vpt) {
+                                       return_P("Failed creating exists");
+                               }
+                               rad_const_free(c->data.vpt->name);
+                               c->data.vpt->name = talloc_strdup(c->data.vpt, lhs);
+                               c->pass2_fixup = PASS2_FIXUP_ATTR;
                        }
 
                        rad_assert(c->data.vpt->type != VPT_TYPE_REGEX);
 
                } else { /* it's an operator */
-                       int regex;
+                       bool regex;
+                       bool i_flag = false;
 
                        /*
                         *      The next thing should now be a comparison operator.
@@ -613,7 +631,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 *      Allow /foo/i
                                 */
                                if (p[slen] == 'i') {
-                                       c->regex_i = true;
+                                       i_flag = true;
                                        slen++;
                                }
 
@@ -626,15 +644,28 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
                        if (!c->data.map) {
                                /*
-                                *      FIXME: if strings are T_BARE_WORD and they start with '&',
+                                *      If strings are T_BARE_WORD and they start with '&',
                                 *      then they refer to attributes which have not yet been
                                 *      defined.  Create the template(s) as literals, and
                                 *      fix them up in pass2.
                                 */
-                               if (*lhs == '&') {
+                               if ((*lhs != '&') ||
+                                   (lhs_type != T_BARE_WORD)) {
+                                       return_0("Syntax error");
+                               }
+                               c->data.map = radius_str2map(c, lhs, lhs_type + 1, op, rhs, rhs_type,
+                                                            REQUEST_CURRENT, PAIR_LIST_REQUEST,
+                                                            REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                               if (!c->data.map) {
                                        return_0("Unknown attribute");
                                }
-                               return_0("Syntax error");
+                               rad_const_free(c->data.map->dst->name);
+                               c->data.map->dst->name = talloc_strdup(c->data.map->dst, lhs);
+                               c->pass2_fixup = PASS2_FIXUP_ATTR;
+                       }
+
+                       if (c->data.map->src->type == VPT_TYPE_REGEX) {
+                               c->data.map->src->vpt_iflag = i_flag;
                        }
 
                        /*
@@ -726,16 +757,16 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                        /*
                                         *      ipaddr to ipv4prefix is OK
                                         */
-                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPADDR) &&
-                                           (c->cast->type == PW_TYPE_IPV4PREFIX)) {
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPV4_ADDR) &&
+                                           (c->cast->type == PW_TYPE_IPV4_PREFIX)) {
                                                goto cast_ok;
                                        }
 
                                        /*
                                         *      ipv6addr to ipv6prefix is OK
                                         */
-                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPV6ADDR) &&
-                                           (c->cast->type == PW_TYPE_IPV6PREFIX)) {
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPV6_ADDR) &&
+                                           (c->cast->type == PW_TYPE_IPV6_PREFIX)) {
                                                goto cast_ok;
                                        }
 
@@ -1037,6 +1068,9 @@ done:
                /*
                 *      FOO =* BAR --> FOO
                 *      FOO !* BAR --> !FOO
+                *
+                *      FOO may be a string, or a delayed attribute
+                *      reference.
                 */
                if ((c->data.map->op == T_OP_CMP_TRUE) ||
                    (c->data.map->op == T_OP_CMP_FALSE)) {
@@ -1074,7 +1108,6 @@ done:
                        rcode = radius_evaluate_map(NULL, 0, 0, c);
                        TALLOC_FREE(c->data.map);
                        c->cast = NULL;
-                       c->regex_i = false;
                        if (rcode) {
                                c->type = COND_TYPE_TRUE;
                        } else {
@@ -1093,16 +1126,19 @@ done:
                 *      doesn't need to be done at run time
                 */
                if ((c->data.map->src->type == VPT_TYPE_LITERAL) &&
-                   (c->data.map->dst->type == VPT_TYPE_LITERAL)) {
+                   (c->data.map->dst->type == VPT_TYPE_LITERAL) &&
+                   !c->pass2_fixup) {
                        int rcode;
 
                        rad_assert(c->cast == NULL);
-                       rad_assert(c->regex_i == false);
 
                        rcode = radius_evaluate_map(NULL, 0, 0, c);
                        if (rcode) {
                                c->type = COND_TYPE_TRUE;
                        } else {
+                               DEBUG("OPTIMIZING %s %s --> FALSE",
+                                     c->data.map->dst->name,
+                                     c->data.map->src->name);
                                c->type = COND_TYPE_FALSE;
                        }
 
@@ -1165,6 +1201,11 @@ done:
        /*
         *      Existence checks.  We short-circuit static strings,
         *      too.
+        *
+        *      FIXME: the data types should be in the template, too.
+        *      So that we know where a literal came from.
+        *
+        *      "foo" is NOT the same as 'foo' or a bare foo.
         */
        if (c->type == COND_TYPE_EXISTS) {
                switch (c->data.vpt->type) {
@@ -1216,6 +1257,7 @@ done:
 
                        } else if (lhs_type == T_BARE_WORD) {
                                int rcode;
+                               bool zeros = true;
                                char const *q;
 
                                for (q = c->data.vpt->name;
@@ -1224,6 +1266,7 @@ done:
                                        if (!isdigit((int) *q)) {
                                                break;
                                        }
+                                       if (*q != '0') zeros = false;
                                }
 
                                /*
@@ -1231,11 +1274,23 @@ done:
                                 *      'true'.
                                 */
                                if (!*q) {
-                                       c->type = COND_TYPE_TRUE;
+                                       if (zeros) {
+                                               c->type = COND_TYPE_TRUE;
+                                       } else {
+                                               c->type = COND_TYPE_TRUE;
+                                       }
                                        TALLOC_FREE(c->data.vpt);
                                        break;
                                }
 
+                               /*
+                                *      Allow &Foo-Bar where Foo-Bar is an attribute
+                                *      defined by a module.
+                                */
+                               if (c->pass2_fixup == PASS2_FIXUP_ATTR) {
+                                       break;
+                               }
+
                                rcode = fr_str2int(allowed_return_codes,
                                                   c->data.vpt->name, 0);
                                if (!rcode) {