mirror of
https://github.com/apache/httpd.git
synced 2025-04-18 22:24:07 +03:00
The function apreq_param_make() will return NULL on failure. However NULL check are forgetten before derenference, which could lead to NULL pointer dereference. Adding NULL check to all use of apreq_param_make(). Submitted by: Zhou Qingyang <zhou1615@umn.edu> Github: closes #303 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908981 13f79535-47bb-0310-9956-ffa450edef68
359 lines
9.6 KiB
C
359 lines
9.6 KiB
C
/*
|
|
** Licensed to the Apache Software Foundation (ASF) under one or more
|
|
** contributor license agreements. See the NOTICE file distributed with
|
|
** this work for additional information regarding copyright ownership.
|
|
** The ASF licenses this file to You under the Apache License, Version 2.0
|
|
** (the "License"); you may not use this file except in compliance with
|
|
** the License. You may obtain a copy of the License at
|
|
**
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
|
|
#include "apreq_error.h"
|
|
#include "apreq_parser.h"
|
|
#include "apreq_util.h"
|
|
#include "apr_strings.h"
|
|
#include "apr_xml.h"
|
|
#include "apr_hash.h"
|
|
|
|
#define PARSER_STATUS_CHECK(PREFIX) do { \
|
|
if (ctx->status == PREFIX##_ERROR) \
|
|
return APREQ_ERROR_GENERAL; \
|
|
else if (ctx->status == PREFIX##_COMPLETE) \
|
|
return APR_SUCCESS; \
|
|
else if (bb == NULL) \
|
|
return APR_INCOMPLETE; \
|
|
} while (0);
|
|
|
|
APREQ_DECLARE(apreq_parser_t *) apreq_parser_make(apr_pool_t *pool,
|
|
apr_bucket_alloc_t *ba,
|
|
const char *content_type,
|
|
apreq_parser_function_t pfn,
|
|
apr_size_t brigade_limit,
|
|
const char *temp_dir,
|
|
apreq_hook_t *hook,
|
|
void *ctx)
|
|
{
|
|
apreq_parser_t *p = apr_palloc(pool, sizeof *p);
|
|
p->content_type = content_type;
|
|
p->parser = pfn;
|
|
p->hook = hook;
|
|
p->pool = pool;
|
|
p->bucket_alloc = ba;
|
|
p->brigade_limit = brigade_limit;
|
|
p->temp_dir = temp_dir;
|
|
p->ctx = ctx;
|
|
return p;
|
|
}
|
|
|
|
APREQ_DECLARE(apreq_hook_t *) apreq_hook_make(apr_pool_t *pool,
|
|
apreq_hook_function_t hook,
|
|
apreq_hook_t *next,
|
|
void *ctx)
|
|
{
|
|
apreq_hook_t *h = apr_palloc(pool, sizeof *h);
|
|
h->hook = hook;
|
|
h->next = next;
|
|
h->pool = pool;
|
|
h->ctx = ctx;
|
|
return h;
|
|
}
|
|
|
|
|
|
/*XXX this may need to check the parser's state before modifying the hook list */
|
|
APREQ_DECLARE(apr_status_t) apreq_parser_add_hook(apreq_parser_t *p,
|
|
apreq_hook_t *h)
|
|
{
|
|
apreq_hook_t *last = h;
|
|
|
|
while (last->next)
|
|
last = last->next;
|
|
|
|
last->next = p->hook;
|
|
p->hook = h;
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static int default_parsers_lock = 0;
|
|
static apr_hash_t *default_parsers = NULL;
|
|
static apr_pool_t *default_parser_pool = NULL;
|
|
|
|
static apr_status_t apreq_parsers_cleanup(void *data)
|
|
{
|
|
default_parsers_lock = 0;
|
|
default_parsers = NULL;
|
|
default_parser_pool = NULL;
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APREQ_DECLARE(apr_status_t) apreq_pre_initialize(apr_pool_t *pool)
|
|
{
|
|
apr_status_t status;
|
|
|
|
if (default_parser_pool != NULL)
|
|
return APR_SUCCESS;
|
|
|
|
if (default_parsers_lock)
|
|
return APREQ_ERROR_GENERAL;
|
|
|
|
status = apr_pool_create(&default_parser_pool, pool);
|
|
if (status != APR_SUCCESS)
|
|
return status;
|
|
apr_pool_tag(default_parser_pool, "apreq_default_parser");
|
|
|
|
apr_pool_cleanup_register(default_parser_pool, NULL,
|
|
apreq_parsers_cleanup,
|
|
apr_pool_cleanup_null);
|
|
|
|
default_parsers = apr_hash_make(default_parser_pool);
|
|
|
|
apreq_register_parser("application/x-www-form-urlencoded",
|
|
apreq_parse_urlencoded);
|
|
apreq_register_parser("multipart/form-data", apreq_parse_multipart);
|
|
apreq_register_parser("multipart/related", apreq_parse_multipart);
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APREQ_DECLARE(apr_status_t) apreq_post_initialize(apr_pool_t *pool)
|
|
{
|
|
(void)pool;
|
|
|
|
if (default_parser_pool == NULL)
|
|
return APREQ_ERROR_GENERAL;
|
|
|
|
default_parsers_lock = 1;
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APREQ_DECLARE(apr_status_t) apreq_initialize(apr_pool_t *pool)
|
|
{
|
|
apr_status_t s = apreq_pre_initialize(pool);
|
|
|
|
if (s != APR_SUCCESS)
|
|
return s;
|
|
|
|
return apreq_post_initialize(pool);
|
|
}
|
|
|
|
|
|
APREQ_DECLARE(apr_status_t) apreq_register_parser(const char *enctype,
|
|
apreq_parser_function_t pfn)
|
|
{
|
|
apreq_parser_function_t *f = NULL;
|
|
|
|
if (default_parsers == NULL)
|
|
return APR_EINIT;
|
|
|
|
if (enctype == NULL)
|
|
return APR_EINVAL;
|
|
|
|
if (default_parsers_lock)
|
|
return APREQ_ERROR_GENERAL;
|
|
|
|
if (pfn != NULL) {
|
|
f = apr_palloc(default_parser_pool, sizeof *f);
|
|
*f = pfn;
|
|
}
|
|
apr_hash_set(default_parsers, apr_pstrdup(default_parser_pool, enctype),
|
|
APR_HASH_KEY_STRING, f);
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
APREQ_DECLARE(apreq_parser_function_t)apreq_parser(const char *enctype)
|
|
{
|
|
apreq_parser_function_t *f;
|
|
apr_size_t tlen = 0;
|
|
|
|
if (enctype == NULL || default_parsers_lock == 0)
|
|
return NULL;
|
|
|
|
while (enctype[tlen] && enctype[tlen] != ';')
|
|
++tlen;
|
|
|
|
f = apr_hash_get(default_parsers, enctype, tlen);
|
|
|
|
if (f != NULL)
|
|
return *f;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
APREQ_DECLARE_HOOK(apreq_hook_disable_uploads)
|
|
{
|
|
return (bb == NULL) ? APR_SUCCESS : APREQ_ERROR_GENERAL;
|
|
}
|
|
|
|
APREQ_DECLARE_HOOK(apreq_hook_discard_brigade)
|
|
{
|
|
apr_status_t s = APR_SUCCESS;
|
|
if (hook->next)
|
|
s = apreq_hook_run(hook->next, param, bb);
|
|
if (bb != NULL)
|
|
apr_brigade_cleanup(bb);
|
|
return s;
|
|
}
|
|
|
|
|
|
/* generic parser */
|
|
|
|
struct gen_ctx {
|
|
apreq_param_t *param;
|
|
enum {
|
|
GEN_INCOMPLETE,
|
|
GEN_COMPLETE,
|
|
GEN_ERROR
|
|
} status;
|
|
};
|
|
|
|
APREQ_DECLARE_PARSER(apreq_parse_generic)
|
|
{
|
|
struct gen_ctx *ctx = parser->ctx;
|
|
apr_pool_t *pool = parser->pool;
|
|
apr_status_t s = APR_SUCCESS;
|
|
apr_bucket *e = APR_BRIGADE_LAST(bb);
|
|
unsigned saw_eos = 0;
|
|
|
|
if (ctx == NULL) {
|
|
parser->ctx = ctx = apr_palloc(pool, sizeof *ctx);
|
|
ctx->status = GEN_INCOMPLETE;
|
|
ctx->param = apreq_param_make(pool,
|
|
"_dummy_", strlen("_dummy_"), "", 0);
|
|
if (ctx->param == NULL)
|
|
return APR_ENOMEM;
|
|
ctx->param->upload = apr_brigade_create(pool, parser->bucket_alloc);
|
|
ctx->param->info = apr_table_make(pool, APREQ_DEFAULT_NELTS);
|
|
}
|
|
|
|
|
|
PARSER_STATUS_CHECK(GEN);
|
|
|
|
while (e != APR_BRIGADE_SENTINEL(bb)) {
|
|
if (APR_BUCKET_IS_EOS(e)) {
|
|
saw_eos = 1;
|
|
break;
|
|
}
|
|
e = APR_BUCKET_PREV(e);
|
|
}
|
|
|
|
if (parser->hook != NULL) {
|
|
s = apreq_hook_run(parser->hook, ctx->param, bb);
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = GEN_ERROR;
|
|
return s;
|
|
}
|
|
}
|
|
|
|
apreq_brigade_setaside(bb, pool);
|
|
s = apreq_brigade_concat(pool, parser->temp_dir, parser->brigade_limit,
|
|
ctx->param->upload, bb);
|
|
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = GEN_ERROR;
|
|
return s;
|
|
}
|
|
|
|
if (saw_eos) {
|
|
ctx->status = GEN_COMPLETE;
|
|
return APR_SUCCESS;
|
|
}
|
|
else
|
|
return APR_INCOMPLETE;
|
|
}
|
|
|
|
|
|
struct xml_ctx {
|
|
apr_xml_doc *doc;
|
|
apr_xml_parser *xml_parser;
|
|
enum {
|
|
XML_INCOMPLETE,
|
|
XML_COMPLETE,
|
|
XML_ERROR
|
|
} status;
|
|
};
|
|
|
|
|
|
APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser)
|
|
{
|
|
apr_pool_t *pool = hook->pool;
|
|
struct xml_ctx *ctx = hook->ctx;
|
|
apr_status_t s = APR_SUCCESS;
|
|
apr_bucket *e;
|
|
|
|
if (ctx == NULL) {
|
|
hook->ctx = ctx = apr_palloc(pool, sizeof *ctx);
|
|
ctx->doc = NULL;
|
|
ctx->xml_parser = apr_xml_parser_create(pool);
|
|
ctx->status = XML_INCOMPLETE;
|
|
}
|
|
|
|
PARSER_STATUS_CHECK(XML);
|
|
|
|
for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb);
|
|
e = APR_BUCKET_NEXT(e))
|
|
{
|
|
const char *data;
|
|
apr_size_t dlen;
|
|
|
|
if (APR_BUCKET_IS_EOS(e)) {
|
|
s = apr_xml_parser_done(ctx->xml_parser, &ctx->doc);
|
|
if (s == APR_SUCCESS) {
|
|
ctx->status = XML_COMPLETE;
|
|
if (hook->next)
|
|
s = apreq_hook_run(hook->next, param, bb);
|
|
}
|
|
else {
|
|
ctx->status = XML_ERROR;
|
|
}
|
|
return s;
|
|
}
|
|
else if (APR_BUCKET_IS_METADATA(e)) {
|
|
continue;
|
|
}
|
|
|
|
s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
|
|
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = XML_ERROR;
|
|
return s;
|
|
}
|
|
|
|
s = apr_xml_parser_feed(ctx->xml_parser, data, dlen);
|
|
|
|
if (s != APR_SUCCESS) {
|
|
ctx->status = XML_ERROR;
|
|
return s;
|
|
}
|
|
}
|
|
|
|
if (hook->next)
|
|
return apreq_hook_run(hook->next, param, bb);
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
|
|
APREQ_DECLARE_HOOK(apreq_hook_find_param)
|
|
{
|
|
apreq_hook_find_param_ctx_t *ctx = hook->ctx;
|
|
int is_final = (bb == NULL) || APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb));
|
|
apr_status_t s = (hook->next == NULL)
|
|
? APR_SUCCESS : apreq_hook_run(hook->next, param, bb);
|
|
|
|
if (is_final && s == APR_SUCCESS && ctx->param == NULL
|
|
&& strcasecmp(ctx->name, param->v.name) == 0) {
|
|
ctx->param = param;
|
|
ctx->prev->next = hook->next;
|
|
}
|
|
return s;
|
|
}
|