Coverage Report

Created: 2026-02-26 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/httpd/server/apreq_parser_urlencoded.c
Line
Count
Source
1
/*
2
**  Licensed to the Apache Software Foundation (ASF) under one or more
3
** contributor license agreements.  See the NOTICE file distributed with
4
** this work for additional information regarding copyright ownership.
5
** The ASF licenses this file to You under the Apache License, Version 2.0
6
** (the "License"); you may not use this file except in compliance with
7
** the License.  You may obtain a copy of the License at
8
**
9
**      http://www.apache.org/licenses/LICENSE-2.0
10
**
11
**  Unless required by applicable law or agreed to in writing, software
12
**  distributed under the License is distributed on an "AS IS" BASIS,
13
**  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
**  See the License for the specific language governing permissions and
15
**  limitations under the License.
16
*/
17
18
#include "apreq_parser.h"
19
#include "apreq_util.h"
20
#include "apreq_error.h"
21
22
23
144
#define PARSER_STATUS_CHECK(PREFIX)   do {         \
24
144
    if (ctx->status == PREFIX##_ERROR)             \
25
144
        return APREQ_ERROR_GENERAL;                \
26
144
    else if (ctx->status == PREFIX##_COMPLETE)     \
27
144
        return APR_SUCCESS;                        \
28
144
    else if (bb == NULL)                           \
29
144
        return APR_INCOMPLETE;                     \
30
144
} while (0);
31
32
33
34
struct url_ctx {
35
    apr_bucket_brigade *bb;
36
    apr_size_t          nlen;
37
    apr_size_t          vlen;
38
    enum {
39
        URL_NAME,
40
        URL_VALUE,
41
        URL_COMPLETE,
42
        URL_ERROR
43
    }                   status;
44
};
45
46
47
/******************** application/x-www-form-urlencoded ********************/
48
49
static apr_status_t split_urlword(apreq_param_t **p, apr_pool_t *pool,
50
                                  apr_bucket_brigade *bb,
51
                                  apr_size_t nlen,
52
                                  apr_size_t vlen)
53
577
{
54
577
    apreq_param_t *param;
55
577
    apreq_value_t *v;
56
577
    apr_bucket *e, *f;
57
577
    apr_status_t s;
58
577
    struct iovec vec[APREQ_DEFAULT_NELTS];
59
577
    apr_array_header_t arr;
60
577
    apr_size_t mark;
61
577
    apreq_charset_t charset;
62
63
577
    if (nlen == 0)
64
1
        return APR_EBADARG;
65
66
576
    param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
67
576
    if (param == NULL)
68
0
        return APR_ENOMEM;
69
576
    *(const apreq_value_t **)&v = &param->v;
70
71
576
    arr.pool     = pool;
72
576
    arr.elt_size = sizeof(struct iovec);
73
576
    arr.nelts    = 0;
74
576
    arr.nalloc   = APREQ_DEFAULT_NELTS;
75
576
    arr.elts     = (char *)vec;
76
77
576
    ++nlen, ++vlen;
78
576
    e = APR_BRIGADE_FIRST(bb);
79
80
576
    while (!APR_BUCKET_IS_EOS(e)) {
81
576
        struct iovec *iov = apr_array_push(&arr);
82
576
        apr_size_t len;
83
576
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
84
576
                            &len, APR_BLOCK_READ);
85
576
        if (s != APR_SUCCESS)
86
0
            return s;
87
88
576
        iov->iov_len = len;
89
576
        nlen -= len;
90
91
576
        e = APR_BUCKET_NEXT(e);
92
93
576
        if (nlen == 0) {
94
576
            iov->iov_len--;
95
576
            break;
96
576
        }
97
576
    }
98
99
576
    mark = arr.nelts;
100
101
576
    while (!APR_BUCKET_IS_EOS(e)) {
102
576
        struct iovec *iov = apr_array_push(&arr);
103
576
        apr_size_t len;
104
576
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
105
576
                            &len, APR_BLOCK_READ);
106
576
        if (s != APR_SUCCESS)
107
0
            return s;
108
109
576
        iov->iov_len = len;
110
576
        vlen -= len;
111
112
576
        e = APR_BUCKET_NEXT(e);
113
114
576
        if (vlen == 0) {
115
576
            iov->iov_len--;
116
576
            break;
117
576
        }
118
119
576
    }
120
121
576
    s = apreq_decodev(v->data, &vlen,
122
576
                      (struct iovec *)arr.elts + mark, arr.nelts - mark);
123
576
    if (s != APR_SUCCESS)
124
22
        return s;
125
126
554
    charset = apreq_charset_divine(v->data, vlen);
127
128
554
    v->name = v->data + vlen + 1;
129
554
    v->dlen = vlen;
130
131
554
    s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark);
132
554
    if (s != APR_SUCCESS)
133
39
        return s;
