Coverage Report

Created: 2026-08-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/app-layer-htp-file.c
Line
Count
Source
1
/* Copyright (C) 2007-2021 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * This file provides HTTP protocol file handling support for the engine
24
 * using the HTP library.
25
 */
26
27
#include "suricata-common.h"
28
#include "app-layer-htp-file.h"
29
#include "app-layer-htp-range.h"
30
#include "app-layer-events.h"
31
#include "util-validate.h"
32
33
extern StreamingBufferConfig htp_sbcfg;
34
35
/**
36
 *  \brief Open the file with "filename" and pass the first chunk
37
 *         of data if any.
38
 *
39
 *  \param s http state
40
 *  \param filename name of the file
41
 *  \param filename_len length of the name
42
 *  \param data data chunk (if any)
43
 *  \param data_len length of the data portion
44
 *  \param direction flow direction
45
 *
46
 *  \retval  0 ok
47
 *  \retval -1 error
48
 *  \retval -2 not handling files on this flow
49
 */
50
int HTPFileOpen(HtpState *s, HtpTxUserData *tx, const uint8_t *filename, uint16_t filename_len,
51
        const uint8_t *data, uint32_t data_len, uint64_t txid, uint8_t direction)
52
384k
{
53
384k
    int retval = 0;
54
384k
    uint16_t flags = 0;
55
384k
    FileContainer *files = NULL;
56
57
384k
    SCLogDebug("data %p data_len %"PRIu32, data, data_len);
58
59
384k
    if (direction & STREAM_TOCLIENT) {
60
350k
        files = &tx->files_tc;
61
350k
        flags = FileFlowFlagsToFlags(tx->tx_data.file_flags, STREAM_TOCLIENT);
62
63
        // we shall not open a new file if there is a current one
64
350k
        DEBUG_VALIDATE_BUG_ON(tx->file_range != NULL);
65
350k
    } else {
66
33.7k
        files = &tx->files_ts;
67
33.7k
        flags = FileFlowFlagsToFlags(tx->tx_data.file_flags, STREAM_TOSERVER);
68
33.7k
    }
69
70
384k
    if (FileOpenFileWithId(files, &htp_sbcfg, s->file_track_id++, filename, filename_len, data,
71
384k
                data_len, flags) != 0) {
72
0
        retval = -1;
73
384k
    } else {
74
384k
        const HTPCfgDir *cfg;
75
384k
        if (direction & STREAM_TOCLIENT) {
76
350k
            cfg = &s->cfg->response;
77
350k
        } else {
78
33.7k
            cfg = &s->cfg->request;
79
33.7k
        }
80
384k
        FileSetInspectSizes(files->tail, cfg->inspect_window, cfg->inspect_min_size);
81
384k
    }
82
83
384k
    tx->tx_data.files_opened++;
84
85
384k
    SCReturnInt(retval);
86
384k
}
87
88
/**
89
 * Performs parsing of the content-range value
90
 *
91
 * @param[in] rawvalue
92
 * @param[out] range
93
 *
94
 * @return HTP_OK on success, HTP_ERROR on failure.
95
 */
96
int HTPParseContentRange(bstr *rawvalue, HTTPContentRange *range)
97
41.9k
{
98
41.9k
    uint32_t len = bstr_len(rawvalue);
99
41.9k
    return rs_http_parse_content_range(range, bstr_ptr(rawvalue), len);
100
41.9k
}
101
102
/**
103
 * Performs parsing + checking of the content-range value
104
 *
105
 * @param[in] rawvalue
106
 * @param[out] range
107
 *
108
 * @return HTP_OK on success, HTP_ERROR, -2, -3 on failure.
109
 */
110
static int HTPParseAndCheckContentRange(
111
        bstr *rawvalue, HTTPContentRange *range, HtpState *s, HtpTxUserData *htud)
112
60.3k
{
113
60.3k
    int r = HTPParseContentRange(rawvalue, range);
114
60.3k
    if (r != 0) {
115
6.63k
        AppLayerDecoderEventsSetEventRaw(&htud->tx_data.events, HTTP_DECODER_EVENT_RANGE_INVALID);
116
6.63k
        s->events++;
117
6.63k
        SCLogDebug("parsing range failed, going back to normal file");
118
6.63k
        return r;
119
6.63k
    }
120
    /* crparsed.end <= 0 means a range with only size
121
     * this is the answer to an unsatisfied range with the whole file
122
     * crparsed.size <= 0 means an unknown size, so we do not know
123
     * when to close it...
124
     */
125
53.7k
    if (range->end <= 0 || range->size <= 0) {
126
782
        SCLogDebug("range without all information");
127
782
        return -2;
128
52.9k
    } else if (range->end == range->size - 1 && range->start == 0) {
129
878
        SCLogDebug("range without all information");
130
878
        return -3;
131
52.0k
    } else if (range->start > range->end || range->end > range->size - 1) {
132
1.79k
        AppLayerDecoderEventsSetEventRaw(&htud->tx_data.events, HTTP_DECODER_EVENT_RANGE_INVALID);
133
1.79k
        s->events++;
134
1.79k
        SCLogDebug("invalid range");
135
1.79k
        return -4;
136
1.79k
    }
137
50.2k
    return r;
138
53.7k
}
139
140
/**
141
 *  \brief Sets range for a file
142
 *
143
 *  \param s http state
144
 *  \param rawvalue raw header value
145
 *
146
 *  \retval 0 ok
147
 *  \retval -1 error
148
 */
