Le 08/04/2021 à 20:59, Tim Duesterhus a écrit :
Willy,
Christopher,

this is in prepatation for the next normalizer which normalizes character case
of the percent encoding.

The resources I found are not clear on whether a percent that is not followed
by two hex digits is valid or not. Most browsers and servers appear to support
it, so I opted to leave the user a choice whether to reject it or not.

It is probably a good choice


To support this I need to differ between "normalizing failed for internal
reasons" and "normalizing failed, because the user sent garbage".

Overall I am not happy with this patch, because the control flow is ugly. I
also wasn't sure in which cases I need to increase server counters (e.g.
failed_rewrites) or not. I'd be happy if you could give some advice!


The function may be simplified. I'll propose you a new version, based on my
other comments.

[...]

+enum uri_normalizer_err {
+       URI_NORMALIZER_ERR_NONE = 0,
+       URI_NORMALIZER_ERR_TRASH,
+       URI_NORMALIZER_ERR_ALLOC,
+       URI_NORMALIZER_ERR_INVALID_INPUT,
+       URI_NORMALIZER_ERR_BUG = 0xdead,
+};

URI_NORMALIZER_ERR_BUG value is useless. And I guess it could be useful to have
a rewrite error too.

[...]

    fail_rewrite:
        _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
        if (s->flags & SF_BE_ASSIGNED)
@@ -300,6 +301,24 @@ static enum act_return http_action_normalize_uri(struct 
act_rule *rule, struct p
                        s->flags |= SF_ERR_PRXCOND;
        }
        goto leave;
+
+  err:
+       switch (err) {
+       case URI_NORMALIZER_ERR_NONE:

You must break here, otherwise, an error is returned on success.

+       case URI_NORMALIZER_ERR_BUG:
+       case URI_NORMALIZER_ERR_TRASH:
+               /* These must not happen. */
+               BUG_ON("Logic error during URI normalization.");
+               ret = ACT_RET_ERR;
+               goto leave;
+       case URI_NORMALIZER_ERR_ALLOC:
+               goto fail_alloc;
+       case URI_NORMALIZER_ERR_INVALID_INPUT:
+               ret = ACT_RET_INV;
+               goto leave;
+       }
+
+       my_unreachable();
  }

You may rewrite http_action_normalize_uri() this way (on top of your series,
normalizers must be adapted). It is not untested nor compiled.

static enum act_return http_action_normalize_uri(struct act_rule *rule, struct 
proxy *px,
                                                 struct session *sess, struct 
stream *s, int flags)
{
        enum act_return ret = ACT_RET_CONT;
        struct htx *htx = htxbuf(&s->req.buf);
        const struct ist uri = htx_sl_req_uri(http_get_stline(htx));
        const struct ist path = http_get_path(uri);
        struct ist dst;
        struct buffer *replace = alloc_trash_chunk();
        enum uri_normalizer_err err;

        if (!replace)
                goto fail_alloc;

        if (!isttest(path))
                goto leave;

        dst = ist2(replace->area, replace->size);
        switch ((enum act_normalize_uri) rule->action) {
        case ACT_NORMALIZE_URI_MERGE_SLASHES:
                err = uri_normalizer_path_merge_slashes(iststop(path, '?'), 
&dst);
                if (err != URI_NORMALIZER_ERR_NONE)
                        break;
                if (!http_replace_req_path(htx, dst, 0))
                        goto fail_rewrite;
                break;
        case ACT_NORMALIZE_URI_DOTDOT:
        case ACT_NORMALIZE_URI_DOTDOT_FULL:
                err = uri_normalizer_path_dotdot(iststop(path, '?'), &dst, 
rule->action == ACT_NORMALIZE_URI_DOTDOT_FULL);
                if (err != URI_NORMALIZER_ERR_NONE)
                        break;
                if (!http_replace_req_path(htx, dst, 0))
                        goto fail_rewrite;
                break;
        case ACT_NORMALIZE_URI_SORT_QUERY:
                err = uri_normalizer_query_sort(istfind(path, '?'), &dst, '&');
                if (err != URI_NORMALIZER_ERR_NONE)
                        break;
                if (!http_replace_req_query(htx, dst))
                        goto fail_rewrite;
                break;
        case ACT_NORMALIZE_URI_PERCENT_UPPER:
        case ACT_NORMALIZE_URI_PERCENT_UPPER_STRICT:
                err = uri_normalizer_percent_upper(path, &dst, rule->action == 
ACT_NORMALIZE_URI_PERCENT_UPPER_STRICT);
                if (err != URI_NORMALIZER_ERR_NONE)
                        break;
                if (!http_replace_req_path(htx, dst, 1))
                        goto fail_rewrite;
                break;
        }

        switch (err) {
        case URI_NORMALIZER_ERR_NONE:
                break;
        case URI_NORMALIZER_ERR_BUG:
        case URI_NORMALIZER_ERR_TRASH:
                /* These must not happen. */
                BUG_ON("Logic error during URI normalization.");
                ret = ACT_RET_ERR;
                break;
        case URI_NORMALIZER_ERR_ALLOC:
                goto fail_alloc;
        case URI_NORMALIZER_ERR_INVALID_INPUT:
                ret = ACT_RET_INV;
                goto leave;
        }

  leave:
        free_trash_chunk(replace);
        return ret;

  fail_alloc:
        if (!(s->flags & SF_ERR_MASK))
                s->flags |= SF_ERR_RESOURCE;
        ret = ACT_RET_ERR;
        goto leave;

  fail_rewrite:
        _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1);
        if (s->flags & SF_BE_ASSIGNED)
                _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1);
        if (sess->listener && sess->listener->counters)
                _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1);
        if (objt_server(s->target))
                
_HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1);

        if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) {
                ret = ACT_RET_ERR;
                if (!(s->flags & SF_ERR_MASK))
                        s->flags |= SF_ERR_PRXCOND;
        }
        goto leave;
}

--
Christopher Faulet

Reply via email to