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