149
int HTPFileOpenWithRange(HtpState *s, HtpTxUserData *txud, const uint8_t *filename,
150
        uint16_t filename_len, const uint8_t *data, uint32_t data_len, uint64_t txid,
151
        bstr *rawvalue, HtpTxUserData *htud)
152
11.6k
{
153
11.6k
    SCEnter();
154
11.6k
    uint16_t flags;
155
156
11.6k
    DEBUG_VALIDATE_BUG_ON(s == NULL);
157
158
    // This function is only called STREAM_TOCLIENT from HtpResponseBodyHandle
159
11.6k
    HTTPContentRange crparsed;
160
11.6k
    if (HTPParseAndCheckContentRange(rawvalue, &crparsed, s, htud) != 0) {
161
        // range is invalid, fall back to classic open
162
412
        return HTPFileOpen(s, txud, filename, filename_len, data, data_len, txid, STREAM_TOCLIENT);
163
412
    }
164
11.2k
    flags = FileFlowToFlags(s->f, STREAM_TOCLIENT);
165
11.2k
    FileContainer *files = &txud->files_tc;
166
167
    // we open a file for this specific range
168
11.2k
    if (FileOpenFileWithId(files, &htp_sbcfg, s->file_track_id++, filename, filename_len, data,
169
11.2k
                data_len, flags) != 0) {
170
0
        SCReturnInt(-1);
171
11.2k
    } else {
172
11.2k
        const HTPCfgDir *cfg = &s->cfg->response;
173
11.2k
        FileSetInspectSizes(files->tail, cfg->inspect_window, cfg->inspect_min_size);
174
11.2k
    }
175
11.2k
    txud->tx_data.files_opened++;
176
177
11.2k
    if (FileSetRange(files, crparsed.start, crparsed.end) < 0) {
178
0
        SCLogDebug("set range failed");
179
0
    }
180
181
    // Then, we will try to handle reassembly of different ranges of the same file
182
    // TODO have the caller pass directly the tx
183
11.2k
    htp_tx_t *tx = htp_list_get(s->conn->transactions, txid - s->tx_freed);
184
11.2k
    if (!tx) {
185
0
        SCReturnInt(-1);
186
0
    }
187
11.2k
    uint8_t *keyurl;
188
11.2k
    uint32_t keylen;
189
11.2k
    if (tx->request_hostname != NULL) {
190
8.57k
        keylen = bstr_len(tx->request_hostname) + filename_len;
191
8.57k
        keyurl = SCMalloc(keylen);
192
8.57k
        if (keyurl == NULL) {
193
0
            SCReturnInt(-1);
194
0
        }
195
8.57k
        memcpy(keyurl, bstr_ptr(tx->request_hostname), bstr_len(tx->request_hostname));
196
8.57k
        memcpy(keyurl + bstr_len(tx->request_hostname), filename, filename_len);
197
8.57k
    } else {
198
        // do not reassemble file without host info
199
2.62k
        SCReturnInt(0);
200
2.62k
    }
201
8.57k
    DEBUG_VALIDATE_BUG_ON(htud->file_range);
202
8.57k
    htud->file_range = HttpRangeContainerOpenFile(keyurl, keylen, s->f, &crparsed, &htp_sbcfg,
203
8.57k
            filename, filename_len, flags, data, data_len);
204
8.57k
    SCFree(keyurl);
205
8.57k
    if (htud->file_range == NULL) {
206
0
        SCReturnInt(-1);
207
0
    }
208
8.57k
    SCReturnInt(0);
209
8.57k
}
210
211
/**
212
 *  \brief Store a chunk of data in the flow
213
 *
214
 *  \param s HtpState
215
 *  \param tx HtpTxUserData
216
 *  \param data data chunk (if any)
217
 *  \param data_len length of the data portion
218
 *  \param direction flow direction
219
 *
220
 *  \retval 0 ok
221
 *  \retval -1 error
222
 *  \retval -2 file doesn't need storing
223
 */
224
int HTPFileStoreChunk(
225
        HtpState *s, HtpTxUserData *tx, const uint8_t *data, uint32_t data_len, uint8_t direction)
226
2.92M
{
227
2.92M
    SCEnter();
228
229
2.92M
    int retval = 0;
230
2.92M
    int result = 0;
231
2.92M
    FileContainer *files = NULL;
232
233
2.92M
    if (direction & STREAM_TOCLIENT) {
234
2.76M
        files = &tx->files_tc;
235
2.76M
    } else {
236
164k
        files = &tx->files_ts;
237
164k
    }
238
2.92M
    SCLogDebug("files %p data %p data_len %" PRIu32, files, data, data_len);
239
240
2.92M
    if (files == NULL) {
241
0
        SCLogDebug("no files in state");
242
0
        retval = -1;
243
0
        goto end;
244
0
    }
245
246
2.92M
    if (tx->file_range != NULL) {
247
195k
        if (HttpRangeAppendData(&htp_sbcfg, tx->file_range, data, data_len) < 0) {
248
0
            SCLogDebug("Failed to append data");
249
0
        }
250
195k
    }
251
252
2.92M
    result = FileAppendData(files, &htp_sbcfg, data, data_len);
253
2.92M
    if (result == -1) {
254
674k
        SCLogDebug("appending data failed");
255
674k
        retval = -1;
256
2.25M
    } else if (result == -2) {
257
0
        retval = -2;
258
0
    }
259
2.92M
    SCLogDebug("result %u", result);
260
261
2.92M
end:
262
2.92M
    SCReturnInt(retval);
263
2.92M
}
264
265
/** \brief close range, add reassembled file if possible
266
 *  \retval true if reassembled file was added
267
 *  \retval false if no reassembled file was added
268
 */
