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