Coverage Report

Created: 2025-10-10 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/httpd/server/apreq_parser_header.c
Line
Count
Source
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
#include <assert.h>
18
#include "apreq_parser.h"
19
#include "apreq_error.h"
20
#include "apreq_util.h"
21
22
#include "apr_lib.h" /* for apr_iscntrl() & co */
23
24
856
#define PARSER_STATUS_CHECK(PREFIX)   do {         \
25
856
    if (ctx->status == PREFIX##_ERROR)             \
26
856
        return APREQ_ERROR_GENERAL;                \
27
856
    else if (ctx->status == PREFIX##_COMPLETE)     \
28
856
        return APR_SUCCESS;                        \
29
856
    else if (bb == NULL)                           \
30
856
        return APR_INCOMPLETE;                     \
31
856
} while (0);
32
33
34
struct hdr_ctx {
35
    apr_bucket_brigade *bb;
36
    apr_size_t          nlen;
37
    apr_size_t          glen;
38
    apr_size_t          vlen;
39
    enum {
40
        HDR_NAME,
41
        HDR_GAP,
42
        HDR_VALUE,
43
        HDR_NEWLINE,
44
        HDR_ENDLINE,
45
        HDR_FOLDLINE,
46
        HDR_LASTLINE,
47
        HDR_COMPLETE,
48
        HDR_ERROR
49
    }                   status;
50
};
51
52
/********************* header parsing utils ********************/
53
54
55
static apr_bucket *split_header_line(apr_bucket *e, apr_size_t *off,
56
                                     const char **data, apr_size_t *dlen)
57
3.64k
{
58
3.64k
    if (*off > 1) {
59
3.63k
        apr_bucket_split(e, *off - 1);
60
3.63k
        e = APR_BUCKET_NEXT(e);
61
3.63k
        *dlen -= *off - 1;
62
3.63k
        *data += *off - 1;
63
3.63k
        *off = 1;
64
3.63k
    }
65
3.64k
    return e;
66
3.64k
}
67
68
static apr_status_t consume_header_line(apreq_param_t **p,
69
                                        apr_pool_t *pool,
70
                                        apr_bucket_brigade *bb,
71
                                        apr_size_t nlen,
72
                                        apr_size_t glen,
73
                                        apr_size_t vlen)
74
1.17k
{
75
1.17k
    apreq_param_t *param;
76
1.17k
    apreq_value_t *v;
77
1.17k
    apr_bucket *e, *f;
78
1.17k
    apr_status_t s;
79
1.17k
    struct iovec vec[APREQ_DEFAULT_NELTS], *iov;
80
1.17k
    apr_array_header_t arr;
81
1.17k
    char *dest;
82
1.17k
    const char *data;
83
1.17k
    apr_size_t dlen;
84
1.17k
    int i, eol = 0;
85
86
1.17k
    param = apreq_param_make(pool, NULL, nlen, NULL, vlen);
87
1.17k
    if (param == NULL)
88
0
        return APR_ENOMEM;
89
1.17k
    *(const apreq_value_t **)&v = &param->v;
90
91
1.17k
    arr.pool     = pool;
92
1.17k
    arr.elt_size = sizeof(struct iovec);
93
1.17k
    arr.nelts    = 0;
94
1.17k
    arr.nalloc   = APREQ_DEFAULT_NELTS;
95
1.17k
    arr.elts     = (char *)vec;
96
97
1.17k
    e = APR_BRIGADE_FIRST(bb);
98
99
    /* store name in a temporary iovec array */
100
1.18k
    do {
101
1.18k
        apr_size_t len;
102
103
1.18k
        assert(e != APR_BRIGADE_SENTINEL(bb));
104
1.18k
        iov = (struct iovec *)apr_array_push(&arr);
105
1.18k
        s = apr_bucket_read(e, (const char **)&iov->iov_base,
106
1.18k
                            &len, APR_BLOCK_READ);
107
1.18k
        if (s != APR_SUCCESS)
108
0
            return s;
109
1.18k
        iov->iov_len = len;
110
111
1.18k
        assert(nlen >= len);
112
1.18k
        nlen -= len;
113
114
1.18k
        e = APR_BUCKET_NEXT(e);
115
1.18k
    } while (nlen > 0);
116
117
    /* skip gap */
118
1.18k
    do {
119
1.18k
        assert(e != APR_BRIGADE_SENTINEL(bb));
120
1.18k
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
121
1.18k
        if (s != APR_SUCCESS)
122
0
            return s;
123
124
1.18k
        assert(glen >= dlen);
125
1.18k
        glen -= dlen;
126
127
1.18k
        e = APR_BUCKET_NEXT(e);
128
1.18k
    } while (glen > 0);
129
130
    /* copy value */
131
1.17k
    dest = v->data;
132
1.17k
    do {
133
1.17k
        apr_size_t off;
134
135
1.17k
        assert(e != APR_BRIGADE_SENTINEL(bb));
136
1.17k
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
137
1.17k
        if (s != APR_SUCCESS)
138
0
            return s;
139
140
12.5k
        for (off = 0; off < dlen; ++off) {
141
11.3k
            const char ch = data[off];
142
11.3k
            if (ch == '\r' || ch == '\n') {
143
                /* Eat [CR]LF of continuation or end of line */
144
1.63k
                if (!vlen && ch == '\n')
145
1.17k
                    eol = 1; /* done */
146
1.63k
                continue;
147
1.63k
            }
148
11.3k
            assert(vlen > 0);
149
9.76k
            *dest = ch;
150
9.76k
            ++dest;
151
9.76k
            --vlen;
152
9.76k
        }
153
154
1.17k
        e = APR_BUCKET_NEXT(e);
155
1.17k
    } while (!eol);
156
1.17k
    v->dlen = dest - v->data;
157
1.17k
    *dest++ = 0;
158
159
    /* write name */
160
1.17k
    v->name = dest;
161
2.35k
    for (i = 0; i < arr.nelts; ++i) {
162
1.18k
        iov = &((struct iovec *)arr.elts)[i];
163
1.18k
        memcpy(dest, iov->iov_base, iov->iov_len);
164
1.18k
        dest += iov->iov_len;
165
1.18k
        ++iov;
166
1.18k
    }
167
1.17k
    v->nlen = dest - v->name;
168
1.17k
    *dest = 0;
169
170
4.71k
    while ((f = APR_BRIGADE_FIRST(bb)) != e)
171
3.54k
        apr_bucket_delete(f);
172
173
1.17k
    apreq_param_tainted_on(param);
174
1.17k
    *p = param;
175
1.17k
    return APR_SUCCESS;
176
177
1.17k
}
178
179
10.0k
#define IS_TOKEN_CHAR(c) (apr_isalnum(c) \
180
10.0k
                          || ((c) && strchr("!#$%&'*+-.^_`|~", (c))))
