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