269
bool HTPFileCloseHandleRange(const StreamingBufferConfig *sbcfg, FileContainer *files,
270
        const uint16_t flags, HttpRangeContainerBlock *c, const uint8_t *data, uint32_t data_len)
271
26.4k
{
272
26.4k
    bool added = false;
273
26.4k
    if (HttpRangeAppendData(sbcfg, c, data, data_len) < 0) {
274
0
        SCLogDebug("Failed to append data");
275
0
    }
276
26.4k
    if (c->container) {
277
        // we only call HttpRangeClose if we may some new data
278
        // ie we do not call it if we skipped all this range request
279
19.5k
        THashDataLock(c->container->hdata);
280
19.5k
        if (c->container->error) {
281
0
            SCLogDebug("range in ERROR state");
282
0
        }
283
19.5k
        File *ranged = HttpRangeClose(sbcfg, c, flags);
284
19.5k
        if (ranged && files) {
285
            /* HtpState owns the constructed file now */
286
268
            FileContainerAdd(files, ranged);
287
268
            added = true;
288
268
        }
289
19.5k
        DEBUG_VALIDATE_BUG_ON(ranged && !files);
290
19.5k
        THashDataUnlock(c->container->hdata);
291
19.5k
    }
292
26.4k
    return added;
293
26.4k
}
294
295
/**
296
 *  \brief Close the file in the flow
297
 *
298
 *  \param tx HtpTxUserData
299
 *  \param data data chunk if any
300
 *  \param data_len length of the data portion
301
 *  \param flags flags to indicate events
302
 *  \param direction flow direction
303
 *
304
 *  Currently on the FLOW_FILE_TRUNCATED flag is implemented, indicating
305
 *  that the file isn't complete but we're stopping storing it.
306
 *
307
 *  \retval 0 ok
308
 *  \retval -1 error
309
 *  \retval -2 not storing files on this flow/tx
310
 */
311
int HTPFileClose(HtpState *s, HtpTxUserData *tx, const uint8_t *data, uint32_t data_len,
312
        uint8_t flags, uint8_t direction)