181
182
APREQ_DECLARE_PARSER(apreq_parse_headers)
183
856
{
184
856
    apr_pool_t *pool = parser->pool;
185
856
    apr_bucket *e;
186
856
    struct hdr_ctx *ctx;
187
856
    char ch;
188
189
856
    if (parser->ctx == NULL) {
190
856
        ctx = apr_pcalloc(pool, sizeof *ctx);
191
856
        ctx->bb = apr_brigade_create(pool, parser->bucket_alloc);
192
856
        parser->ctx = ctx;
193
856
        ctx->status = HDR_NAME;
194
856
    }
195
0
    else
196
0
        ctx = parser->ctx;
197
198
856
    PARSER_STATUS_CHECK(HDR);
199
856
    e = APR_BRIGADE_LAST(ctx->bb);
200
856
    APR_BRIGADE_CONCAT(ctx->bb, bb);
201
202
    /* parse the brigade for CRLF_CRLF-terminated header block,
203
     * each time starting from the front of the brigade.
204
     */
205
206
856
    for (e = APR_BUCKET_NEXT(e);
207
962
         e != APR_BRIGADE_SENTINEL(ctx->bb);
208
856
         e = APR_BUCKET_NEXT(e))
209
883
    {
210
883
        apr_size_t off = 0, dlen;
211
883
        const char *data;
212
883
        apr_status_t s;
213
883
        apreq_param_t *param = NULL; /* silences gcc-4.0 warning */
214
215
883
        if (APR_BUCKET_IS_EOS(e)) {
216
0
            ctx->status = HDR_COMPLETE;
217
0
            APR_BRIGADE_CONCAT(bb, ctx->bb);
218
0
            return APR_SUCCESS;
219
0
        }
220
221
883
        s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ);
222
883
        if ( s != APR_SUCCESS ) {
223
0
            ctx->status = HDR_ERROR;
224
0
            return s;
225
0
        }
226
883
        if (dlen == 0)
227
0
            continue;
228
229
5.99k
    parse_hdr_bucket:
230
231
        /*              gap           nlen = 13
232
         *              vvv           glen =  3
233
         * Sample-Header:  grape      vlen =  5
234
         * ^^^^^^^^^^^^^   ^^^^^
235
         *     name        value
236
         */
237
238
5.99k
        switch (ctx->status) {
239
240
1.29k
        case HDR_NAME:
241
242
10.8k
            while (off < dlen) {
243
10.8k
                ch = data[off++];
244
10.8k
                switch (ch) {
245
1.24k
                case ':':
246
1.24k
                    if (!ctx->nlen) {
247
4
                        ctx->status = HDR_ERROR;
248
4
                        return APREQ_ERROR_BADHEADER;
249
4
                    }
250
1.23k
                    e = split_header_line(e, &off, &data, &dlen);
251
1.23k
                    ++ctx->glen;
252
1.23k
                    ctx->status = HDR_GAP;
253
1.23k
                    goto parse_hdr_bucket;
254
255
9.60k
                default:
256
9.60k
                    if (!IS_TOKEN_CHAR(ch)) {
257
18
                        ctx->status = HDR_ERROR;
258
18
                        return APREQ_ERROR_BADCHAR;
259
18
                    }
260
9.58k
                    ++ctx->nlen;
261
10.8k
                }
262
10.8k
            }
263
32
            break;
264
265
266
1.24k
        case HDR_GAP:
267
268
1.75k
            while (off < dlen) {
269
1.73k
                ch = data[off++];
270
1.73k
                switch (ch) {
271
205
                case ' ':
272
508
                case '\t':
273
508
                    ++ctx->glen;
274
508
                    break;
275
276
317
                case '\n':
277
317
                    e = split_header_line(e, &off, &data, &dlen);
278
317
                    ctx->status = HDR_NEWLINE;
279
317
                    goto parse_hdr_bucket;
280
281
109
                case '\r':
282
109
                    e = split_header_line(e, &off, &data, &dlen);
283
109
                    ctx->status = HDR_ENDLINE;
284
109
                    goto parse_hdr_bucket;
285
286
803
                default:
287
803
                    if (apr_iscntrl(ch)) {
288
1
                        ctx->status = HDR_ERROR;
289
1
                        return APREQ_ERROR_BADCHAR;
290
1
                    }
291
802
                    e = split_header_line(e, &off, &data, &dlen);
292
802
                    ++ctx->vlen;
293
802
                    ctx->status = HDR_VALUE;
294
802
                    goto parse_hdr_bucket;
295
1.73k
                }
296
1.73k
            }
297
20
            break;
298
299
300
970
        case HDR_VALUE:
301
302
9.83k
            while (off < dlen) {
303
9.81k
                ch = data[off++];
304
9.81k
                switch (ch) {
305
763
                case '\n':
306
763
                    ctx->status = HDR_NEWLINE;
307
763
                    goto parse_hdr_bucket;
308
309
184
                case '\r':
310
184
                    ctx->status = HDR_ENDLINE;
311
184
                    goto parse_hdr_bucket;
312
313
8.86k
                default:
314
8.86k
                    if (apr_iscntrl(ch)) {
315
2
                        ctx->status = HDR_ERROR;
316
2
                        return APREQ_ERROR_BADCHAR;
317
2
                    }
318
8.86k
                    ++ctx->vlen;
319
9.81k
                }
320
9.81k
            }
321
21
            break;
322
323
324
354
        case HDR_ENDLINE:
325
493
        case HDR_LASTLINE:
326
327
493
            if (off == dlen)
328
9
                break;
329
330
484
            if (data[off++] != '\n') {
331
11
                ctx->status = HDR_ERROR;
332
11
                return APREQ_ERROR_BADHEADER;
333
11
            }
334
335
473
            if (ctx->status == HDR_LASTLINE) {
336
131
                ctx->status = HDR_COMPLETE;
337
131
                goto parse_hdr_bucket;
338
131
            }
339
340
            /* fall thru */
341
342
            ctx->status = HDR_NEWLINE;
342
343
1.60k
        case HDR_NEWLINE:
344
345
1.60k
            if (off == dlen)
346
10
                break;
347
348
1.59k
            ch = data[off++];
349
1.59k
            switch (ch) {
350
225
            case ' ':
351
424
            case '\t':
352
424
                ++ctx->vlen;
353
424
                break;
354
355
1.17k
            default:
356
1.17k
                e = split_header_line(e, &off, &data, &dlen);
357
358
                /* consume from splitted brigade now */
359
1.17k
                s = consume_header_line(&param, pool, ctx->bb,
360
1.17k
                                        ctx->nlen, ctx->glen, ctx->vlen);
361
1.17k
                if (parser->hook != NULL && s == APR_SUCCESS)
362
0
                    s = apreq_hook_run(parser->hook, param, NULL);
363
1.17k
                if (s != APR_SUCCESS) {
364
0
                    ctx->status = HDR_ERROR;
365
0
                    return s;
366
0
                }
367
1.17k
                apreq_value_table_add(&param->v, t);
368
1.17k
                ctx->nlen = 0;
369
1.17k
                ctx->vlen = 0;
370
1.17k
                ctx->glen = 0;
371
372
1.17k
                switch (ch) {
373
595
                case '\n':
374
595
                    ctx->status = HDR_COMPLETE;
375
595
                    break;
376
377
138
                case '\r':
378
138
                    ctx->status = HDR_LASTLINE;
379
138
                    break;
380
381
442
                default:
382
442
                    if (!IS_TOKEN_CHAR(ch)) {
383
11
                        ctx->status = HDR_ERROR;
384
11
                        return APREQ_ERROR_BADCHAR;
385
11
                    }
386
431
                    ++ctx->nlen;
387
431
                    ctx->status = HDR_NAME;
388
431
                    break;
389
1.17k
                }
390
1.16k
                goto parse_hdr_bucket;
391
1.59k
            }
392
393
            /* fall thru */
394
424
            ctx->status = HDR_FOLDLINE;
395
396
427
        case HDR_FOLDLINE:
397
398
1.01k
            while (off < dlen) {
399
1.00k
                ch = data[off++];
400
1.00k
                switch (ch) {
401
286
                case ' ':
402
591
                case '\t':
403
591
                    ++ctx->vlen;
404
591
                    break;
405
406
185
                case '\n':
407
185
                    ctx->status = HDR_NEWLINE;
408
185
                    goto parse_hdr_bucket;
409
410
59
                case '\r':
411
59
                    ctx->status = HDR_ENDLINE;
412
59
                    goto parse_hdr_bucket;
413
414
169
                default:
415
169
                    if (apr_iscntrl(ch)) {
416
4
                        ctx->status = HDR_ERROR;
417
4
                        return APREQ_ERROR_BADCHAR;
418
4
                    }
419
165
                    ctx->status = HDR_VALUE;
420
165
                    ++ctx->vlen;
421
165
                    goto parse_hdr_bucket;
422
1.00k
                }
423
1.00k
            }
424
14
            break;
425
426
427
726
        case HDR_COMPLETE:
428
429
726
            if (off < dlen)
430
720
                apr_bucket_split(e, off);
431
432
726
            e = APR_BUCKET_NEXT(e);
433
727
            do {
434
727
                apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb);
435
727
                apr_bucket_delete(f);
436
727
            } while (e != APR_BRIGADE_FIRST(ctx->bb));
437
438
726
            APR_BRIGADE_CONCAT(bb, ctx->bb);
439
726
            return APR_SUCCESS;
440
441
442
0
        default:
443
0
            assert(0); /* not reached */
444
5.99k
        }
445
5.99k
    }
446
79
    apreq_brigade_setaside(ctx->bb,pool);
447
79
    return APR_INCOMPLETE;
448
856
}