Coverage Report

Created: 2023-03-26 06:28

/src/httpd/server/apreq_parser.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_error.h"
19
#include "apreq_parser.h"
20
#include "apreq_util.h"
21
#include "apr_strings.h"
22
#include "apr_xml.h"
23
#include "apr_hash.h"
24
25
0
#define PARSER_STATUS_CHECK(PREFIX)   do {         \
26
0
    if (ctx->status == PREFIX##_ERROR)             \
27
0
        return APREQ_ERROR_GENERAL;                \
28
0
    else if (ctx->status == PREFIX##_COMPLETE)     \
29
0
        return APR_SUCCESS;                        \
30
0
    else if (bb == NULL)                           \
31
0
        return APR_INCOMPLETE;                     \
32
0
} while (0);
33
34
APREQ_DECLARE(apreq_parser_t *) apreq_parser_make(apr_pool_t *pool,
35
                                                  apr_bucket_alloc_t *ba,
36
                                                  const char *content_type,
37
                                                  apreq_parser_function_t pfn,
38
                                                  apr_size_t brigade_limit,
39
                                                  const char *temp_dir,
40
                                                  apreq_hook_t *hook,
41
                                                  void *ctx)
42
955
{
43
955
    apreq_parser_t *p = apr_palloc(pool, sizeof *p);
44
955
    p->content_type = content_type;
45
955
    p->parser = pfn;
46
955
    p->hook = hook;
47
955
    p->pool = pool;
48
955
    p->bucket_alloc = ba;
49
955
    p->brigade_limit = brigade_limit;
50
955
    p->temp_dir = temp_dir;
51
955
    p->ctx = ctx;
52
955
    return p;
53
955
}
54
55
APREQ_DECLARE(apreq_hook_t *) apreq_hook_make(apr_pool_t *pool,
56
                                              apreq_hook_function_t hook,
57
                                              apreq_hook_t *next,
58
                                              void *ctx)
59
1.06k
{
60
1.06k
    apreq_hook_t *h = apr_palloc(pool, sizeof *h);
61
1.06k
    h->hook = hook;
62
1.06k
    h->next = next;
63
1.06k
    h->pool = pool;
64
1.06k
    h->ctx = ctx;
65
1.06k
    return h;
66
1.06k
}
67
68
69
/*XXX this may need to check the parser's state before modifying the hook list */
70
APREQ_DECLARE(apr_status_t) apreq_parser_add_hook(apreq_parser_t *p,
71
                                                  apreq_hook_t *h)
72
0
{
73
0
    apreq_hook_t *last = h;
74
75
0
    while (last->next)
76
0
        last = last->next;
77
78
0
    last->next = p->hook;
79
0
    p->hook = h;
80
81
0
    return APR_SUCCESS;
82
0
}
83
84
static int default_parsers_lock = 0;
85
static apr_hash_t *default_parsers = NULL;
86
static apr_pool_t *default_parser_pool = NULL;
87
88
static apr_status_t apreq_parsers_cleanup(void *data)
89
0
{
90
0
    default_parsers_lock = 0;
91
0
    default_parsers = NULL;
92
0
    default_parser_pool = NULL;
93
94
0
    return APR_SUCCESS;
95
0
}
96
97
APREQ_DECLARE(apr_status_t) apreq_pre_initialize(apr_pool_t *pool)
98
0
{
99
0
    apr_status_t status;
100
101
0
    if (default_parser_pool != NULL)
102
0
        return APR_SUCCESS;
103
104
0
    if (default_parsers_lock)
105
0
        return APREQ_ERROR_GENERAL;
106
107
0
    status = apr_pool_create(&default_parser_pool, pool);
108
0
    if (status != APR_SUCCESS)
109
0
        return status;
110
0
    apr_pool_tag(default_parser_pool, "apreq_default_parser");
111
112
0
    apr_pool_cleanup_register(default_parser_pool, NULL,
113
0
                              apreq_parsers_cleanup,
114
0
                              apr_pool_cleanup_null);
115
116
0
    default_parsers = apr_hash_make(default_parser_pool);
117
118
0
    apreq_register_parser("application/x-www-form-urlencoded",
119
0
                          apreq_parse_urlencoded);
120
0
    apreq_register_parser("multipart/form-data", apreq_parse_multipart);
121
0
    apreq_register_parser("multipart/related", apreq_parse_multipart);
122
123
0
    return APR_SUCCESS;
124
0
}
125
126
APREQ_DECLARE(apr_status_t) apreq_post_initialize(apr_pool_t *pool)
127
0
{
128
0
    (void)pool;
129
130
0
    if (default_parser_pool == NULL)
131
0
        return APREQ_ERROR_GENERAL;
132
133
0
    default_parsers_lock = 1;
134
0
    return APR_SUCCESS;
135
0
}
136
137
APREQ_DECLARE(apr_status_t) apreq_initialize(apr_pool_t *pool)
138
0
{
139
0
    apr_status_t s = apreq_pre_initialize(pool);
140
141
0
    if (s != APR_SUCCESS)
142
0
        return s;
143
144
0
    return apreq_post_initialize(pool);
145
0
}
146
147
148
APREQ_DECLARE(apr_status_t) apreq_register_parser(const char *enctype,
149
                                                  apreq_parser_function_t pfn)
150
0
{
151
0
    apreq_parser_function_t *f = NULL;
152
153
0
    if (default_parsers == NULL)
154
0
        return APR_EINIT;
155
156
0
    if (enctype == NULL)
157
0
        return APR_EINVAL;
158
159
0
    if (default_parsers_lock)
160
0
        return APREQ_ERROR_GENERAL;
161
162
0
    if (pfn != NULL) {
163
0
        f = apr_palloc(default_parser_pool, sizeof *f);
164
0
        *f = pfn;
165
0
    }
166
0
    apr_hash_set(default_parsers, apr_pstrdup(default_parser_pool, enctype),
167
0
                 APR_HASH_KEY_STRING, f);
168
169
0
    return APR_SUCCESS;
170
0
}
171
172
APREQ_DECLARE(apreq_parser_function_t)apreq_parser(const char *enctype)
173
0
{
174
0
    apreq_parser_function_t *f;
175
0
    apr_size_t tlen = 0;
176
177
0
    if (enctype == NULL || default_parsers_lock == 0)
178
0
        return NULL;
179
180
0
    while (enctype[tlen] && enctype[tlen] != ';')
181
0
        ++tlen;
182
183
0
    f = apr_hash_get(default_parsers, enctype, tlen);
184
185
0
    if (f != NULL)
186
0
        return *f;
187
0
    else
188
0
        return NULL;
189
0
}
190
191
APREQ_DECLARE_HOOK(apreq_hook_disable_uploads)
192
0
{
193
0
    return (bb == NULL) ? APR_SUCCESS : APREQ_ERROR_GENERAL;
194
0
}
195
196
APREQ_DECLARE_HOOK(apreq_hook_discard_brigade)
197
0
{
198
0
    apr_status_t s = APR_SUCCESS;
199
0
    if (hook->next)
200
0
        s = apreq_hook_run(hook->next, param, bb);
201
0
    if (bb != NULL)
202
0
        apr_brigade_cleanup(bb);
203
0
    return s;
204
0
}
205
206
207
/* generic parser */
208
209
struct gen_ctx {
210
    apreq_param_t               *param;
211
    enum {
212
        GEN_INCOMPLETE,
213
        GEN_COMPLETE,
214
        GEN_ERROR
215
    }                            status;
216
};
217
218
APREQ_DECLARE_PARSER(apreq_parse_generic)
219
0
{
220
0
    struct gen_ctx *ctx = parser->ctx;
221
0
    apr_pool_t *pool = parser->pool;
222
0
    apr_status_t s = APR_SUCCESS;
223
0
    apr_bucket *e = APR_BRIGADE_LAST(bb);
224
0
    unsigned saw_eos = 0;
225
226
0
    if (ctx == NULL) {
227
0
        parser->ctx = ctx = apr_palloc(pool, sizeof *ctx);
228
0
        ctx->status = GEN_INCOMPLETE;
229
0
        ctx->param = apreq_param_make(pool,
230
0
                                      "_dummy_", strlen("_dummy_"), "", 0);
231
0
        ctx->param->upload = apr_brigade_create(pool, parser->bucket_alloc);
232
0
        ctx->param->info = apr_table_make(pool, APREQ_DEFAULT_NELTS);
233
0
    }
234
235
236
0
    PARSER_STATUS_CHECK(GEN);
237
238
0
    while (e != APR_BRIGADE_SENTINEL(bb)) {
239
0
        if (APR_BUCKET_IS_EOS(e)) {
240
0
            saw_eos = 1;
241
0
            break;
242
0
        }
243
0
        e = APR_BUCKET_PREV(e);
244
0
    }
245
246
0
    if (parser->hook != NULL) {
247
0
        s = apreq_hook_run(parser->hook, ctx->param, bb);
248
0
        if (s != APR_SUCCESS) {
249
0
            ctx->status = GEN_ERROR;
250
0
            return s;
251
0
        }
252
0
    }
253
254
0
    apreq_brigade_setaside(bb, pool);
255
0
    s = apreq_brigade_concat(pool, parser->temp_dir, parser->brigade_limit,
256
0
                             ctx->param->upload, bb);
257
258
0
    if (s != APR_SUCCESS) {
259
0
        ctx->status = GEN_ERROR;
260
0
        return s;
261
0
    }
262
263
0
    if (saw_eos) {
264
0
        ctx->status = GEN_COMPLETE;
265
0
        return APR_SUCCESS;
266
0
    }
267
0
    else
268
0
        return APR_INCOMPLETE;
269
0
}
270
271
272
struct xml_ctx {
273
    apr_xml_doc                 *doc;
274
    apr_xml_parser              *xml_parser;
275
    enum {
276
        XML_INCOMPLETE,
277
        XML_COMPLETE,
278
        XML_ERROR
279
    }                            status;
280
};
281
282
283
APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser)
284
0
{
285
0
    apr_pool_t *pool = hook->pool;
286
0
    struct xml_ctx *ctx = hook->ctx;
287
0
    apr_status_t s = APR_SUCCESS;
288
0
    apr_bucket *e;
289
290
0
    if (ctx == NULL) {
291
0
        hook->ctx = ctx = apr_palloc(pool, sizeof *ctx);
292
0
        ctx->doc = NULL;
293
0
        ctx->xml_parser = apr_xml_parser_create(pool);
294
0
        ctx->status = XML_INCOMPLETE;
295
0
    }
296
297
0
    PARSER_STATUS_CHECK(XML);
298
299
0
    for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb);
