Coverage Report

Created: 2023-06-07 06:38

/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
985
{
43
985
    apreq_parser_t *p = apr_palloc(pool, sizeof *p);
44
985
    p->content_type = content_type;
45
985
    p->parser = pfn;
46
985
    p->hook = hook;
47
985
    p->pool = pool;
48
985
    p->bucket_alloc = ba;
49
985
    p->brigade_limit = brigade_limit;
50
985
    p->temp_dir = temp_dir;
51
985
    p->ctx = ctx;
52
985
    return p;
53
985
}
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.10k
{
60
1.10k
    apreq_hook_t *h = apr_palloc(pool, sizeof *h);
61
1.10k
    h->hook = hook;
62
1.10k
    h->next = next;
63
1.10k
    h->pool = pool;
64
1.10k
    h->ctx = ctx;
65
1.10k
    return h;
66
1.10k
}
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
        if (ctx->param == NULL)
232
0
            return APR_ENOMEM;
233
0
        ctx->param->upload = apr_brigade_create(pool, parser->bucket_alloc);
234
0
        ctx->param->info = apr_table_make(pool, APREQ_DEFAULT_NELTS);
235
0
    }
236
237
238
0
    PARSER_STATUS_CHECK(GEN);
239
240
0
    while (e != APR_BRIGADE_SENTINEL(bb)) {
241
0
        if (APR_BUCKET_IS_EOS(e)) {
242
0
            saw_eos = 1;
243
0
            break;
244
0
        }
245
0
        e = APR_BUCKET_PREV(e);
246
0
    }
247
248
0
    if (parser->hook != NULL) {
249
0
        s = apreq_hook_run(parser->hook, ctx->param, bb);
250
0
        if (s != APR_SUCCESS) {
251
0
            ctx->status = GEN_ERROR;
252
0
            return s;
253
0
        }
254
0
    }
255
256
0
    apreq_brigade_setaside(bb, pool);
257
0
    s = apreq_brigade_concat(pool, parser->temp_dir, parser->brigade_limit,
258
0
                             ctx->param->upload, bb);
259
260
0
    if (s != APR_SUCCESS) {
261
0
        ctx->status = GEN_ERROR;
262
0
        return s;
263
0
    }
264
265
0
    if (saw_eos) {
266
0
        ctx->status = GEN_COMPLETE;
267
0
        return APR_SUCCESS;
268
0
    }
269
0
    else
270
0
        return APR_INCOMPLETE;
271
0
}
272
273
274
struct xml_ctx {
275
    apr_xml_doc                 *doc;
276
    apr_xml_parser              *xml_parser;
277
    enum {
278
        XML_INCOMPLETE,
279
        XML_COMPLETE,
280
        XML_ERROR
281
    }                            status;
282
};
283
284
285
APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser)
286
0
{
287
0
    apr_pool_t *pool = hook->pool;
288
0
    struct xml_ctx *ctx = hook->ctx;
289
0
    apr_status_t s = APR_SUCCESS;
290
0
    apr_bucket *e;
291
292
0
    if (ctx == NULL) {
293
0
        hook->ctx = ctx = apr_palloc(pool, sizeof *ctx);
294
0
        ctx->doc = NULL;
295
0
        ctx->xml_parser = apr_xml_parser_create(pool);
296
0
        ctx->status = XML_INCOMPLETE;
297
0
    }
298
299
0
    PARSER_STATUS_CHECK(XML);
300
301
0
    for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb);
302
0
         e = APR_BUCKET_NEXT(e))
303
0
    {
304
0
        const char *data;
305
0
        apr_size_t dlen;
306
307
0
        if (APR_BUCKET_IS_EOS(e)) {
308
0
            s = apr_xml_parser_done(ctx->xml_parser, &ctx->doc);
309
0
            if (s == APR_SUCCESS) {
310
0
                ctx->status = XML_COMPLETE;
311
0
                if (hook->next)
312
0
                    s = apreq_hook_run(hook->next, param, bb);
313
0
            }
314
0
            else {
315
0
                ctx->status = XML_ERROR;
316
0
            }
317
0
            return s;
318
0
        }
319
0
        else if (APR_BUCKET_IS_METADATA(e)) {
320
0
            continue;
321
0
        }
322
323
0
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
324
325
0
        if (s != APR_SUCCESS) {
326
0
            ctx->status = XML_ERROR;
327
0
            return s;
328
0
        }
329
330
0
        s = apr_xml_parser_feed(ctx->xml_parser, data, dlen);
331
332
0
        if (s != APR_SUCCESS) {
333
0
            ctx->status = XML_ERROR;
334
0
            return s;
335
0
        }
336
0
    }
337
338
0
    if (hook->next)
339
0
        return apreq_hook_run(hook->next, param, bb);
340
341
0
    return APR_SUCCESS;
342
0
}
343
344
345
APREQ_DECLARE_HOOK(apreq_hook_find_param)
346
0
{
347
0
    apreq_hook_find_param_ctx_t *ctx = hook->ctx;
348
0
    int is_final = (bb == NULL) || APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb));
349
0
    apr_status_t s = (hook->next == NULL)
350
0
        ? APR_SUCCESS : apreq_hook_run(hook->next, param, bb);
351
352
0
    if (is_final && s == APR_SUCCESS && ctx->param == NULL
353
0
        && strcasecmp(ctx->name, param->v.name) == 0) {
354
0
        ctx->param = param;
355
0
        ctx->prev->next = hook->next;
356
0
    }
357
0
    return s;
358
0
}