134
135
515
    switch (apreq_charset_divine(v->name, nlen)) {
136
68
    case APREQ_CHARSET_UTF8:
137
68
        if (charset == APREQ_CHARSET_ASCII)
138
37
            charset = APREQ_CHARSET_UTF8;
139
263
    case APREQ_CHARSET_ASCII:
140
263
        break;
141
142
164
    case APREQ_CHARSET_LATIN1:
143
164
        if (charset != APREQ_CHARSET_CP1252)
144
139
            charset = APREQ_CHARSET_LATIN1;
145
164
        break;
146
88
    case APREQ_CHARSET_CP1252:
147
88
        charset = APREQ_CHARSET_CP1252;
148
515
    }
149
150
515
    v->nlen = nlen;
151
152
1.54k
    while ((f = APR_BRIGADE_FIRST(bb)) != e)
153
1.03k
        apr_bucket_delete(f);
154
155
515
    apreq_param_tainted_on(param);
156
515
    apreq_param_charset_set(param, charset);
157
515
    *p = param;
158
515
    return APR_SUCCESS;
159
515
}
160
161
APREQ_DECLARE_PARSER(apreq_parse_urlencoded)
162
144
{
163
144
    apr_pool_t *pool = parser->pool;
164
144
    apr_bucket *e;
165
144
    struct url_ctx *ctx;
166
167
144
    if (parser->ctx == NULL) {
168
144
        ctx = apr_pcalloc(pool, sizeof *ctx);
169
144
        ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
170
144
        parser->ctx = ctx;
171
144
        ctx->status = URL_NAME;
172
144
    }
173
0
    else
174
0
        ctx = parser->ctx;
175
176
144
    PARSER_STATUS_CHECK(URL);
177
144
    e = APR_BRIGADE_LAST(ctx->bb);
178
144
    APR_BRIGADE_CONCAT(ctx->bb, bb);
179
180
659
 parse_url_brigade:
181
182
659
    for (e  = APR_BUCKET_NEXT(e);
183
741
         e != APR_BRIGADE_SENTINEL(ctx->bb);
184
659
         e  = APR_BUCKET_NEXT(e))
185
659
    {
186
659
        apreq_param_t *param;
187
659
        apr_size_t off = 0, dlen;
188
659
        const char *data;
189
659
        apr_status_t s;
190
191
659
        if (APR_BUCKET_IS_EOS(e)) {
192
0
            if (ctx->status == URL_NAME) {
193
0
                s = APR_SUCCESS;
194
0
            }
195
0
            else {
196
0
                s = split_urlword(&param, pool, ctx->bb, ctx->nlen, ctx->vlen);
197
0
                if (parser->hook != NULL && s == APR_SUCCESS)
198
0
                    s = apreq_hook_run(parser->hook, param, NULL);
199
200
0
                if (s == APR_SUCCESS) {
201
0
                    apreq_value_table_add(&param->v, t);
202
0
                    ctx->status = URL_COMPLETE;
203
0
                }
204
0
                else {
205
0
                    ctx->status = URL_ERROR;
206
0
                }
207
0
            }
208
209
0
            APR_BRIGADE_CONCAT(bb, ctx->bb);
210
0
            return s;
211
0
        }
212
213
659
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
214
659
        if ( s != APR_SUCCESS ) {
215
0
            ctx->status = URL_ERROR;
216
0
            return s;
217
0
        }
218
219
1.24k
    parse_url_bucket:
220
221
1.24k
        switch (ctx->status) {
222
223
659
        case URL_NAME:
224
5.51k
            while (off < dlen) {
225
5.45k
                switch (data[off++]) {
226
590
                case '=':
227
590
                    apr_bucket_split(e, off);
228
590
                    dlen -= off;
229
590
                    data += off;
230
590
                    off = 0;
231
590
                    e = APR_BUCKET_NEXT(e);
232
590
                    ctx->status = URL_VALUE;
233
590
                    goto parse_url_bucket;
234
4.86k
                default:
235
4.86k
                    ++ctx->nlen;
236
5.45k
                }
237
5.45k
            }
238
69
            break;
239
240
590
        case URL_VALUE:
241
2.86k
            while (off < dlen) {
242
243
2.85k
                switch (data[off++]) {
244
120
                case '&':
245
577
                case ';':
246
577
                    apr_bucket_split(e, off);
247
577
                    s = split_urlword(&param, pool, ctx->bb,
248
577
                                      ctx->nlen, ctx->vlen);
249
577
                    if (parser->hook != NULL && s == APR_SUCCESS)
250
515
                        s = apreq_hook_run(parser->hook, param, NULL);
251
252
577
                    if (s != APR_SUCCESS) {
253
62
                        ctx->status = URL_ERROR;
254
62
                        return s;
255
62
                    }
256
257
515
                    apreq_value_table_add(&param->v, t);
258
515
                    ctx->status = URL_NAME;
259
515
                    ctx->nlen = 0;
260
515
                    ctx->vlen = 0;
261
515
                    e = APR_BRIGADE_SENTINEL(ctx->bb);
262
515
                    goto parse_url_brigade;
263
264
2.27k
                default:
265
2.27k
                    ++ctx->vlen;
266
2.85k
                }
267
2.85k
            }
268
13
            break;
269
13
        default:
270
0
            ; /* not reached */
271
1.24k
        }
272
1.24k
    }
273
82
    apreq_brigade_setaside(ctx->bb, pool);
274
82
    return APR_INCOMPLETE;
275
659
}
276
277