/* ** 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 #include "apreq_parser.h" #include "apreq_error.h" #include "apreq_util.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); struct hdr_ctx { apr_bucket_brigade *bb; apr_size_t nlen; apr_size_t glen; apr_size_t vlen; enum { HDR_NAME, HDR_GAP, HDR_VALUE, HDR_NEWLINE, HDR_CONTINUE, HDR_COMPLETE, HDR_ERROR } status; }; /********************* header parsing utils ********************/ static apr_status_t split_header_line(apreq_param_t **p, apr_pool_t *pool, apr_bucket_brigade *bb, apr_size_t nlen, apr_size_t glen, apr_size_t vlen) { apreq_param_t *param; apreq_value_t *v; apr_bucket *e, *f; apr_status_t s; struct iovec vec[APREQ_DEFAULT_NELTS], *iov, *end; apr_array_header_t arr; char *dest; const char *data; apr_size_t dlen; if (nlen == 0) return APR_EBADARG; param = apreq_param_make(pool, NULL, nlen, NULL, vlen - 1); /*drop (CR)LF */ *(const apreq_value_t **)&v = ¶m->v; arr.pool = pool; arr.elt_size = sizeof(struct iovec); arr.nelts = 0; arr.nalloc = APREQ_DEFAULT_NELTS; arr.elts = (char *)vec; e = APR_BRIGADE_FIRST(bb); /* store name in a temporary iovec array */ while (nlen > 0) { apr_size_t len; end = apr_array_push(&arr); s = apr_bucket_read(e, (const char **)&end->iov_base, &len, APR_BLOCK_READ); if (s != APR_SUCCESS) return s; assert(nlen >= len); end->iov_len = len; nlen -= len; e = APR_BUCKET_NEXT(e); } /* skip gap */ while (glen > 0) { s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); if (s != APR_SUCCESS) return s; assert(glen >= dlen); glen -= dlen; e = APR_BUCKET_NEXT(e); } /* copy value */ assert(vlen > 0); dest = v->data; while (vlen > 0) { s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); if (s != APR_SUCCESS) return s; memcpy(dest, data, dlen); dest += dlen; assert(vlen >= dlen); vlen -= dlen; e = APR_BUCKET_NEXT(e); } assert(dest[-1] == '\n'); if (dest[-2] == '\r') --dest; dest[-1] = 0; v->dlen = (dest - v->data) - 1; /* write name */ v->name = dest; iov = (struct iovec *)arr.elts; while (iov <= end) { memcpy(dest, iov->iov_base, iov->iov_len); dest += iov->iov_len; ++iov; } *dest = 0; while ((f = APR_BRIGADE_FIRST(bb)) != e) apr_bucket_delete(f); apreq_param_tainted_on(param); *p = param; return APR_SUCCESS; } APREQ_DECLARE_PARSER(apreq_parse_headers) { apr_pool_t *pool = parser->pool; apr_bucket *e; struct hdr_ctx *ctx; if (parser->ctx == NULL) { ctx = apr_pcalloc(pool, sizeof *ctx); ctx->bb = apr_brigade_create(pool, parser->bucket_alloc); parser->ctx = ctx; ctx->status = HDR_NAME; } else ctx = parser->ctx; PARSER_STATUS_CHECK(HDR); e = APR_BRIGADE_LAST(ctx->bb); APR_BRIGADE_CONCAT(ctx->bb, bb); parse_hdr_brigade: /* parse the brigade for CRLF_CRLF-terminated header block, * each time starting from the front of the brigade. */ for (e = APR_BUCKET_NEXT(e); e != APR_BRIGADE_SENTINEL(ctx->bb); e = APR_BUCKET_NEXT(e)) { apr_size_t off = 0, dlen; const char *data; apr_status_t s; apreq_param_t *param = NULL; /* silences gcc-4.0 warning */ if (APR_BUCKET_IS_EOS(e)) { ctx->status = HDR_COMPLETE; APR_BRIGADE_CONCAT(bb, ctx->bb); return APR_SUCCESS; } s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); if ( s != APR_SUCCESS ) { ctx->status = HDR_ERROR; return s; } if (dlen == 0) continue; parse_hdr_bucket: /* gap nlen = 13 * vvv glen = 3 * Sample-Header: grape vlen = 5 * ^^^^^^^^^^^^^ ^^^^^ * name value */ switch (ctx->status) { case HDR_NAME: while (off < dlen) { switch (data[off++]) { case '\n': if (off < dlen) apr_bucket_split(e, off); e = APR_BUCKET_NEXT(e); do { apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb); apr_bucket_delete(f); } while (e != APR_BRIGADE_FIRST(ctx->bb)); APR_BRIGADE_CONCAT(bb, ctx->bb); ctx->status = HDR_COMPLETE; return APR_SUCCESS; case ':': if (off > 1) { apr_bucket_split(e, off - 1); dlen -= off - 1; data += off - 1; off = 1; e = APR_BUCKET_NEXT(e); } ++ctx->glen; ctx->status = HDR_GAP; goto parse_hdr_bucket; default: ++ctx->nlen; } } break; case HDR_GAP: while (off < dlen) { switch (data[off++]) { case ' ': case '\t': ++ctx->glen; break; case '\n': ctx->status = HDR_NEWLINE; goto parse_hdr_bucket; default: ctx->status = HDR_VALUE; if (off > 1) { apr_bucket_split(e, off - 1); dlen -= off - 1; data += off - 1; off = 1; e = APR_BUCKET_NEXT(e); } ++ctx->vlen; goto parse_hdr_bucket; } } break; case HDR_VALUE: while (off < dlen) { ++ctx->vlen; if (data[off++] == '\n') { ctx->status = HDR_NEWLINE; goto parse_hdr_bucket; } } break; case HDR_NEWLINE: if (off == dlen) break; else { switch (data[off]) { case ' ': case '\t': ctx->status = HDR_CONTINUE; ++off; ++ctx->vlen; break; default: /* can parse brigade now */ if (off > 0) apr_bucket_split(e, off); s = split_header_line(¶m, pool, ctx->bb, ctx->nlen, ctx->glen, ctx->vlen); if (parser->hook != NULL && s == APR_SUCCESS) s = apreq_hook_run(parser->hook, param, NULL); if (s != APR_SUCCESS) { ctx->status = HDR_ERROR; return s; } apreq_value_table_add(¶m->v, t); e = APR_BRIGADE_SENTINEL(ctx->bb); ctx->status = HDR_NAME; ctx->nlen = 0; ctx->vlen = 0; ctx->glen = 0; goto parse_hdr_brigade; } /* cases ' ', '\t' fall through to HDR_CONTINUE */ } case HDR_CONTINUE: while (off < dlen) { switch (data[off++]) { case ' ': case '\t': ++ctx->vlen; break; case '\n': ctx->status = HDR_NEWLINE; goto parse_hdr_bucket; default: ctx->status = HDR_VALUE; ++ctx->vlen; goto parse_hdr_bucket; } } break; default: ; /* not reached */ } } apreq_brigade_setaside(ctx->bb,pool); return APR_INCOMPLETE; }