300
0
         e = APR_BUCKET_NEXT(e))
301
0
    {
302
0
        const char *data;
303
0
        apr_size_t dlen;
304
305
0
        if (APR_BUCKET_IS_EOS(e)) {
306
0
            s = apr_xml_parser_done(ctx->xml_parser, &ctx->doc);
307
0
            if (s == APR_SUCCESS) {
308
0
                ctx->status = XML_COMPLETE;
309
0
                if (hook->next)
310
0
                    s = apreq_hook_run(hook->next, param, bb);
311
0
            }
312
0
            else {
313
0
                ctx->status = XML_ERROR;
314
0
            }
315
0
            return s;
316
0
        }
317
0
        else if (APR_BUCKET_IS_METADATA(e)) {
318
0
            continue;
319
0
        }
320
321
0
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
322
323
0
        if (s != APR_SUCCESS) {
324
0
            ctx->status = XML_ERROR;
325
0
            return s;
326
0
        }
327
328
0
        s = apr_xml_parser_feed(ctx->xml_parser, data, dlen);
329
330
0
        if (s != APR_SUCCESS) {
331
0
            ctx->status = XML_ERROR;
332
0
            return s;
333
0
        }
334
0
    }
335
336
0
    if (hook->next)
337
0
        return apreq_hook_run(hook->next, param, bb);
338
339
0
    return APR_SUCCESS;
340
0
}
341
342
343
APREQ_DECLARE_HOOK(apreq_hook_find_param)
344
0
{
345
0
    apreq_hook_find_param_ctx_t *ctx = hook->ctx;
346
0
    int is_final = (bb == NULL) || APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb));
347
0
    apr_status_t s = (hook->next == NULL)
348
0
        ? APR_SUCCESS : apreq_hook_run(hook->next, param, bb);
349
350
0
    if (is_final && s == APR_SUCCESS && ctx->param == NULL
351
0
        && strcasecmp(ctx->name, param->v.name) == 0) {
352
0
        ctx->param = param;
353
0
        ctx->prev->next = hook->next;
354
0
    }
355
0
    return s;
356
0
}