Coverage Report

Created: 2023-03-26 06:28

/src/httpd/server/apreq_parser_urlencoded.c
Line
Count
Source (jump to first uncovered line)
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
165
#define PARSER_STATUS_CHECK(PREFIX)   do {         \
24
165
    if (ctx->status == PREFIX##_ERROR)             \
25
165
        return APREQ_ERROR_GENERAL;                \
26
165
    else if (ctx->status == PREFIX##_COMPLETE)     \
27
165
        return APR_SUCCESS;                        \
28
165
    else if (bb == NULL)                           \
29
165
        return APR_INCOMPLETE;                     \
30
165
} 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
570
{
54
570
    apreq_param_t *param;
55
570
    apreq_value_t *v;
56
570
    apr_bucket *e, *f;
57
570
    apr_status_t s;
58
570
    struct iovec vec[APREQ_DEFAULT_NELTS];
59
570
    apr_array_header_t arr;
60
570
    apr_size_t mark;
61
570
    apreq_charset_t charset;
62
63
570
    if (nlen == 0)
64
1
        return APR_EBADARG;
65
66
569
    param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
67
569
    *(const apreq_value_t **)&v = &param->v;
68
69
569
    arr.pool     = pool;
70
569
    arr.elt_size = sizeof(struct iovec);
71
569
    arr.nelts    = 0;
72
569
    arr.nalloc   = APREQ_DEFAULT_NELTS;
73
569
    arr.elts     = (char *)vec;
74
75
569
    ++nlen, ++vlen;
76
569
    e = APR_BRIGADE_FIRST(bb);
77
78
569
    while (!APR_BUCKET_IS_EOS(e)) {
79
569
        struct iovec *iov = apr_array_push(&arr);
80
569
        apr_size_t len;
81
569
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
82
569
                            &len, APR_BLOCK_READ);
83
569
        if (s != APR_SUCCESS)
84
0
            return s;
85
86
569
        iov->iov_len = len;
87
569
        nlen -= len;
88
89
569
        e = APR_BUCKET_NEXT(e);
90
91
569
        if (nlen == 0) {
92
569
            iov->iov_len--;
93
569
            break;
94
569
        }
95
569
    }
96
97
569
    mark = arr.nelts;
98
99
569
    while (!APR_BUCKET_IS_EOS(e)) {
100
569
        struct iovec *iov = apr_array_push(&arr);
101
569
        apr_size_t len;
102
569
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
103
569
                            &len, APR_BLOCK_READ);
104
569
        if (s != APR_SUCCESS)
105
0
            return s;
106
107
569
        iov->iov_len = len;
108
569
        vlen -= len;
109
110
569
        e = APR_BUCKET_NEXT(e);
111
112
569
        if (vlen == 0) {
113
569
            iov->iov_len--;
114
569
            break;
115
569
        }
116
117
569
    }
118
119
569
    s = apreq_decodev(v->data, &vlen,
120
569
                      (struct iovec *)arr.elts + mark, arr.nelts - mark);
121
569
    if (s != APR_SUCCESS)
122
34
        return s;
123
124
535
    charset = apreq_charset_divine(v->data, vlen);
125
126
535
    v->name = v->data + vlen + 1;
127
535
    v->dlen = vlen;
128
129
535
    s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark);
130
535
    if (s != APR_SUCCESS)
131
35
        return s;
132
133
500
    switch (apreq_charset_divine(v->name, nlen)) {
134
92
    case APREQ_CHARSET_UTF8:
135
92
        if (charset == APREQ_CHARSET_ASCII)
136
59
            charset = APREQ_CHARSET_UTF8;
137
286
    case APREQ_CHARSET_ASCII:
138
286
        break;
139
140
158
    case APREQ_CHARSET_LATIN1:
141
158
        if (charset != APREQ_CHARSET_CP1252)
142
122
            charset = APREQ_CHARSET_LATIN1;
143
158
        break;
144
56
    case APREQ_CHARSET_CP1252:
145
56
        charset = APREQ_CHARSET_CP1252;
146
500
    }
147
148
500
    v->nlen = nlen;
149
150
1.50k
    while ((f = APR_BRIGADE_FIRST(bb)) != e)
151
1.00k
        apr_bucket_delete(f);
152
153
500
    apreq_param_tainted_on(param);
154
500
    apreq_param_charset_set(param, charset);
155
500
    *p = param;
156
500
    return APR_SUCCESS;