313
428k
{
314
428k
    SCEnter();
315
316
428k
    SCLogDebug("flags %04x FILE_TRUNCATED %s", flags, (flags & FILE_TRUNCATED) ? "true" : "false");
317
318
428k
    int retval = 0;
319
428k
    int result = 0;
320
428k
    FileContainer *files = NULL;
321
322
428k
    if (direction & STREAM_TOCLIENT) {
323
394k
        files = &tx->files_tc;
324
394k
    } else {
325
34.3k
        files = &tx->files_ts;
326
34.3k
    }
327
328
428k
    SCLogDebug("files %p data %p data_len %" PRIu32, files, data, data_len);
329
330
428k
    if (files == NULL) {
331
0
        retval = -1;
332
0
        goto end;
333
0
    }
334
335
428k
    result = FileCloseFile(files, &htp_sbcfg, data, data_len, flags);
336
428k
    if (result == -1) {
337
2.73k
        retval = -1;
338
425k
    } else if (result == -2) {
339
0
        retval = -2;
340
0
    }
341
428k
    SCLogDebug("result %u", result);
342
343
428k
    if (tx->file_range != NULL) {
344
37.2k
        bool added =
345
37.2k
                HTPFileCloseHandleRange(&htp_sbcfg, files, flags, tx->file_range, data, data_len);
346
37.2k
        if (added) {
347
12
            tx->tx_data.files_opened++;
348
12
        }
349
37.2k
        HttpRangeFreeBlock(tx->file_range);
350
37.2k
        tx->file_range = NULL;
351
37.2k
    }
352
353
428k
end:
354
428k
    SCReturnInt(retval);
355
428k
}
356
357
#ifdef UNITTESTS
358
#include "stream-tcp.h"
359
#include "app-layer-parser.h"
360
#include "util-unittest-helper.h"
361
362
static int HTPFileParserTest01(void)
363
{
364
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
365
                         "Host: www.server.lan\r\n"
366
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
367
                         "Content-Length: 215\r\n"
368
                         "\r\n"
369
                         "-----------------------------277531038314945\r\n"
370
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
371
                         "Content-Type: image/jpeg\r\n"
372
                         "\r\n";
373
374
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
375
    uint8_t httpbuf2[] = "filecontent\r\n"
376
                         "-----------------------------277531038314945--";
377
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
378
379
    TcpSession ssn;
380
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
381
    HtpState *http_state = NULL;
382
    memset(&ssn, 0, sizeof(ssn));
383
384
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
385
    FAIL_IF_NULL(f);
386
    f->protoctx = &ssn;
387
    f->proto = IPPROTO_TCP;
388
    f->alproto = ALPROTO_HTTP1;
389
390
    StreamTcpInitConfig(true);
391
392
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
393
    int r = AppLayerParserParse(
394
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
395
    FAIL_IF_NOT(r == 0);
396
397
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
398
    r = AppLayerParserParse(
399
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
400
    FAIL_IF_NOT(r == 0);
401
402
    http_state = f->alstate;
403
    FAIL_IF_NULL(http_state);
404
405
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
406
    FAIL_IF_NULL(tx);
407
    FAIL_IF_NULL(tx->request_method);
408
409
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
410
411
    AppLayerParserThreadCtxFree(alp_tctx);
412
    StreamTcpFreeConfig(true);
413
    UTHFreeFlow(f);
414
    PASS;
415
}
416
417
static int HTPFileParserTest02(void)
418
{
419
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
420
                         "Host: www.server.lan\r\n"
421
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
422
                         "Content-Length: 337\r\n"
423
                         "\r\n";
424
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
425
426
    uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
427
                         "Content-Disposition: form-data; name=\"email\"\r\n"
428
                         "\r\n"
429
                         "someaddress@somedomain.lan\r\n";
430
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
431
432
    uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
433
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
434
                         "Content-Type: image/jpeg\r\n"
435
                         "\r\n";
436
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
437
438
    uint8_t httpbuf4[] = "filecontent\r\n"
439
                         "-----------------------------277531038314945--";
440
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
441
442
    TcpSession ssn;
443
    HtpState *http_state = NULL;
444
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
445
446
    memset(&ssn, 0, sizeof(ssn));
447
448
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
449
    FAIL_IF_NULL(f);
450
    f->protoctx = &ssn;
451
    f->proto = IPPROTO_TCP;
452
    f->alproto = ALPROTO_HTTP1;
453
454
    StreamTcpInitConfig(true);
455
456
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
457
    int r = AppLayerParserParse(
458
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
459
    FAIL_IF_NOT(r == 0);
460
461
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
462
    r = AppLayerParserParse(
463
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
464
    FAIL_IF_NOT(r == 0);
465
466
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
467
    r = AppLayerParserParse(
468
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
469
    FAIL_IF_NOT(r == 0);
470
471
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
472
    r = AppLayerParserParse(
473
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
474
    FAIL_IF_NOT(r == 0);
475
476
    http_state = f->alstate;
477
    FAIL_IF_NULL(http_state);
478
479
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
480
    FAIL_IF_NULL(tx);
481
    FAIL_IF_NULL(tx->request_method);
482
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
483
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
484
    FAIL_IF_NULL(tx_ud);
485
    FAIL_IF_NULL(tx_ud->files_ts.tail);
486
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
487
488
    AppLayerParserThreadCtxFree(alp_tctx);
489
    StreamTcpFreeConfig(true);
490
    UTHFreeFlow(f);
491
    PASS;
492
}
493
494
static int HTPFileParserTest03(void)
495
{
496
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
497
                         "Host: www.server.lan\r\n"
498
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
499
                         "Content-Length: 337\r\n"
500
                         "\r\n";
501
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
502
503
    uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
504
                         "Content-Disposition: form-data; name=\"email\"\r\n"
505
                         "\r\n"
506
                         "someaddress@somedomain.lan\r\n";
507
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
508
509
    uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
510
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
511
                         "Content-Type: image/jpeg\r\n"
512
                         "\r\n";
513
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
514
515
    uint8_t httpbuf4[] = "file";
516
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
517
518
    uint8_t httpbuf5[] = "content\r\n";
519
    uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
520
521
    uint8_t httpbuf6[] = "-----------------------------277531038314945--";
522
    uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
523
524
    TcpSession ssn;
525
    HtpState *http_state = NULL;
526
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
527
528
    memset(&ssn, 0, sizeof(ssn));
529
530
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
531
    FAIL_IF_NULL(f);
532
    f->protoctx = &ssn;
533
    f->proto = IPPROTO_TCP;
534
    f->alproto = ALPROTO_HTTP1;
535
536
    StreamTcpInitConfig(true);
537
538
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
539
    int r = AppLayerParserParse(
540
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
541
    FAIL_IF_NOT(r == 0);
542
543
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
544
    r = AppLayerParserParse(
545
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
546
    FAIL_IF_NOT(r == 0);
547
548
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
549
    r = AppLayerParserParse(
550
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
551
    FAIL_IF_NOT(r == 0);
552
553
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
554
    r = AppLayerParserParse(
555
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
556
    FAIL_IF_NOT(r == 0);
557
558
    SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5);
559
    r = AppLayerParserParse(
560
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf5, httplen5);
561
    FAIL_IF_NOT(r == 0);
562
563
    SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6);
564
    r = AppLayerParserParse(
565
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf6, httplen6);
566
    FAIL_IF_NOT(r == 0);
567
568
    http_state = f->alstate;
569
    FAIL_IF_NULL(http_state);
570
571
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
572
    FAIL_IF_NULL(tx);
573
    FAIL_IF_NULL(tx->request_method);
574
575
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
576
577
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
578
    FAIL_IF_NULL(tx_ud);
579
    FAIL_IF_NULL(tx_ud->files_ts.head);
580
    FAIL_IF_NULL(tx_ud->files_ts.tail);
581
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
582
    FAIL_IF(FileDataSize(tx_ud->files_ts.head) != 11);
583
584
    AppLayerParserThreadCtxFree(alp_tctx);
585
    StreamTcpFreeConfig(true);
586
    UTHFreeFlow(f);
587
    PASS;
588
}
589
590
static int HTPFileParserTest04(void)
591
{
592
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
593
                         "Host: www.server.lan\r\n"
594
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
595
                         "Content-Length: 373\r\n"
596
                         "\r\n";
597
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
598
599
    uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
600
                         "Content-Disposition: form-data; name=\"email\"\r\n"
601
                         "\r\n"
602
                         "someaddress@somedomain.lan\r\n";
603
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
604
605
    uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
606
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
607
                         "Content-Type: image/jpeg\r\n"
608
                         "\r\n";
609
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
610
611
    uint8_t httpbuf4[] = "file0123456789abcdefghijklmnopqrstuvwxyz";
612
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
613
614
    uint8_t httpbuf5[] = "content\r\n";
615
    uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
616
617
    uint8_t httpbuf6[] = "-----------------------------277531038314945--";
618
    uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
619
620
    TcpSession ssn;
621
    HtpState *http_state = NULL;
622
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
623
624
    memset(&ssn, 0, sizeof(ssn));
625
626
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
627
    FAIL_IF_NULL(f);
628
    f->protoctx = &ssn;
629
    f->proto = IPPROTO_TCP;
630
    f->alproto = ALPROTO_HTTP1;
631
632
    StreamTcpInitConfig(true);
633
634
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
635
    int r = AppLayerParserParse(
636
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
637
    FAIL_IF_NOT(r == 0);
638
639
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
640
    r = AppLayerParserParse(
641
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
642
    FAIL_IF_NOT(r == 0);
643
644
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
645
    r = AppLayerParserParse(
646
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
647
    FAIL_IF_NOT(r == 0);
648
649
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
650
    r = AppLayerParserParse(
651
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
652
    FAIL_IF_NOT(r == 0);
653
654
    SCLogDebug("\n>>>> processing chunk 5 size %u <<<<\n", httplen5);
655
    r = AppLayerParserParse(
656
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf5, httplen5);
657
    FAIL_IF_NOT(r == 0);
658
659
    SCLogDebug("\n>>>> processing chunk 6 size %u <<<<\n", httplen6);
660
    r = AppLayerParserParse(
661
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf6, httplen6);
662
    FAIL_IF_NOT(r == 0);
663
664
    http_state = f->alstate;
665
    FAIL_IF_NULL(http_state);
666
667
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
668
    FAIL_IF_NULL(tx);
669
    FAIL_IF_NULL(tx->request_method);
670
671
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
672
673
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
674
    FAIL_IF_NULL(tx_ud);
675
    FAIL_IF_NULL(tx_ud->files_ts.head);
676
    FAIL_IF_NULL(tx_ud->files_ts.tail);
677
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
678
679
    AppLayerParserThreadCtxFree(alp_tctx);
680
    StreamTcpFreeConfig(true);
681
    UTHFreeFlow(f);
682
    PASS;
683
}
684
685
static int HTPFileParserTest05(void)
686
{
687
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
688
                         "Host: www.server.lan\r\n"
689
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
690
                         "Content-Length: 544\r\n"
691
                         "\r\n"
692
                         "-----------------------------277531038314945\r\n"
693
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
694
                         "Content-Type: image/jpeg\r\n"
695
                         "\r\n"
696
                         "filecontent\r\n"
697
                         "-----------------------------277531038314945\r\n";
698
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
699
    uint8_t httpbuf2[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
700
                         "Content-Type: image/jpeg\r\n"
701
                         "\r\n"
702
                         "FILECONTENT\r\n"
703
        "-----------------------------277531038314945--";
704
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
705
706
    TcpSession ssn;
707
    HtpState *http_state = NULL;
708
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
709
710
    memset(&ssn, 0, sizeof(ssn));
711
712
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
713
    FAIL_IF_NULL(f);
714
    f->protoctx = &ssn;
715
    f->proto = IPPROTO_TCP;
716
    f->alproto = ALPROTO_HTTP1;
717
718
    StreamTcpInitConfig(true);
719
720
    SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
721
    int r = AppLayerParserParse(
722
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
723
    FAIL_IF_NOT(r == 0);
724
725
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
726
    r = AppLayerParserParse(
727
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
728
    FAIL_IF_NOT(r == 0);
729
730
    http_state = f->alstate;
731
    FAIL_IF_NULL(http_state);
732
733
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
734
    FAIL_IF_NULL(tx);
735
    FAIL_IF_NULL(tx->request_method);
736
737
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
738
739
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
740
    FAIL_IF_NULL(tx_ud);
741
    FAIL_IF_NULL(tx_ud->files_ts.head);
742
    FAIL_IF_NULL(tx_ud->files_ts.tail);
743
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
744
745
    FAIL_IF(tx_ud->files_ts.head == tx_ud->files_ts.tail);
746
    FAIL_IF(tx_ud->files_ts.head->next != tx_ud->files_ts.tail);
747
748
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.head->sb, (uint8_t *)"filecontent", 11) !=
749
            1);
750
751
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) !=
752
            1);
753
    AppLayerParserThreadCtxFree(alp_tctx);
754
    StreamTcpFreeConfig(true);
755
    UTHFreeFlow(f);
756
    PASS;
757
}
758
759
/** \test first multipart part contains file but doesn't end in first chunk */
760
static int HTPFileParserTest06(void)
761
{
762
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
763
                         "Host: www.server.lan\r\n"
764
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
765
                         "Content-Length: 544\r\n"
766
                         "\r\n"
767
                         "-----------------------------277531038314945\r\n"
768
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
769
                         "Content-Type: image/jpeg\r\n"
770
                         "\r\n"
771
                         "filecontent\r\n"
772
                         "-----------------------------27753103831494";
773
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
774
    uint8_t httpbuf2[] = "5\r\nContent-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
775
                         "Content-Type: image/jpeg\r\n"
776
                         "\r\n"
777
                         "FILECONTENT\r\n"
778
        "-----------------------------277531038314945--";
779
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
780
781
    TcpSession ssn;
782
    HtpState *http_state = NULL;
783
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
784
785
    memset(&ssn, 0, sizeof(ssn));
786
787
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
788
    FAIL_IF_NULL(f);
789
    f->protoctx = &ssn;
790
    f->proto = IPPROTO_TCP;
791
    f->alproto = ALPROTO_HTTP1;
792
793
    StreamTcpInitConfig(true);
794
795
    SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
796
    int r = AppLayerParserParse(
797
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
798
    FAIL_IF_NOT(r == 0);
799
800
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
801
    r = AppLayerParserParse(
802
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
803
    FAIL_IF_NOT(r == 0);
804
805
    http_state = f->alstate;
806
    FAIL_IF_NULL(http_state);
807
808
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
809
    FAIL_IF_NULL(tx);
810
    FAIL_IF_NULL(tx->request_method);
811
812
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
813
814
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
815
    FAIL_IF_NULL(tx_ud);
816
    FAIL_IF_NULL(tx_ud->files_ts.head);
817
    FAIL_IF_NULL(tx_ud->files_ts.tail);
818
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
819
820
    FAIL_IF(tx_ud->files_ts.head == tx_ud->files_ts.tail);
821
    FAIL_IF(tx_ud->files_ts.head->next != tx_ud->files_ts.tail);
822
823
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.head->sb, (uint8_t *)"filecontent", 11) !=
824
            1);
825
826
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) !=
827
            1);
828
829
    AppLayerParserThreadCtxFree(alp_tctx);
830
    StreamTcpFreeConfig(true);
831
    UTHFreeFlow(f);
832
    PASS;
833
}
834
835
/** \test POST, but not multipart */
836
static int HTPFileParserTest07(void)
837
{
838
    uint8_t httpbuf1[] = "POST /filename HTTP/1.1\r\n"
839
                         "Host: www.server.lan\r\n"
840
                         "Content-Length: 11\r\n"
841
                         "\r\n";
842
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
843
    uint8_t httpbuf2[] = "FILECONTENT";
844
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
845
846
    TcpSession ssn;
847
    HtpState *http_state = NULL;
848
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
849
850
    memset(&ssn, 0, sizeof(ssn));
851
852
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
853
    FAIL_IF_NULL(f);
854
    f->protoctx = &ssn;
855
    f->proto = IPPROTO_TCP;
856
    f->alproto = ALPROTO_HTTP1;
857
858
    StreamTcpInitConfig(true);
859
860
    SCLogDebug("\n>>>> processing chunk 1 size %u <<<<\n", httplen1);
861
    int r = AppLayerParserParse(
862
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
863
    FAIL_IF_NOT(r == 0);
864
865
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
866
    r = AppLayerParserParse(
867
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
868
    FAIL_IF_NOT(r == 0);
869
870
    http_state = f->alstate;
871
    FAIL_IF_NULL(http_state);
872
873
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
874
    FAIL_IF_NULL(tx);
875
    FAIL_IF_NULL(tx->request_method);
876
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
877
878
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
879
    FAIL_IF_NULL(tx_ud);
880
    FAIL_IF_NULL(tx_ud->files_ts.head);
881
    FAIL_IF_NULL(tx_ud->files_ts.tail);
882
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
883
884
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) !=
885
            1);
886
887
    AppLayerParserThreadCtxFree(alp_tctx);
888
    StreamTcpFreeConfig(true);
889
    UTHFreeFlow(f);
890
    PASS;
891
}
892
893
static int HTPFileParserTest08(void)
894
{
895
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
896
                         "Host: www.server.lan\r\n"
897
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
898
                         "Content-Length: 215\r\n"
899
                         "\r\n"
900
                         "-----------------------------277531038314945\r\n"
901
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
902
                         "Content-Type: image/jpeg\r\n";
903
904
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
905
    uint8_t httpbuf2[] = "filecontent\r\n\r\n"
906
                         "-----------------------------277531038314945--";
907
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
908
909
    TcpSession ssn;
910
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
911
    HtpState *http_state = NULL;
912
    memset(&ssn, 0, sizeof(ssn));
913
914
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
915
    FAIL_IF_NULL(f);
916
    f->protoctx = &ssn;
917
    f->proto = IPPROTO_TCP;
918
    f->alproto = ALPROTO_HTTP1;
919
920
    StreamTcpInitConfig(true);
921
922
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
923
    int r = AppLayerParserParse(
924
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
925
    FAIL_IF_NOT(r == 0);
926
927
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
928
    r = AppLayerParserParse(
929
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
930
    FAIL_IF_NOT(r == 0);
931
932
    http_state = f->alstate;
933
    FAIL_IF_NULL(http_state);
934
935
    void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
936
    FAIL_IF_NULL(tx);
937
938
    AppLayerDecoderEvents *decoder_events =
939
            AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx);
940
    FAIL_IF_NULL(decoder_events);
941
942
    FAIL_IF(decoder_events->cnt != 2);
943
944
    AppLayerParserThreadCtxFree(alp_tctx);
945
    StreamTcpFreeConfig(true);
946
    UTHFreeFlow(f);
947
    PASS;
948
}
949
950
/** \test invalid header: Somereallylongheaderstr: has no value */
951
static int HTPFileParserTest09(void)
952
{
953
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
954
                         "Host: www.server.lan\r\n"
955
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
956
                         "Content-Length: 337\r\n"
957
                         "\r\n";
958
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
959
960
    uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
961
                         "Content-Disposition: form-data; name=\"email\"\r\n"
962
                         "\r\n"
963
                         "someaddress@somedomain.lan\r\n";
964
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
965
966
    uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
967
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
968
                         "Somereallylongheaderstr:\r\n"
969
                         "\r\n";
970
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
971
972
    uint8_t httpbuf4[] = "filecontent\r\n"
973
                         "-----------------------------277531038314945--";
974
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
975
976
    TcpSession ssn;
977
    HtpState *http_state = NULL;
978
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
979
980
    memset(&ssn, 0, sizeof(ssn));
981
982
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
983
    FAIL_IF_NULL(f);
984
    f->protoctx = &ssn;
985
    f->proto = IPPROTO_TCP;
986
    f->alproto = ALPROTO_HTTP1;
987
988
    StreamTcpInitConfig(true);
989
990
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
991
    int r = AppLayerParserParse(
992
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
993
    FAIL_IF_NOT(r == 0);
994
995
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
996
    r = AppLayerParserParse(
997
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
998
    FAIL_IF_NOT(r == 0);
999
1000
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
1001
    r = AppLayerParserParse(
1002
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
1003
    FAIL_IF_NOT(r == 0);
1004
1005
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
1006
    r = AppLayerParserParse(
1007
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
1008
    FAIL_IF_NOT(r == 0);
1009
1010
    http_state = f->alstate;
1011
    FAIL_IF_NULL(http_state);
1012
1013
    void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
1014
    FAIL_IF_NULL(tx);
1015
1016
    AppLayerDecoderEvents *decoder_events =
1017
            AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx);
1018
    FAIL_IF_NULL(decoder_events);
1019
1020
    FAIL_IF(decoder_events->cnt != 1);
1021
1022
    AppLayerParserThreadCtxFree(alp_tctx);
1023
    StreamTcpFreeConfig(true);
1024
    UTHFreeFlow(f);
1025
    PASS;
1026
}
1027
1028
/** \test empty entries */
1029
static int HTPFileParserTest10(void)
1030
{
1031
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
1032
                         "Host: www.server.lan\r\n"
1033
                         "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
1034
                         "Content-Length: 337\r\n"
1035
                         "\r\n";
1036
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1037
1038
    uint8_t httpbuf2[] = "-----------------------------277531038314945\r\n"
1039
                         "\r\n";
1040
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
1041
1042
    uint8_t httpbuf3[] = "-----------------------------277531038314945\r\n"
1043
                         "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
1044
                         "Somereallylongheaderstr: with a good value\r\n"
1045
                         "\r\n";
1046
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
1047
1048
    uint8_t httpbuf4[] = "filecontent\r\n"
1049
                         "-----------------------------277531038314945--";
1050
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
1051
1052
    TcpSession ssn;
1053
    HtpState *http_state = NULL;
1054
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1055
1056
    memset(&ssn, 0, sizeof(ssn));
1057
1058
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
1059
    FAIL_IF_NULL(f);
1060
    f->protoctx = &ssn;
1061
    f->proto = IPPROTO_TCP;
1062
    f->alproto = ALPROTO_HTTP1;
1063
1064
    StreamTcpInitConfig(true);
1065
1066
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
1067
    int r = AppLayerParserParse(
1068
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
1069
    FAIL_IF_NOT(r == 0);
1070
1071
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
1072
    r = AppLayerParserParse(
1073
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
1074
    FAIL_IF_NOT(r == 0);
1075
1076
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
1077
    r = AppLayerParserParse(
1078
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
1079
    FAIL_IF_NOT(r == 0);
1080
1081
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
1082
    r = AppLayerParserParse(
1083
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
1084
    FAIL_IF_NOT(r == 0);
1085
1086
    http_state = f->alstate;
1087
    FAIL_IF_NULL(http_state);
1088
1089
    void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
1090
    FAIL_IF_NULL(tx);
1091
    AppLayerDecoderEvents *decoder_events =
1092
            AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, tx);
1093
    FAIL_IF_NOT_NULL(decoder_events);
1094
1095
    AppLayerParserThreadCtxFree(alp_tctx);
1096
    StreamTcpFreeConfig(true);
1097
    UTHFreeFlow(f);
1098
    PASS;
1099
}
1100
1101
/** \test filedata cut in two pieces */
1102
static int HTPFileParserTest11(void)
1103
{
1104
    uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
1105
                         "Host: www.server.lan\r\n"
1106
                         "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1107
                         "Content-Length: 1102\r\n"
1108
                         "\r\n";
1109
    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1110
1111
    uint8_t httpbuf2[] = "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n";
1112
    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
1113
1114
    uint8_t httpbuf3[] = "Content-Disposition: form-data; name=\"PROGRESS_URL\"\r\n"
1115
                         "\r\n"
1116
                         "http://somserver.com/progress.php?UPLOAD_IDENTIFIER=XXXXXXXXX.XXXXXXXXXX.XXXXXXXX.XX.X\r\n"
1117
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1118
                         "Content-Disposition: form-data; name=\"DESTINATION_DIR\"\r\n"
1119
                         "\r\n"
1120
                         "10\r\n"
1121
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1122
                         "Content-Disposition: form-data; name=\"js_enabled\"\r\n"
1123
                         "\r\n"
1124
                         "1"
1125
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1126
                         "Content-Disposition: form-data; name=\"signature\"\r\n"
1127
                         "\r\n"
1128
                         "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n"
1129
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1130
                         "Content-Disposition: form-data; name=\"upload_files\"\r\n"
1131
                         "\r\n"
1132
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1133
                         "Content-Disposition: form-data; name=\"terms\"\r\n"
1134
                         "\r\n"
1135
                         "1"
1136
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1137
                         "Content-Disposition: form-data; name=\"file[]\"\r\n"
1138
                         "\r\n"
1139
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1140
                         "Content-Disposition: form-data; name=\"description[]\"\r\n"
1141
                         "\r\n"
1142
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo\r\n"
1143
                         "Content-Disposition: form-data; name=\"upload_file[]\"; filename=\"filename.doc\"\r\n"
1144
                         "Content-Type: application/msword\r\n"
1145
                         "\r\n"
1146
                         "FILE";
1147
    uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
1148
1149
    uint8_t httpbuf4[] = "CONTENT\r\n"
1150
                         "------WebKitFormBoundaryBRDbP74mBhBxsIdo--";
1151
    uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
1152
1153
    TcpSession ssn;
1154
    HtpState *http_state = NULL;
1155
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
1156
1157
    memset(&ssn, 0, sizeof(ssn));
1158
1159
    Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
1160
    FAIL_IF_NULL(f);
1161
    f->protoctx = &ssn;
1162
    f->proto = IPPROTO_TCP;
1163
    f->alproto = ALPROTO_HTTP1;
1164
1165
    StreamTcpInitConfig(true);
1166
1167
    SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
1168
    int r = AppLayerParserParse(
1169
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
1170
    FAIL_IF_NOT(r == 0);
1171
1172
    SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
1173
    r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
1174
    FAIL_IF_NOT(r == 0);
1175
1176
    SCLogDebug("\n>>>> processing chunk 3 size %u <<<<\n", httplen3);
1177
    r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
1178
    FAIL_IF_NOT(r == 0);
1179
1180
    SCLogDebug("\n>>>> processing chunk 4 size %u <<<<\n", httplen4);
1181
    r = AppLayerParserParse(
1182
            NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
1183
    FAIL_IF_NOT(r == 0);
1184
1185
    http_state = f->alstate;
1186
    FAIL_IF_NULL(http_state);
1187
1188
    void *txtmp = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
1189
    FAIL_IF_NULL(txtmp);
1190
1191
    AppLayerDecoderEvents *decoder_events =
1192
            AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP1, txtmp);
1193
    FAIL_IF_NOT_NULL(decoder_events);
1194
1195
    htp_tx_t *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, http_state, 0);
1196
    FAIL_IF_NULL(tx);
1197
    FAIL_IF_NULL(tx->request_method);
1198
1199
    FAIL_IF(memcmp(bstr_util_strdup_to_c(tx->request_method), "POST", 4) != 0);
1200
1201
    HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
1202
    FAIL_IF_NULL(tx_ud);
1203
    FAIL_IF_NULL(tx_ud->files_ts.head);
1204
    FAIL_IF_NULL(tx_ud->files_ts.tail);
1205
    FAIL_IF(tx_ud->files_ts.tail->state != FILE_STATE_CLOSED);
1206
1207
    FAIL_IF(StreamingBufferCompareRawData(tx_ud->files_ts.tail->sb, (uint8_t *)"FILECONTENT", 11) !=
1208
            1);
1209
1210
    AppLayerParserThreadCtxFree(alp_tctx);
1211
    StreamTcpFreeConfig(true);
1212
    UTHFreeFlow(f);
1213
    PASS;
1214
}
1215
1216
void AppLayerHtpFileRegisterTests (void);
1217
#include "tests/app-layer-htp-file.c"
1218
#endif /* UNITTESTS */
1219
1220
void HTPFileParserRegisterTests(void)
1221
0
{
1222
#ifdef UNITTESTS
1223
    UtRegisterTest("HTPFileParserTest01", HTPFileParserTest01);
1224
    UtRegisterTest("HTPFileParserTest02", HTPFileParserTest02);
1225
    UtRegisterTest("HTPFileParserTest03", HTPFileParserTest03);
1226
    UtRegisterTest("HTPFileParserTest04", HTPFileParserTest04);
1227
    UtRegisterTest("HTPFileParserTest05", HTPFileParserTest05);
1228
    UtRegisterTest("HTPFileParserTest06", HTPFileParserTest06);
1229
    UtRegisterTest("HTPFileParserTest07", HTPFileParserTest07);
1230
    UtRegisterTest("HTPFileParserTest08", HTPFileParserTest08);
1231
    UtRegisterTest("HTPFileParserTest09", HTPFileParserTest09);
1232
    UtRegisterTest("HTPFileParserTest10", HTPFileParserTest10);
1233
    UtRegisterTest("HTPFileParserTest11", HTPFileParserTest11);
1234
    AppLayerHtpFileRegisterTests();
1235
#endif /* UNITTESTS */
1236
0
}