157
500
}
158
159
APREQ_DECLARE_PARSER(apreq_parse_urlencoded)
160
165
{
161
165
    apr_pool_t *pool = parser->pool;
162
165
    apr_bucket *e;
163
165
    struct url_ctx *ctx;
164
165
165
    if (parser->ctx == NULL) {
166
165
        ctx = apr_pcalloc(pool, sizeof *ctx);
167
165
        ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
168
165
        parser->ctx = ctx;
169
165
        ctx->status = URL_NAME;
170
165
    }
171
0
    else
172
0
        ctx = parser->ctx;
173
174
165
    PARSER_STATUS_CHECK(URL);
175
165
    e = APR_BRIGADE_LAST(ctx->bb);
176
165
    APR_BRIGADE_CONCAT(ctx->bb, bb);
177
178
665
 parse_url_brigade:
179
180
665
    for (e  = APR_BUCKET_NEXT(e);
181
760
         e != APR_BRIGADE_SENTINEL(ctx->bb);
182
665
         e  = APR_BUCKET_NEXT(e))
183
665
    {
184
665
        apreq_param_t *param;
185
665
        apr_size_t off = 0, dlen;
186
665
        const char *data;
187
665
        apr_status_t s;
188
189
665
        if (APR_BUCKET_IS_EOS(e)) {
190
0
            if (ctx->status == URL_NAME) {
191
0
                s = APR_SUCCESS;
192
0
            }
193
0
            else {
194
0
                s = split_urlword(&param, pool, ctx->bb, ctx->nlen, ctx->vlen);
195
0
                if (parser->hook != NULL && s == APR_SUCCESS)
196
0
                    s = apreq_hook_run(parser->hook, param, NULL);
197
198
0
                if (s == APR_SUCCESS) {
199
0
                    apreq_value_table_add(&param->v, t);
200
0
                    ctx->status = URL_COMPLETE;
201
0
                }
202
0
                else {
203
0
                    ctx->status = URL_ERROR;
204
0
                }
205
0
            }
206
207
0
            APR_BRIGADE_CONCAT(bb, ctx->bb);
208
0
            return s;
209
0
        }
210
211
665
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
212
665
        if ( s != APR_SUCCESS ) {
213
0
            ctx->status = URL_ERROR;
214
0
            return s;
215
0
        }
216
217
1.25k
    parse_url_bucket:
218
219
1.25k
        switch (ctx->status) {
220
221
665
        case URL_NAME:
222
5.75k
            while (off < dlen) {
223
5.68k
                switch (data[off++]) {
224
593
                case '=':
225
593
                    apr_bucket_split(e, off);
226
593
                    dlen -= off;
227
593
                    data += off;
228
593
                    off = 0;
229
593
                    e = APR_BUCKET_NEXT(e);
230
593
                    ctx->status = URL_VALUE;
231
593
                    goto parse_url_bucket;
232
5.09k
                default:
233
5.09k
                    ++ctx->nlen;
234
5.68k
                }
235
5.68k
            }
236
72
            break;
237
238
593
        case URL_VALUE:
239
3.91k
            while (off < dlen) {
240
241
3.89k
                switch (data[off++]) {
242
253
                case '&':
243
570
                case ';':
244
570
                    apr_bucket_split(e, off);
245
570
                    s = split_urlword(&param, pool, ctx->bb,
246
570
                                      ctx->nlen, ctx->vlen);
247
570
                    if (parser->hook != NULL && s == APR_SUCCESS)
248
500
                        s = apreq_hook_run(parser->hook, param, NULL);
249
250
570
                    if (s != APR_SUCCESS) {
251
70
                        ctx->status = URL_ERROR;
252
70
                        return s;
253
70
                    }
254
255
500
                    apreq_value_table_add(&param->v, t);
256
500
                    ctx->status = URL_NAME;
257
500
                    ctx->nlen = 0;
258
500
                    ctx->vlen = 0;
259
500
                    e = APR_BRIGADE_SENTINEL(ctx->bb);
260
500
                    goto parse_url_brigade;
261
262
3.32k
                default:
263
3.32k
                    ++ctx->vlen;
264
3.89k
                }
265
3.89k
            }
266
23
            break;
267
23
        default:
268
0
            ; /* not reached */
269
1.25k
        }
270
1.25k
    }
271
95
    apreq_brigade_setaside(ctx->bb, pool);
272
95
    return APR_INCOMPLETE;
273
665
}
274
275