suricata
app-layer-ftp.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2026 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 Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
22  * \author Eric Leblond <eric@regit.org>
23  * \author Jeff Lucovsky <jlucovsky@oisf.net>
24  *
25  * App Layer Parser for FTP
26  */
27 
28 #include "suricata-common.h"
29 
30 #include "app-layer-ftp.h"
31 #include "app-layer-parser.h"
32 #include "app-layer-expectation.h"
33 #include "app-layer-detect-proto.h"
34 #include "app-layer-events.h"
35 
36 #include "rust.h"
37 
38 #include "util-mpm.h"
39 #include "util-memcmp.h"
40 #include "util-validate.h"
41 #include "app-layer-protos.h"
42 #include "flow-storage.h"
43 #include "flow.h"
44 #include "util-debug.h"
45 #include "util-error.h"
46 #include "util-prefilter.h"
47 #include "util-streaming-buffer.h"
48 
49 typedef struct FTPThreadCtx_ {
53 
54 #define FTP_MPM mpm_default_matcher
55 
56 static MpmCtx *ftp_mpm_ctx = NULL;
57 
58 uint64_t ftp_config_memcap = 64 * 1024 * 1024;
59 uint32_t ftp_config_maxtx = 1024;
60 uint32_t ftp_max_line_len = 4096;
61 
62 /* Preliminary (1xx) responses kept per transaction. A preliminary response
63  * keeps the transaction open, so without a limit a server can grow the
64  * transaction's response list until the flow ends or reaches the stream
65  * reassembly depth. */
66 static uint32_t ftp_max_preliminary_responses = 16;
67 
68 SC_ATOMIC_DECLARE(uint64_t, ftp_memuse);
69 SC_ATOMIC_DECLARE(uint64_t, ftp_memcap);
70 
71 static FTPTransaction *FTPGetOldestTx(const FtpState *, FTPTransaction *);
72 
73 static void FTPParseMemcap(void)
74 {
75  SCFTPGetConfigValues(&ftp_config_memcap, &ftp_config_maxtx, &ftp_max_line_len,
76  &ftp_max_preliminary_responses);
77 
78  SC_ATOMIC_INIT(ftp_memuse);
79  SC_ATOMIC_INIT(ftp_memcap);
80 }
81 
82 static void FTPIncrMemuse(uint64_t size)
83 {
84  (void)SC_ATOMIC_ADD(ftp_memuse, size);
85 }
86 
87 static void FTPDecrMemuse(uint64_t size)
88 {
89  (void)SC_ATOMIC_SUB(ftp_memuse, size);
90 }
91 
92 uint64_t FTPMemuseGlobalCounter(void)
93 {
94  uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse);
95  return tmpval;
96 }
97 
98 uint64_t FTPMemcapGlobalCounter(void)
99 {
100  uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap);
101  return tmpval;
102 }
103 
104 int FTPSetMemcap(uint64_t size)
105 {
106  if ((uint64_t)SC_ATOMIC_GET(ftp_memcap) < size) {
107  SC_ATOMIC_SET(ftp_memcap, size);
108  return 1;
109  }
110 
111  return 0;
112 }
113 
114 /**
115  * \brief Check if alloc'ing "size" would mean we're over memcap
116  *
117  * \retval 1 if in bounds
118  * \retval 0 if not in bounds
119  */
120 static int FTPCheckMemcap(uint64_t size)
121 {
122  if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap)
123  return 1;
124  (void) SC_ATOMIC_ADD(ftp_memcap, 1);
125  return 0;
126 }
127 
128 static void *FTPCalloc(size_t n, size_t size)
129 {
130  if (FTPCheckMemcap((uint32_t)(n * size)) == 0) {
132  return NULL;
133  }
134 
135  void *ptr = SCCalloc(n, size);
136 
137  if (unlikely(ptr == NULL)) {
139  return NULL;
140  }
141 
142  FTPIncrMemuse((uint64_t)(n * size));
143  return ptr;
144 }
145 
146 static void *FTPRealloc(void *ptr, size_t orig_size, size_t size)
147 {
148  /* a smaller size needs no memcap check, and size - orig_size would wrap */
149  if (size > orig_size && FTPCheckMemcap(size - orig_size) == 0) {
151  return NULL;
152  }
153 
154  void *rptr = SCRealloc(ptr, size);
155  if (rptr == NULL) {
157  return NULL;
158  }
159 
160  if (size > orig_size) {
161  FTPIncrMemuse(size - orig_size);
162  } else {
163  FTPDecrMemuse(orig_size - size);
164  }
165 
166  return rptr;
167 }
168 
169 static void FTPFree(void *ptr, size_t size)
170 {
171  SCFree(ptr);
172 
173  FTPDecrMemuse((uint64_t)size);
174 }
175 
176 static FTPResponseWrapper *FTPResponseWrapperAlloc(FTPResponseLine *response)
177 {
178  FTPResponseWrapper *wrapper = FTPCalloc(1, sizeof(FTPResponseWrapper));
179  if (likely(wrapper)) {
180  FTPIncrMemuse(response->total_size);
181  wrapper->response = response;
182  }
183  return wrapper;
184 }
185 
186 static void FTPResponseWrapperFree(FTPResponseWrapper *wrapper)
187 {
188  if (wrapper->response) {
189  FTPDecrMemuse(wrapper->response->total_size);
190  SCFTPFreeResponseLine(wrapper->response);
191  }
192 
193  FTPFree(wrapper, sizeof(FTPResponseWrapper));
194 }
195 
196 static void *FTPLocalStorageAlloc(void)
197 {
198  /* needed by the mpm */
199  FTPThreadCtx *td = SCCalloc(1, sizeof(*td));
200  if (td == NULL) {
201  exit(EXIT_FAILURE);
202  }
203 
204  td->pmq = SCCalloc(1, sizeof(*td->pmq));
205  if (td->pmq == NULL) {
206  exit(EXIT_FAILURE);
207  }
208  PmqSetup(td->pmq);
209 
210  td->ftp_mpm_thread_ctx = SCCalloc(1, sizeof(MpmThreadCtx));
211  if (unlikely(td->ftp_mpm_thread_ctx == NULL)) {
212  exit(EXIT_FAILURE);
213  }
214  MpmInitThreadCtx(td->ftp_mpm_thread_ctx, ftp_mpm_ctx, FTP_MPM);
215  return td;
216 }
217 
218 static void FTPLocalStorageFree(void *ptr)
219 {
220  FTPThreadCtx *td = ptr;
221  if (td != NULL) {
222  if (td->pmq != NULL) {
223  PmqFree(td->pmq);
224  SCFree(td->pmq);
225  }
226 
227  if (td->ftp_mpm_thread_ctx != NULL) {
230  }
231 
232  SCFree(td);
233  }
234 }
235 static FTPTransaction *FTPTransactionCreate(FtpState *state)
236 {
237  SCEnter();
238  FTPTransaction *firsttx = TAILQ_FIRST(&state->tx_list);
239  if (firsttx && state->tx_cnt - firsttx->tx_id > ftp_config_maxtx) {
240  FTPTransaction *tx_old;
241  TAILQ_FOREACH (tx_old, &state->tx_list, next) {
242  if (!tx_old->done) {
243  tx_old->done = true;
244  tx_old->tx_data.updated_ts = true;
245  tx_old->tx_data.updated_tc = true;
247  &tx_old->tx_data.events, FtpEventTooManyTransactions);
248  break;
249  }
250  }
251  return NULL;
252  }
253  FTPTransaction *tx = FTPCalloc(1, sizeof(*tx));
254  if (tx == NULL) {
255  return NULL;
256  }
257 
258  TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
259  tx->tx_id = state->tx_cnt++;
260 
261  TAILQ_INIT(&tx->response_list);
262 
263  SCLogDebug("new transaction %p (state tx cnt %"PRIu64")", tx, state->tx_cnt);
264  return tx;
265 }
266 
267 static void FTPTransactionFree(FTPTransaction *tx)
268 {
269  SCEnter();
270 
272 
273  if (tx->request) {
274  FTPFree(tx->request, tx->request_length);
275  }
276 
277  FTPResponseWrapper *wrapper;
278  while ((wrapper = TAILQ_FIRST(&tx->response_list))) {
279  TAILQ_REMOVE(&tx->response_list, wrapper, next);
280  FTPResponseWrapperFree(wrapper);
281  }
282 
283  FTPFree(tx, sizeof(*tx));
284 }
285 
286 typedef struct FtpInput_ {
287  const uint8_t *buf;
288  int32_t consumed;
289  int32_t len;
290  int32_t orig_len;
292 
293 static AppLayerResult FTPGetLineForDirection(
294  FtpLineState *line, FtpInput *input, bool *current_line_truncated)
295 {
296  SCEnter();
297 
298  /* the caller reuses one FtpLineState for every line in a slice */
299  line->truncated = false;
300 
301  while (input->len > 0) {
302  const uint8_t *lf_idx = memchr(input->buf + input->consumed, 0x0a, input->len);
303 
304  if (lf_idx == NULL) {
305  if (!(*current_line_truncated) && (uint32_t)input->len >= ftp_max_line_len) {
306  *current_line_truncated = true;
307  line->truncated = true;
308  line->buf = input->buf + input->consumed;
309  line->len = ftp_max_line_len;
310  line->delim_len = 0;
311  /* No caller reads consumed after this; advance it so the cursor
312  * still describes the slice it was handed. */
313  input->consumed += input->len;
314  input->len = 0;
316  }
317  SCReturnStruct(APP_LAYER_INCOMPLETE(input->consumed, input->len + 1));
318  }
319 
320  const uint32_t o_consumed = input->consumed;
321  input->consumed = (uint32_t)(lf_idx - input->buf + 1);
322  const uint32_t line_len = (uint32_t)(input->consumed - o_consumed);
323  input->len -= (int32_t)line_len;
324  DEBUG_VALIDATE_BUG_ON((input->consumed + input->len) != input->orig_len);
325 
326  if (*current_line_truncated) {
327  /* The tail of an over-long line ends at this LF. Discard just that
328  * tail -- anything after the LF is a line of its own and still has
329  * to be parsed. */
330  *current_line_truncated = false;
331  continue;
332  }
333 
334  line->buf = input->buf + o_consumed;
335  line->len = line_len;
336  // There could be one chunk of command data that has LF but post the line limit
337  // e.g. input_len = 5077
338  // lf_idx = 5010
339  // max_line_len = 4096
340  if (line->len >= ftp_max_line_len) {
341  /* This LF ends the line, so nothing carries into the next slice;
342  * the line is simply reported clipped to the limit. */
343  line->truncated = true;
344  line->len = ftp_max_line_len;
346  }
347  if (input->consumed >= 2 && input->buf[input->consumed - 2] == 0x0D) {
348  line->delim_len = 2;
349  line->len -= 2;
350  } else {
351  line->delim_len = 1;
352  line->len -= 1;
353  }
355  }
356 
357  /* we have run out of input */
358  return APP_LAYER_ERROR;
359 }
360 
361 /**
362  * \brief This function is called to determine and set which command is being
363  * transferred to the ftp server
364  * \param thread context
365  * \param input input line of the command
366  * \param len of the command
367  * \param cmd_descriptor when the command has been parsed
368  *
369  * \retval 1 when the command is parsed, 0 otherwise
370  */
371 static int FTPParseRequestCommand(
372  FTPThreadCtx *td, FtpLineState *line, FtpCommandInfo *cmd_descriptor)
373 {
374  SCEnter();
375 
376  /* I don't like this pmq reset here. We'll devise a method later, that
377  * should make the use of the mpm very efficient */
378  PmqReset(td->pmq);
379  int mpm_cnt = mpm_table[FTP_MPM].Search(
380  ftp_mpm_ctx, td->ftp_mpm_thread_ctx, td->pmq, line->buf, line->len);
381  if (mpm_cnt) {
382  uint8_t command_code;
383  if (SCGetFtpCommandInfo(td->pmq->rule_id_array[0], NULL, &command_code, NULL)) {
384  cmd_descriptor->command_code = command_code;
385  /* FTP command indices are expressed in Rust as a u8 */
386  cmd_descriptor->command_index = (uint8_t)td->pmq->rule_id_array[0];
387  SCReturnInt(1);
388  } else {
389  /* Where is out command? */
391  }
392 #ifdef DEBUG
393  if (SCLogDebugEnabled()) {
394  const char *command_name = NULL;
395  (void)SCGetFtpCommandInfo(td->pmq->rule_id_array[0], &command_name, NULL, NULL);
396  SCLogDebug("matching FTP command is %s [code: %d, index %d]", command_name,
397  command_code, td->pmq->rule_id_array[0]);
398  }
399 #endif
400  }
401 
402  cmd_descriptor->command_code = FTP_COMMAND_UNKNOWN;
403  SCReturnInt(0);
404 }
405 
406 static void FtpTransferCmdFree(void *data)
407 {
408  FtpTransferCmd *cmd = (FtpTransferCmd *)data;
409  if (cmd == NULL)
410  return;
411  if (cmd->file_name) {
412  FTPFree((void *)cmd->file_name, cmd->file_len + 1);
413  }
414  SCFTPTransferCmdFree(cmd);
415  FTPDecrMemuse((uint64_t)sizeof(FtpTransferCmd));
416 }
417 
418 static uint32_t CopyCommandLine(uint8_t **dest, FtpLineState *line)
419 {
420  /* Strip trailing whitespace before allocating, so the length accounted by
421  * FTPCalloc matches the length the caller later hands to FTPFree. */
422  while (line->len && isspace((unsigned char)line->buf[line->len - 1])) {
423  line->len--;
424  }
425 
426  if (unlikely(line->len == 0)) {
427  return 0;
428  }
429 
430  uint8_t *where = FTPCalloc(line->len + 1, sizeof(char));
431  if (unlikely(where == NULL)) {
432  return 0;
433  }
434  memcpy(where, line->buf, line->len);
435  where[line->len] = '\0';
436  *dest = where;
437 
438  return line->len + 1;
439 }
440 
441 
442 /**
443  * \brief This function is called to retrieve a ftp request
444  * \param ftp_state the ftp state structure for the parser
445  *
446  * \retval APP_LAYER_OK when input was process successfully
447  * \retval APP_LAYER_ERROR when a unrecoverable error was encountered
448  */
449 static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserState *pstate,
450  StreamSlice stream_slice, void *local_data)
451 {
452  FTPThreadCtx *thread_data = local_data;
453 
454  SCEnter();
455  /* PrintRawDataFp(stdout, input,input_len); */
456 
457  FtpState *state = (FtpState *)ftp_state;
458  void *ptmp;
459 
460  const uint8_t *input = StreamSliceGetData(&stream_slice);
461  uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
462 
463  if (input == NULL && SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS)) {
465  } else if (input == NULL || input_len == 0) {
467  }
468 
469  FtpInput ftpi = { .buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
470  FtpLineState line = { .buf = NULL, .len = 0, .delim_len = 0, .truncated = false };
471 
472  uint8_t direction = STREAM_TOSERVER;
473  AppLayerResult res;
474  while (1) {
475  res = FTPGetLineForDirection(&line, &ftpi, &state->current_line_truncated_ts);
476  if (res.status == 1) {
477  return res;
478  } else if (res.status == -1) {
479  break;
480  }
481 
482  FtpCommandInfo cmd_descriptor;
483  if (!FTPParseRequestCommand(thread_data, &line, &cmd_descriptor)) {
484  state->command = FTP_COMMAND_UNKNOWN;
485  continue;
486  }
487 
488  state->command = cmd_descriptor.command_code;
489  FTPTransaction *tx = FTPTransactionCreate(state);
490  if (unlikely(tx == NULL))
492  tx->tx_data.updated_ts = true;
493  state->curr_tx = tx;
494 
495  tx->command_descriptor = cmd_descriptor;
496  tx->request_length = CopyCommandLine(&tx->request, &line);
497  tx->request_truncated = line.truncated;
498  if (tx->request_truncated) {
499  SCAppLayerDecoderEventsSetEventRaw(&tx->tx_data.events, FtpEventRequestCommandTooLong);
500  }
501 
502  /* change direction (default to server) so expectation will handle
503  * the correct message when expectation will match.
504  * For ftp active mode, data connection direction is opposite to
505  * control direction.
506  */
507  if ((state->active &&
508  (state->command == FTP_COMMAND_STOR || state->command == FTP_COMMAND_APPE ||
509  state->command == FTP_COMMAND_STOU)) ||
510  (!state->active &&
511  (state->command == FTP_COMMAND_RETR || state->command == FTP_COMMAND_NLST ||
512  state->command == FTP_COMMAND_LIST ||
513  state->command == FTP_COMMAND_MLSD))) {
514  direction = STREAM_TOCLIENT;
515  }
516 
517  bool has_file = false;
518 
519  switch (state->command) {
520  case FTP_COMMAND_EPRT:
521  // fallthrough
522  case FTP_COMMAND_PORT:
523  if (line.len + 1 > state->port_line_size) {
524  /* Allocate an extra byte for a NULL terminator */
525  ptmp = FTPRealloc(state->port_line, state->port_line_size, line.len + 1);
526  if (ptmp == NULL) {
527  if (state->port_line) {
528  FTPFree(state->port_line, state->port_line_size);
529  state->port_line = NULL;
530  state->port_line_size = 0;
531  state->port_line_len = 0;
532  }
534  }
535  state->port_line = ptmp;
536  state->port_line_size = line.len + 1;
537  }
538  memcpy(state->port_line, line.buf, line.len);
539  state->port_line_len = line.len;
540  break;
541  case FTP_COMMAND_RETR:
542  // fallthrough
543  case FTP_COMMAND_STOR:
544  // fallthrough
545  case FTP_COMMAND_APPE:
546  /* Ensure that there is a file name
547  * -- need more than 5 chars: cmd [4], space, <filename>
548  */
549  if (line.len < 6) {
551  &tx->tx_data.events, FtpEventFileWithoutName);
552  break;
553  }
554  has_file = true;
555  /* fallthrough */
556  case FTP_COMMAND_STOU:
557  if (line.len >= 6) {
558  has_file = true;
559  }
560  /* fallthrough */
561  case FTP_COMMAND_NLST:
562  case FTP_COMMAND_LIST:
563  case FTP_COMMAND_MLSD: {
564  /* Ensure a port has been negotiated. */
565  if (state->dyn_port == 0) {
566  SCAppLayerDecoderEventsSetEventRaw(&tx->tx_data.events, FtpEventFileBeforePort);
567  break;
568  }
569 
570  FtpTransferCmd *data = SCFTPTransferCmdNew();
571  if (data == NULL)
573  FTPIncrMemuse((uint64_t)(sizeof *data));
574  data->cmd = state->command;
575  data->flow_id = FlowGetId(f);
576  data->direction = direction;
577  data->data_free = FtpTransferCmdFree;
578 
579  /*
580  * Min size has been checked in FTPParseRequestCommand
581  * SC_FILENAME_MAX includes the null
582  */
583  if (has_file) {
584  uint32_t file_name_len = MIN(SC_FILENAME_MAX - 1, line.len - 5);
585 #if SC_FILENAME_MAX > UINT16_MAX
586 #error SC_FILENAME_MAX is greater than UINT16_MAX
587 #endif
588  data->file_name = FTPCalloc(file_name_len + 1, sizeof(char));
589  if (data->file_name == NULL) {
590  FtpTransferCmdFree(data);
592  }
593  data->file_name[file_name_len] = 0;
594  data->file_len = (uint16_t)file_name_len;
595  memcpy(data->file_name, line.buf + 5, file_name_len);
596  } else if (state->command == FTP_COMMAND_STOU) {
597  const char default_file_name[] = "<stou>";
598  uint32_t file_name_len = sizeof(default_file_name);
599  data->file_name = FTPCalloc(file_name_len, sizeof(char));
600  if (data->file_name == NULL) {
601  FtpTransferCmdFree(data);
603  }
604  data->file_name[file_name_len - 1] = 0;
605  data->file_len = (uint16_t)file_name_len - 1;
606  memcpy(data->file_name, default_file_name, file_name_len);
607  }
608  int ret = AppLayerExpectationCreate(
609  f, direction, 0, state->dyn_port, ALPROTO_FTPDATA, data);
610  if (ret == -1) {
611  FtpTransferCmdFree(data);
612  SCLogDebug("No expectation created.");
614  } else {
615  SCLogDebug("Expectation created [direction: %s, dynamic port %" PRIu16 "].",
616  state->active ? "to server" : "to client", state->dyn_port);
617  }
618 
619  /* reset the dyn port to avoid duplicate */
620  state->dyn_port = 0;
621  /* reset active/passive indicator */
622  state->active = false;
623 
624  break;
625  }
626  default:
627  break;
628  }
630  }
631 
633 }
634 
635 static int FTPParsePassiveResponse(FtpState *state, const uint8_t *input, uint32_t input_len)
636 {
637  uint16_t dyn_port = SCFTPParsePortPasv(input, input_len);
638  if (dyn_port == 0) {
639  return -1;
640  }
641  SCLogDebug("FTP passive mode (v4): dynamic port %"PRIu16"", dyn_port);
642  state->active = false;
643  state->dyn_port = dyn_port;
644  state->curr_tx->dyn_port = dyn_port;
645  state->curr_tx->active = false;
646 
647  return 0;
648 }
649 
650 static int FTPParsePassiveResponseV6(FtpState *state, const uint8_t *input, uint32_t input_len)
651 {
652  uint16_t dyn_port = SCFTPParsePortEpsv(input, input_len);
653  if (dyn_port == 0) {
654  return -1;
655  }
656  SCLogDebug("FTP passive mode (v6): dynamic port %"PRIu16"", dyn_port);
657  state->active = false;
658  state->dyn_port = dyn_port;
659  state->curr_tx->dyn_port = dyn_port;
660  state->curr_tx->active = false;
661  return 0;
662 }
663 
664 /**
665  * \brief Handle preliminary replies -- keep tx open
666  * \retval bool True for a positive preliminary reply; false otherwise
667  *
668  * 1yz Positive Preliminary reply
669  *
670  * The requested action is being initiated; expect another
671  * reply before proceeding with a new command
672  */
673 static inline bool FTPIsPPR(const uint8_t *input, uint32_t input_len)
674 {
675  return input_len >= 4 && isdigit(input[0]) && input[0] == '1' &&
676  isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
677 }
678 
679 /**
680  * \brief This function is called to retrieve a ftp response
681  * \param ftp_state the ftp state structure for the parser
682  * \param input input line of the command
683  * \param input_len length of the request
684  * \param output the resulting output
685  *
686  * \retval 1 when the command is parsed, 0 otherwise
687  */
688 static AppLayerResult FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
689  StreamSlice stream_slice, void *local_data)
690 {
691  FtpState *state = (FtpState *)ftp_state;
692 
693  const uint8_t *input = StreamSliceGetData(&stream_slice);
694  uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
695 
696  if (unlikely(input_len == 0)) {
698  }
699  FtpInput ftpi = { .buf = input, .len = input_len, .orig_len = input_len, .consumed = 0 };
700  FtpLineState line = { .buf = NULL, .len = 0, .delim_len = 0, .truncated = false };
701 
702  FTPTransaction *lasttx = TAILQ_FIRST(&state->tx_list);
703  AppLayerResult res;
704  while (1) {
705  res = FTPGetLineForDirection(&line, &ftpi, &state->current_line_truncated_tc);
706  if (res.status == 1) {
707  return res;
708  } else if (res.status == -1) {
709  break;
710  }
711  FTPTransaction *tx = FTPGetOldestTx(state, lasttx);
712  if (tx == NULL) {
713  tx = FTPTransactionCreate(state);
714  if (tx != NULL) {
715  /* This is a TC only transaction, skip TS inspection. */
716  tx->tx_data.flags |= APP_LAYER_TX_SKIP_INSPECT_TS;
717  }
718  }
719  if (unlikely(tx == NULL)) {
721  }
722  lasttx = tx;
723  tx->tx_data.updated_tc = true;
724  if (state->command == FTP_COMMAND_UNKNOWN) {
725  /* unknown */
726  tx->command_descriptor.command_code = FTP_COMMAND_UNKNOWN;
727  }
728 
729  state->curr_tx = tx;
730 
731  const bool ppr = FTPIsPPR(line.buf, line.len);
732  uint16_t dyn_port;
733  switch (state->command) {
734  case FTP_COMMAND_AUTH_TLS:
735  if (line.len >= 4 && SCMemcmp("234 ", line.buf, 4) == 0) {
737  }
738  break;
739 
740  case FTP_COMMAND_EPRT:
741  dyn_port = SCFTPParsePortEprt(state->port_line, state->port_line_len);
742  if (dyn_port == 0) {
743  goto tx_complete;
744  }
745  state->dyn_port = dyn_port;
746  state->active = true;
747  tx->dyn_port = dyn_port;
748  tx->active = true;
749  SCLogDebug("FTP active mode (v6): dynamic port %" PRIu16 "", dyn_port);
750  break;
751 
752  case FTP_COMMAND_PORT:
753  dyn_port = SCFTPParsePort(state->port_line, state->port_line_len);
754  if (dyn_port == 0) {
755  goto tx_complete;
756  }
757  state->dyn_port = dyn_port;
758  state->active = true;
759  tx->dyn_port = state->dyn_port;
760  tx->active = true;
761  SCLogDebug("FTP active mode (v4): dynamic port %" PRIu16 "", dyn_port);
762  break;
763 
764  case FTP_COMMAND_PASV:
765  if (line.len >= 4 && SCMemcmp("227 ", line.buf, 4) == 0) {
766  FTPParsePassiveResponse(ftp_state, line.buf, line.len);
767  }
768  break;
769 
770  case FTP_COMMAND_EPSV:
771  if (line.len >= 4 && SCMemcmp("229 ", line.buf, 4) == 0) {
772  FTPParsePassiveResponseV6(ftp_state, line.buf, line.len);
773  }
774  break;
775  default:
776  break;
777  }
778 
779  if (ppr && unlikely(tx->preliminary_response_cnt >= ftp_max_preliminary_responses)) {
780  if (!AppLayerDecoderEventsIsEventSet(
781  tx->tx_data.events, FtpEventTooManyPreliminaryResponses)) {
783  &tx->tx_data.events, FtpEventTooManyPreliminaryResponses);
784  }
785  /* the line is dropped, but its truncation is still reported */
786  if (line.truncated && !AppLayerDecoderEventsIsEventSet(
787  tx->tx_data.events, FtpEventResponseCommandTooLong)) {
789  &tx->tx_data.events, FtpEventResponseCommandTooLong);
790  }
791  } else if (likely(line.len)) {
792  FTPResponseLine *response = SCFTPParseResponseLine((const char *)line.buf, line.len);
793  if (likely(response)) {
794  FTPResponseWrapper *wrapper = FTPResponseWrapperAlloc(response);
795  if (likely(wrapper)) {
796  response->truncated = line.truncated;
797  if (response->truncated) {
799  &tx->tx_data.events, FtpEventResponseCommandTooLong);
800  }
801  TAILQ_INSERT_TAIL(&tx->response_list, wrapper, next);
802  if (ppr) {
804  }
805  } else {
806  SCFTPFreeResponseLine(response);
807  }
808  } else {
809  SCLogDebug("unable to parse FTP response line \"%s\"", line.buf);
810  }
811  }
812 
813  /* Handle preliminary replies -- keep tx open */
814  if (ppr) {
815  continue;
816  }
817  tx_complete:
818  tx->done = true;
820  }
821 
823 }
824 
825 
826 #ifdef DEBUG
827 static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
828 static uint64_t ftp_state_memuse = 0;
829 static uint64_t ftp_state_memcnt = 0;
830 #endif
831 
832 static void *FTPStateAlloc(void *orig_state, AppProto proto_orig)
833 {
834  void *s = FTPCalloc(1, sizeof(FtpState));
835  if (unlikely(s == NULL))
836  return NULL;
837 
838  FtpState *ftp_state = (FtpState *) s;
839  TAILQ_INIT(&ftp_state->tx_list);
840 
841 #ifdef DEBUG
842  SCMutexLock(&ftp_state_mem_lock);
843  ftp_state_memcnt++;
844  ftp_state_memuse+=sizeof(FtpState);
845  SCMutexUnlock(&ftp_state_mem_lock);
846 #endif
847  return s;
848 }
849 
850 static void FTPStateFree(void *s)
851 {
852  FtpState *fstate = (FtpState *) s;
853  if (fstate->port_line != NULL)
854  FTPFree(fstate->port_line, fstate->port_line_size);
855 
856  FTPTransaction *tx = NULL;
857  while ((tx = TAILQ_FIRST(&fstate->tx_list))) {
858  TAILQ_REMOVE(&fstate->tx_list, tx, next);
859 #ifdef DEBUG
860  if (SCLogDebugEnabled()) {
861  const char *command_name = NULL;
862  (void)SCGetFtpCommandInfo(
863  tx->command_descriptor.command_index, &command_name, NULL, NULL);
864  SCLogDebug("[%s] state %p id %" PRIu64 ", Freeing %d bytes at %p",
865  command_name != NULL ? command_name : "n/a", s, tx->tx_id, tx->request_length,
866  tx->request);
867  }
868 #endif
869 
870  FTPTransactionFree(tx);
871  }
872 
873  FTPFree(s, sizeof(FtpState));
874 #ifdef DEBUG
875  SCMutexLock(&ftp_state_mem_lock);
876  ftp_state_memcnt--;
877  ftp_state_memuse-=sizeof(FtpState);
878  SCMutexUnlock(&ftp_state_mem_lock);
879 #endif
880 }
881 
882 /**
883  * \brief This function returns the oldest open transaction; if none
884  * are open, then the oldest transaction is returned
885  * \param ftp_state the ftp state structure for the parser
886  * \param starttx the ftp transaction where to start looking
887  *
888  * \retval transaction pointer when a transaction was found; NULL otherwise.
889  */
890 static FTPTransaction *FTPGetOldestTx(const FtpState *ftp_state, FTPTransaction *starttx)
891 {
892  if (unlikely(!ftp_state)) {
893  SCLogDebug("NULL state object; no transactions available");
894  return NULL;
895  }
896  FTPTransaction *tx = starttx;
897  FTPTransaction *lasttx = NULL;
898  while(tx != NULL) {
899  /* Return oldest open tx */
900  if (!tx->done) {
901  SCLogDebug("Returning tx %p id %"PRIu64, tx, tx->tx_id);
902  return tx;
903  }
904  /* save for the end */
905  lasttx = tx;
906  tx = TAILQ_NEXT(tx, next);
907  }
908  /* All tx are closed; return last element */
909  if (lasttx)
910  SCLogDebug("Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->tx_id);
911  return lasttx;
912 }
913 
914 static void *FTPGetTx(void *state, uint64_t tx_id)
915 {
916  FtpState *ftp_state = (FtpState *)state;
917  if (ftp_state) {
918  FTPTransaction *tx = NULL;
919 
920  if (ftp_state->curr_tx == NULL)
921  return NULL;
922  if (ftp_state->curr_tx->tx_id == tx_id)
923  return ftp_state->curr_tx;
924 
925  TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
926  if (tx->tx_id == tx_id)
927  return tx;
928  }
929  }
930  return NULL;
931 }
932 
933 static AppLayerTxData *FTPGetTxData(void *vtx)
934 {
935  FTPTransaction *tx = (FTPTransaction *)vtx;
936  return &tx->tx_data;
937 }
938 
939 static AppLayerStateData *FTPGetStateData(void *vstate)
940 {
941  FtpState *s = (FtpState *)vstate;
942  return &s->state_data;
943 }
944 
945 static void FTPStateTransactionFree(void *state, uint64_t tx_id)
946 {
947  FtpState *ftp_state = state;
948  FTPTransaction *tx = NULL;
949  TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
950  if (tx_id < tx->tx_id)
951  break;
952  else if (tx_id > tx->tx_id)
953  continue;
954 
955  if (tx == ftp_state->curr_tx)
956  ftp_state->curr_tx = NULL;
957  TAILQ_REMOVE(&ftp_state->tx_list, tx, next);
958  FTPTransactionFree(tx);
959  break;
960  }
961 }
962 
963 static uint64_t FTPGetTxCnt(void *state)
964 {
965  uint64_t cnt = 0;
966  FtpState *ftp_state = state;
967  if (ftp_state) {
968  cnt = ftp_state->tx_cnt;
969  }
970  SCLogDebug("returning state %p %"PRIu64, state, cnt);
971  return cnt;
972 }
973 
974 static int FTPGetAlstateProgress(void *vtx, uint8_t direction)
975 {
976  SCLogDebug("tx %p", vtx);
977  FTPTransaction *tx = vtx;
978 
979  /* having a tx implies request side is done */
980  if (direction == STREAM_TOSERVER) {
981  return FTP_STATE_FINISHED;
982  }
983  if (!tx->done) {
984  return FTP_STATE_IN_PROGRESS;
985  }
986 
987  return FTP_STATE_FINISHED;
988 }
989 
990 static AppProto FTPUserProbingParser(
991  const Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir)
992 {
993  if (f->alproto_tc == ALPROTO_POP3) {
994  // POP traffic begins by same "USER" pattern as FTP
995  return ALPROTO_FAILED;
996  }
997  if (f->alproto_tc == ALPROTO_IMAP) {
998  // USER may be used as an IMAP tag
999  return ALPROTO_FAILED;
1000  }
1001  return ALPROTO_FTP;
1002 }
1003 
1004 static AppProto FTPQuitProbingParser(
1005  const Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir)
1006 {
1007  // another check for minimum length
1008  if (len < 5) {
1009  return ALPROTO_UNKNOWN;
1010  }
1011  // begins by QUIT
1012  if (SCMemcmp(input, "QUIT", 4) != 0) {
1013  return ALPROTO_FAILED;
1014  }
1015  return ALPROTO_FTP;
1016 }
1017 
1018 static AppProto FTPServerProbingParser(
1019  const Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir)
1020 {
1021  // another check for minimum length
1022  if (len < 5) {
1023  return ALPROTO_UNKNOWN;
1024  }
1025  // begins by 220
1026  if (input[0] != '2' || input[1] != '2' || input[2] != '0') {
1027  return ALPROTO_FAILED;
1028  }
1029  // followed by space or hypen
1030  if (input[3] != ' ' && input[3] != '-') {
1031  return ALPROTO_FAILED;
1032  }
1033  if (f->alproto_ts == ALPROTO_FTP || (f->todstbytecnt > 4 && f->alproto_ts == ALPROTO_UNKNOWN)) {
1034  // only validates FTP if client side was FTP
1035  // or if client side is unknown despite having received bytes
1036  if (memchr(input + 4, '\n', len - 4) != NULL) {
1037  return ALPROTO_FTP;
1038  }
1039  }
1040  return ALPROTO_UNKNOWN;
1041 }
1042 
1043 static int FTPRegisterPatternsForProtocolDetection(void)
1044 {
1046  IPPROTO_TCP, ALPROTO_FTP, "220 (", 5, 0, STREAM_TOCLIENT) < 0) {
1047  return -1;
1048  }
1050  IPPROTO_TCP, ALPROTO_FTP, "FEAT", 4, 0, STREAM_TOSERVER) < 0) {
1051  return -1;
1052  }
1053  if (SCAppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_FTP, "USER ", 5, 0,
1054  STREAM_TOSERVER, FTPUserProbingParser, 5, 5) < 0) {
1055  return -1;
1056  }
1057 
1059  IPPROTO_TCP, ALPROTO_FTP, "PORT ", 5, 0, STREAM_TOSERVER) < 0) {
1060  return -1;
1061  }
1062  // Only check FTP on known ports as the banner has nothing special beyond
1063  // the response code shared with SMTP.
1065  "tcp", IPPROTO_TCP, "ftp", ALPROTO_FTP, 0, 5, NULL, FTPServerProbingParser)) {
1066  // STREAM_TOSERVER here means use 21 as flow destination port
1067  // and FTPServerProbingParser is probing parser to client
1068  SCAppLayerProtoDetectPPRegister(IPPROTO_TCP, "21", ALPROTO_FTP, 0, 5, STREAM_TOSERVER,
1069  FTPQuitProbingParser, FTPServerProbingParser);
1070  }
1071  return 0;
1072 }
1073 
1074 
1076 
1077 /**
1078  * \brief This function is called to retrieve a ftp request
1079  * \param ftp_state the ftp state structure for the parser
1080  * \param output the resulting output
1081  *
1082  * \retval 1 when the command is parsed, 0 otherwise
1083  */
1084 static AppLayerResult FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
1085  AppLayerParserState *pstate, StreamSlice stream_slice, void *local_data, uint8_t direction)
1086 {
1087  const uint8_t *input = StreamSliceGetData(&stream_slice);
1088  uint32_t input_len = StreamSliceGetDataLen(&stream_slice);
1089  const bool eof = (direction & STREAM_TOSERVER)
1090  ? SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) != 0
1091  : SCAppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) != 0;
1092 
1093  SCTxDataUpdateFileFlags(&ftpdata_state->tx_data, ftpdata_state->state_data.file_flags);
1094  if (ftpdata_state->tx_data.file_tx == 0)
1095  ftpdata_state->tx_data.file_tx = direction & (STREAM_TOSERVER | STREAM_TOCLIENT);
1096  if (direction & STREAM_TOSERVER) {
1097  ftpdata_state->tx_data.updated_ts = true;
1098  } else {
1099  ftpdata_state->tx_data.updated_tc = true;
1100  }
1101  /* we depend on detection engine for file pruning */
1102  const uint16_t flags = SCFileFlowFlagsToFlags(ftpdata_state->tx_data.file_flags, direction);
1103  int ret = 0;
1104 
1105  SCLogDebug("FTP-DATA input_len %u flags %04x dir %d/%s EOF %s", input_len, flags, direction,
1106  (direction & STREAM_TOSERVER) ? "toserver" : "toclient", eof ? "true" : "false");
1107 
1108  SCLogDebug("FTP-DATA flags %04x dir %d", flags, direction);
1109  if (!ftpdata_state->initialized && input_len) {
1110  FtpTransferCmd *data =
1111  (FtpTransferCmd *)SCFlowGetStorageById(f, AppLayerExpectationGetFlowId());
1112  if (data == NULL) {
1114  }
1115 
1116  /* we shouldn't get data in the wrong dir. Don't set things up for this dir */
1117  if ((direction & data->direction) == 0) {
1118  // TODO set event for data in wrong direction
1119  SCLogDebug("input %u not for our direction (%s): %s/%s", input_len,
1120  (direction & STREAM_TOSERVER) ? "toserver" : "toclient",
1121  data->cmd == FTP_COMMAND_STOR ? "STOR" : "RETR",
1122  (data->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1124  }
1125 
1126  if (data->file_name) {
1127  ftpdata_state->files = FileContainerAlloc();
1128  if (ftpdata_state->files == NULL) {
1131  }
1132 
1133  ftpdata_state->file_name = data->file_name;
1134  ftpdata_state->file_len = data->file_len;
1135  data->file_name = NULL;
1136  data->file_len = 0;
1137  }
1138  f->parent_id = data->flow_id;
1139  ftpdata_state->command = data->cmd;
1140  switch (data->cmd) {
1141  case FTP_COMMAND_STOR:
1142  ftpdata_state->direction = data->direction;
1143  SCLogDebug("STOR data to %s",
1144  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1145  break;
1146  case FTP_COMMAND_APPE:
1147  ftpdata_state->direction = data->direction;
1148  SCLogDebug("APPE data to %s",
1149  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1150  break;
1151  case FTP_COMMAND_STOU:
1152  ftpdata_state->direction = data->direction;
1153  SCLogDebug("STOU data to %s",
1154  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1155  break;
1156  case FTP_COMMAND_RETR:
1157  ftpdata_state->direction = data->direction;
1158  SCLogDebug("RETR data to %s",
1159  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1160  break;
1161  case FTP_COMMAND_NLST:
1162  ftpdata_state->direction = data->direction;
1163  SCLogDebug("NLST data to %s",
1164  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1165  break;
1166  case FTP_COMMAND_LIST:
1167  ftpdata_state->direction = data->direction;
1168  SCLogDebug("LIST data to %s",
1169  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1170  break;
1171  case FTP_COMMAND_MLSD:
1172  ftpdata_state->direction = data->direction;
1173  SCLogDebug("MLSD data to %s",
1174  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1175  break;
1176  default:
1177  break;
1178  }
1179 
1180  if (ftpdata_state->file_name) {
1181  /* open with fixed track_id 0 as we can have just one
1182  * file per ftp-data flow. */
1183  if (FileOpenFileWithId(ftpdata_state->files, &sbcfg, 0ULL,
1184  (uint8_t *)ftpdata_state->file_name, ftpdata_state->file_len, input,
1185  input_len, flags) != 0) {
1186  SCLogDebug("Can't open file");
1187  ret = -1;
1188  }
1189  ftpdata_state->tx_data.files_opened = 1;
1190  }
1192 
1193  ftpdata_state->initialized = true;
1194  } else {
1195  if ((direction & ftpdata_state->direction) == 0) {
1196  if (input_len) {
1197  // TODO set event for data in wrong direction
1198  }
1199  SCLogDebug("input %u not for us (%s): %s/%s", input_len,
1200  (direction & STREAM_TOSERVER) ? "toserver" : "toclient",
1201  ftpdata_state->command == FTP_COMMAND_STOR ? "STOR" : "RETR",
1202  (ftpdata_state->direction & STREAM_TOSERVER) ? "toserver" : "toclient");
1204  }
1205  if (ftpdata_state->state == FTPDATA_STATE_FINISHED) {
1206  SCLogDebug("state is already finished");
1207  DEBUG_VALIDATE_BUG_ON(input_len); // data after state finished is a bug.
1209  }
1210  if (ftpdata_state->file_name && input_len != 0) {
1211  ret = FileAppendData(ftpdata_state->files, &sbcfg, input, input_len);
1212  if (ret == -2) {
1213  ret = 0;
1214  SCLogDebug("FileAppendData() - file no longer being extracted");
1215  goto out;
1216  } else if (ret < 0) {
1217  SCLogDebug("FileAppendData() failed: %d", ret);
1218  ret = -2;
1219  goto out;
1220  }
1221  }
1222  }
1223 
1224  DEBUG_VALIDATE_BUG_ON((direction & ftpdata_state->direction) == 0); // should be unreachable
1225  if (eof) {
1226  if (ftpdata_state->file_name) {
1227  ret = FileCloseFile(ftpdata_state->files, &sbcfg, NULL, 0, flags);
1228  }
1229  ftpdata_state->state = FTPDATA_STATE_FINISHED;
1230  SCLogDebug("closed because of eof: state now FTPDATA_STATE_FINISHED");
1231  }
1232 out:
1233  if (ret < 0) {
1235  }
1237 }
1238 
1239 static AppLayerResult FTPDataParseRequest(Flow *f, void *ftp_state, AppLayerParserState *pstate,
1240  StreamSlice stream_slice, void *local_data)
1241 {
1242  return FTPDataParse(f, ftp_state, pstate, stream_slice, local_data, STREAM_TOSERVER);
1243 }
1244 
1245 static AppLayerResult FTPDataParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
1246  StreamSlice stream_slice, void *local_data)
1247 {
1248  return FTPDataParse(f, ftp_state, pstate, stream_slice, local_data, STREAM_TOCLIENT);
1249 }
1250 
1251 #ifdef DEBUG
1252 static SCMutex ftpdata_state_mem_lock = SCMUTEX_INITIALIZER;
1253 static uint64_t ftpdata_state_memuse = 0;
1254 static uint64_t ftpdata_state_memcnt = 0;
1255 #endif
1256 
1257 static void *FTPDataStateAlloc(void *orig_state, AppProto proto_orig)
1258 {
1259  void *s = FTPCalloc(1, sizeof(FtpDataState));
1260  if (unlikely(s == NULL))
1261  return NULL;
1262 
1263  FtpDataState *state = (FtpDataState *) s;
1264  state->state = FTPDATA_STATE_IN_PROGRESS;
1265 
1266 #ifdef DEBUG
1267  SCMutexLock(&ftpdata_state_mem_lock);
1268  ftpdata_state_memcnt++;
1269  ftpdata_state_memuse+=sizeof(FtpDataState);
1270  SCMutexUnlock(&ftpdata_state_mem_lock);
1271 #endif
1272  return s;
1273 }
1274 
1275 static void FTPDataStateFree(void *s)
1276 {
1277  FtpDataState *fstate = (FtpDataState *) s;
1278 
1279  SCAppLayerTxDataCleanup(&fstate->tx_data);
1280 
1281  if (fstate->file_name != NULL) {
1282  FTPFree(fstate->file_name, fstate->file_len + 1);
1283  }
1284 
1285  FileContainerFree(fstate->files, &sbcfg);
1286 
1287  FTPFree(s, sizeof(FtpDataState));
1288 #ifdef DEBUG
1289  SCMutexLock(&ftpdata_state_mem_lock);
1290  ftpdata_state_memcnt--;
1291  ftpdata_state_memuse-=sizeof(FtpDataState);
1292  SCMutexUnlock(&ftpdata_state_mem_lock);
1293 #endif
1294 }
1295 
1296 static AppLayerTxData *FTPDataGetTxData(void *vtx)
1297 {
1298  FtpDataState *ftp_state = (FtpDataState *)vtx;
1299  return &ftp_state->tx_data;
1300 }
1301 
1302 static AppLayerStateData *FTPDataGetStateData(void *vstate)
1303 {
1304  FtpDataState *ftp_state = (FtpDataState *)vstate;
1305  return &ftp_state->state_data;
1306 }
1307 
1308 static void FTPDataStateTransactionFree(void *state, uint64_t tx_id)
1309 {
1310  /* do nothing */
1311 }
1312 
1313 static void *FTPDataGetTx(void *state, uint64_t tx_id)
1314 {
1315  FtpDataState *ftp_state = (FtpDataState *)state;
1316  return ftp_state;
1317 }
1318 
1319 static uint64_t FTPDataGetTxCnt(void *state)
1320 {
1321  /* ftp-data is single tx */
1322  return 1;
1323 }
1324 
1325 static int FTPDataGetAlstateProgress(void *tx, uint8_t direction)
1326 {
1327  FtpDataState *ftpdata_state = (FtpDataState *)tx;
1328  if (direction == ftpdata_state->direction)
1329  return ftpdata_state->state;
1330  else
1331  return FTPDATA_STATE_FINISHED;
1332 }
1333 
1334 static AppLayerGetFileState FTPDataStateGetTxFiles(void *tx, uint8_t direction)
1335 {
1336  FtpDataState *ftpdata_state = (FtpDataState *)tx;
1337  AppLayerGetFileState files = { .fc = NULL, .cfg = &sbcfg };
1338 
1339  if (direction == ftpdata_state->direction)
1340  files.fc = ftpdata_state->files;
1341 
1342  return files;
1343 }
1344 
1345 static void FTPSetMpmState(void)
1346 {
1347  ftp_mpm_ctx = SCCalloc(1, sizeof(MpmCtx));
1348  if (unlikely(ftp_mpm_ctx == NULL)) {
1349  exit(EXIT_FAILURE);
1350  }
1351  MpmInitCtx(ftp_mpm_ctx, FTP_MPM);
1352 
1353  SCFTPSetMpmState(ftp_mpm_ctx);
1354  mpm_table[FTP_MPM].Prepare(NULL, ftp_mpm_ctx);
1355 }
1356 
1357 static void FTPFreeMpmState(void)
1358 {
1359  if (ftp_mpm_ctx != NULL) {
1360  mpm_table[FTP_MPM].DestroyCtx(ftp_mpm_ctx);
1361  SCFree(ftp_mpm_ctx);
1362  ftp_mpm_ctx = NULL;
1363  }
1364 }
1365 
1366 /** \brief FTP tx iterator, specialized for its linked list
1367  *
1368  * \retval txptr or NULL if no more txs in list
1369  */
1370 static AppLayerGetTxIterTuple FTPGetTxIterator(const uint8_t ipproto, const AppProto alproto,
1371  void *alstate, uint64_t min_tx_id, uint64_t max_tx_id, AppLayerGetTxIterState *state)
1372 {
1373  FtpState *ftp_state = (FtpState *)alstate;
1374  AppLayerGetTxIterTuple no_tuple = { NULL, 0, false };
1375  if (ftp_state) {
1376  FTPTransaction *tx_ptr;
1377  if (state->un.ptr == NULL) {
1378  tx_ptr = TAILQ_FIRST(&ftp_state->tx_list);
1379  } else {
1380  tx_ptr = (FTPTransaction *)state->un.ptr;
1381  }
1382  if (tx_ptr) {
1383  while (tx_ptr->tx_id < min_tx_id) {
1384  tx_ptr = TAILQ_NEXT(tx_ptr, next);
1385  if (!tx_ptr) {
1386  return no_tuple;
1387  }
1388  }
1389  if (tx_ptr->tx_id >= max_tx_id) {
1390  return no_tuple;
1391  }
1392  state->un.ptr = TAILQ_NEXT(tx_ptr, next);
1393  AppLayerGetTxIterTuple tuple = {
1394  .tx_ptr = tx_ptr,
1395  .tx_id = tx_ptr->tx_id,
1396  .has_next = (state->un.ptr != NULL),
1397  };
1398  return tuple;
1399  }
1400  }
1401  return no_tuple;
1402 }
1403 
1405 {
1406  const char *proto_name = "ftp";
1407  const char *proto_data_name = "ftp-data";
1408 
1409  /** FTP */
1410  if (SCAppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
1412  if (FTPRegisterPatternsForProtocolDetection() < 0 )
1413  return;
1415  }
1416 
1417  if (SCAppLayerParserConfParserEnabled("tcp", proto_name)) {
1418  AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
1419  FTPParseRequest);
1420  AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
1421  FTPParseResponse);
1422  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
1424  IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
1425 
1426  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree);
1427 
1428  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx);
1429  AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxData);
1430  AppLayerParserRegisterGetTxIterator(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxIterator);
1431  AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetStateData);
1432 
1433  AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_FTP, FTPLocalStorageAlloc,
1434  FTPLocalStorageFree);
1435  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt);
1436 
1437  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress);
1438 
1440  ALPROTO_FTP, FTP_STATE_FINISHED, FTP_STATE_FINISHED);
1441 
1443  AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER,
1444  FTPDataParseRequest);
1445  AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOCLIENT,
1446  FTPDataParseResponse);
1447  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateAlloc, FTPDataStateFree);
1449  IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER | STREAM_TOCLIENT);
1450  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateTransactionFree);
1451 
1452  AppLayerParserRegisterGetTxFilesFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateGetTxFiles);
1453 
1454  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTx);
1455  AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxData);
1456  AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetStateData);
1457 
1458  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxCnt);
1459 
1460  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetAlstateProgress);
1461 
1463  ALPROTO_FTPDATA, FTPDATA_STATE_FINISHED, FTPDATA_STATE_FINISHED);
1464 
1465  AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info);
1466  AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_FTP, ftp_get_event_info_by_id);
1467 
1470 
1471  sbcfg.buf_size = 4096;
1472  sbcfg.Calloc = FTPCalloc;
1473  sbcfg.Realloc = FTPRealloc;
1474  sbcfg.Free = FTPFree;
1475 
1476  FTPParseMemcap();
1477  } else {
1478  SCLogInfo("Parser disabled for %s protocol. Protocol detection still on.", proto_name);
1479  }
1480 
1481  FTPSetMpmState();
1482 
1483 #ifdef UNITTESTS
1485 #endif
1486 }
1487 
1488 /*
1489  * \brief Returns the ending offset of the next line from a multi-line buffer.
1490  *
1491  * "Buffer" refers to a FTP response in a single buffer containing multiple lines.
1492  * Here, "next line" is defined as terminating on
1493  * - Newline character
1494  * - Null character
1495  *
1496  * \param buffer Contains zero or more characters.
1497  * \param len Size, in bytes, of buffer.
1498  *
1499  * \retval Offset from the start of buffer indicating the where the
1500  * next "line ends". The characters between the input buffer and this
1501  * value comprise the line.
1502  *
1503  * NULL is found first or a newline isn't found, then UINT16_MAX is returned.
1504  */
1505 uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len)
1506 {
1507  if (!buffer || *buffer == '\0') {
1508  return UINT16_MAX;
1509  }
1510 
1511  const char *c = strchr(buffer, '\n');
1512  return c == NULL ? len : (uint16_t)(c - buffer + 1);
1513 }
1514 
1515 bool EveFTPDataAddMetadata(const void *vtx, void *jb)
1516 {
1517  const FtpDataState *ftp_state = vtx;
1518  SCJbOpenObject(jb, "ftp_data");
1519 
1520  if (ftp_state->file_name) {
1521  SCJbSetStringFromBytes(jb, "filename", ftp_state->file_name, ftp_state->file_len);
1522  }
1523  switch (ftp_state->command) {
1524  case FTP_COMMAND_STOR:
1525  JB_SET_STRING(jb, "command", "STOR");
1526  break;
1527  case FTP_COMMAND_APPE:
1528  JB_SET_STRING(jb, "command", "APPE");
1529  break;
1530  case FTP_COMMAND_STOU:
1531  JB_SET_STRING(jb, "command", "STOU");
1532  break;
1533  case FTP_COMMAND_RETR:
1534  JB_SET_STRING(jb, "command", "RETR");
1535  break;
1536  case FTP_COMMAND_NLST:
1537  JB_SET_STRING(jb, "command", "NLST");
1538  break;
1539  case FTP_COMMAND_LIST:
1540  JB_SET_STRING(jb, "command", "LIST");
1541  break;
1542  case FTP_COMMAND_MLSD:
1543  JB_SET_STRING(jb, "command", "MLSD");
1544  break;
1545  default:
1546  break;
1547  }
1548  SCJbClose(jb);
1549  return true;
1550 }
1551 
1552 /**
1553  * \brief Free memory allocated for global FTP parser state.
1554  */
1556 {
1557  FTPFreeMpmState();
1558 }
1559 
1560 /* UNITTESTS */
1561 #ifdef UNITTESTS
1562 #include "flow-util.h"
1563 #include "stream-tcp.h"
1564 
1565 /** \test Send a get request in one chunk. */
1566 static int FTPParserTest01(void)
1567 {
1568  Flow f;
1569  uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
1570  uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
1571  TcpSession ssn;
1573 
1574  memset(&f, 0, sizeof(f));
1575  memset(&ssn, 0, sizeof(ssn));
1576 
1577  f.protoctx = (void *)&ssn;
1578  f.proto = IPPROTO_TCP;
1579  f.alproto = ALPROTO_FTP;
1580 
1581  StreamTcpInitConfig(true);
1582 
1583  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1584  STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
1585  FAIL_IF(r != 0);
1586 
1587  FtpState *ftp_state = f.alstate;
1588  FAIL_IF_NULL(ftp_state);
1589  FAIL_IF(ftp_state->command != FTP_COMMAND_PORT);
1590 
1591  FLOW_DESTROY(&f);
1593  StreamTcpFreeConfig(true);
1594  PASS;
1595 }
1596 
1597 /** \test Supply RETR without a filename */
1598 static int FTPParserTest11(void)
1599 {
1600  Flow f;
1601  uint8_t ftpbuf1[] = "PORT 192,168,1,1,0,80\r\n";
1602  uint8_t ftpbuf2[] = "RETR\r\n";
1603  uint8_t ftpbuf3[] = "227 OK\r\n";
1604  TcpSession ssn;
1605 
1607 
1608  memset(&f, 0, sizeof(f));
1609  memset(&ssn, 0, sizeof(ssn));
1610 
1611  f.protoctx = (void *)&ssn;
1612  f.proto = IPPROTO_TCP;
1613  f.alproto = ALPROTO_FTP;
1614 
1615  StreamTcpInitConfig(true);
1616 
1617  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1618  STREAM_TOSERVER | STREAM_START, ftpbuf1,
1619  sizeof(ftpbuf1) - 1);
1620  FAIL_IF(r != 0);
1621 
1622  /* Response */
1624  STREAM_TOCLIENT,
1625  ftpbuf3,
1626  sizeof(ftpbuf3) - 1);
1627  FAIL_IF(r != 0);
1628 
1630  STREAM_TOSERVER, ftpbuf2,
1631  sizeof(ftpbuf2) - 1);
1632  FAIL_IF(r != 0);
1633 
1634  FtpState *ftp_state = f.alstate;
1635  FAIL_IF_NULL(ftp_state);
1636 
1637  FAIL_IF(ftp_state->command != FTP_COMMAND_RETR);
1638 
1639  FLOW_DESTROY(&f);
1641  StreamTcpFreeConfig(true);
1642  PASS;
1643 }
1644 
1645 /** \test Supply STOR without a filename */
1646 static int FTPParserTest12(void)
1647 {
1648  Flow f;
1649  uint8_t ftpbuf1[] = "PORT 192,168,1,1,0,80\r\n";
1650  uint8_t ftpbuf2[] = "STOR\r\n";
1651  uint8_t ftpbuf3[] = "227 OK\r\n";
1652  TcpSession ssn;
1653 
1655 
1656  memset(&f, 0, sizeof(f));
1657  memset(&ssn, 0, sizeof(ssn));
1658 
1659  f.protoctx = (void *)&ssn;
1660  f.proto = IPPROTO_TCP;
1661  f.alproto = ALPROTO_FTP;
1662 
1663  StreamTcpInitConfig(true);
1664 
1665  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1666  STREAM_TOSERVER | STREAM_START, ftpbuf1,
1667  sizeof(ftpbuf1) - 1);
1668  FAIL_IF(r != 0);
1669 
1670  /* Response */
1672  STREAM_TOCLIENT,
1673  ftpbuf3,
1674  sizeof(ftpbuf3) - 1);
1675  FAIL_IF(r != 0);
1676 
1678  STREAM_TOSERVER, ftpbuf2,
1679  sizeof(ftpbuf2) - 1);
1680  FAIL_IF(r != 0);
1681 
1682  FtpState *ftp_state = f.alstate;
1683  FAIL_IF_NULL(ftp_state);
1684 
1685  FAIL_IF(ftp_state->command != FTP_COMMAND_STOR);
1686 
1687  FLOW_DESTROY(&f);
1689  StreamTcpFreeConfig(true);
1690  PASS;
1691 }
1692 
1693 /** \test A command padded with trailing whitespace must leave the memuse
1694  * counter where it found it, and the padding must not be kept. */
1695 static int FTPParserTest13(void)
1696 {
1697  Flow f;
1698  uint8_t ftpbuf[] = "USER anonymous \r\n";
1699  const char expected[] = "USER anonymous";
1700  TcpSession ssn;
1701 
1703 
1704  memset(&f, 0, sizeof(f));
1705  memset(&ssn, 0, sizeof(ssn));
1706 
1707  f.protoctx = (void *)&ssn;
1708  f.proto = IPPROTO_TCP;
1709  f.alproto = ALPROTO_FTP;
1710 
1711  StreamTcpInitConfig(true);
1712 
1713  const uint64_t memuse = SC_ATOMIC_GET(ftp_memuse);
1714 
1715  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER | STREAM_EOF,
1716  ftpbuf, sizeof(ftpbuf) - 1);
1717  FAIL_IF(r != 0);
1718 
1719  FtpState *ftp_state = f.alstate;
1720  FAIL_IF_NULL(ftp_state);
1721  FAIL_IF(ftp_state->command != FTP_COMMAND_USER);
1722 
1723  /* the request is what the transaction is freed with, so it has to be the
1724  * stripped line: the padding is neither stored nor accounted */
1725  FTPTransaction *tx = TAILQ_FIRST(&ftp_state->tx_list);
1726  FAIL_IF_NULL(tx);
1727  FAIL_IF_NULL(tx->request);
1728  FAIL_IF(tx->request_length != sizeof(expected));
1729  FAIL_IF(memcmp(tx->request, expected, sizeof(expected)) != 0);
1730 
1731  FLOW_DESTROY(&f);
1732  FAIL_IF(SC_ATOMIC_GET(ftp_memuse) != memuse);
1733 
1735  StreamTcpFreeConfig(true);
1736  PASS;
1737 }
1738 
1739 /** \test The padding is stripped from the line itself, not just from the copy
1740  * kept on the transaction: what follows the copy has to see the
1741  * shortened line. */
1742 static int FTPParserTest14(void)
1743 {
1744  Flow f;
1745  uint8_t ftpbuf1[] = "PORT 192,168,1,1,0,80 \r\n";
1746  uint8_t ftpbuf2[] = "227 OK\r\n";
1747  const char expected[] = "PORT 192,168,1,1,0,80";
1748  TcpSession ssn;
1749 
1751 
1752  memset(&f, 0, sizeof(f));
1753  memset(&ssn, 0, sizeof(ssn));
1754 
1755  f.protoctx = (void *)&ssn;
1756  f.proto = IPPROTO_TCP;
1757  f.alproto = ALPROTO_FTP;
1758 
1759  StreamTcpInitConfig(true);
1760 
1761  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER | STREAM_START,
1762  ftpbuf1, sizeof(ftpbuf1) - 1);
1763  FAIL_IF(r != 0);
1764 
1765  FtpState *ftp_state = f.alstate;
1766  FAIL_IF_NULL(ftp_state);
1767  FAIL_IF(ftp_state->command != FTP_COMMAND_PORT);
1768 
1769  /* the port line is taken from the line, so the padding must be gone */
1770  FAIL_IF(ftp_state->port_line_len != sizeof(expected) - 1);
1771  FAIL_IF(memcmp(ftp_state->port_line, expected, sizeof(expected) - 1) != 0);
1772 
1773  r = AppLayerParserParse(
1774  NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOCLIENT, ftpbuf2, sizeof(ftpbuf2) - 1);
1775  FAIL_IF(r != 0);
1776 
1777  FAIL_IF(ftp_state->dyn_port != 80);
1778 
1779  FLOW_DESTROY(&f);
1781  StreamTcpFreeConfig(true);
1782  PASS;
1783 }
1784 
1785 /** \test Preliminary responses past the limit are not stored */
1786 static int FTPParserTest15(void)
1787 {
1788  Flow f;
1789  uint8_t ftpbuf1[] = "USER anonymous\r\n";
1790  const uint8_t ppr[] = "150 \r\n";
1791  const uint8_t final[] = "226 done\r\n";
1792  const uint32_t nppr = 100;
1793  TcpSession ssn;
1794 
1795  uint32_t ftplen2 = nppr * (sizeof(ppr) - 1) + sizeof(final) - 1;
1796  uint8_t *ftpbuf2 = SCMalloc(ftplen2);
1797  FAIL_IF_NULL(ftpbuf2);
1798  for (uint32_t i = 0; i < nppr; i++) {
1799  memcpy(ftpbuf2 + i * (sizeof(ppr) - 1), ppr, sizeof(ppr) - 1);
1800  }
1801  memcpy(ftpbuf2 + nppr * (sizeof(ppr) - 1), final, sizeof(final) - 1);
1802 
1804 
1805  memset(&f, 0, sizeof(f));
1806  memset(&ssn, 0, sizeof(ssn));
1807 
1808  f.protoctx = (void *)&ssn;
1809  f.proto = IPPROTO_TCP;
1810  f.alproto = ALPROTO_FTP;
1811 
1812  StreamTcpInitConfig(true);
1813 
1814  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER | STREAM_START,
1815  ftpbuf1, sizeof(ftpbuf1) - 1);
1816  FAIL_IF(r != 0);
1817 
1818  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOCLIENT, ftpbuf2, ftplen2);
1819  FAIL_IF(r != 0);
1820 
1821  FtpState *ftp_state = f.alstate;
1822  FAIL_IF_NULL(ftp_state);
1823  FTPTransaction *tx = TAILQ_FIRST(&ftp_state->tx_list);
1824  FAIL_IF_NULL(tx);
1825  FAIL_IF(TAILQ_NEXT(tx, next) != NULL);
1826 
1827  uint32_t cnt = 0;
1828  FTPResponseWrapper *wrapper;
1829  TAILQ_FOREACH (wrapper, &tx->response_list, next) {
1830  cnt++;
1831  }
1832  /* the preliminary responses up to the limit, then the final response */
1833  FAIL_IF(cnt != ftp_max_preliminary_responses + 1);
1834  FAIL_IF(tx->preliminary_response_cnt != ftp_max_preliminary_responses);
1835  FAIL_IF(!AppLayerDecoderEventsIsEventSet(
1836  tx->tx_data.events, FtpEventTooManyPreliminaryResponses));
1837  FAIL_IF(!tx->done);
1838 
1839  SCFree(ftpbuf2);
1840  FLOW_DESTROY(&f);
1842  StreamTcpFreeConfig(true);
1843  PASS;
1844 }
1845 
1846 /** \test Multi-line replies are not limited */
1847 static int FTPParserTest16(void)
1848 {
1849  Flow f;
1850  uint8_t ftpbuf1[] = "FEAT\r\n";
1851  const uint8_t first[] = "211-Features:\r\n";
1852  const uint8_t mid[] = "211-SIZE\r\n";
1853  const uint8_t last[] = "211 End\r\n";
1854  const uint32_t nmid = 100;
1855  TcpSession ssn;
1856 
1857  uint32_t ftplen2 = sizeof(first) - 1 + nmid * (sizeof(mid) - 1) + sizeof(last) - 1;
1858  uint8_t *ftpbuf2 = SCMalloc(ftplen2);
1859  FAIL_IF_NULL(ftpbuf2);
1860  uint8_t *p = ftpbuf2;
1861  memcpy(p, first, sizeof(first) - 1);
1862  p += sizeof(first) - 1;
1863  for (uint32_t i = 0; i < nmid; i++) {
1864  memcpy(p, mid, sizeof(mid) - 1);
1865  p += sizeof(mid) - 1;
1866  }
1867  memcpy(p, last, sizeof(last) - 1);
1868 
1870 
1871  memset(&f, 0, sizeof(f));
1872  memset(&ssn, 0, sizeof(ssn));
1873 
1874  f.protoctx = (void *)&ssn;
1875  f.proto = IPPROTO_TCP;
1876  f.alproto = ALPROTO_FTP;
1877 
1878  StreamTcpInitConfig(true);
1879 
1880  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER | STREAM_START,
1881  ftpbuf1, sizeof(ftpbuf1) - 1);
1882  FAIL_IF(r != 0);
1883 
1884  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOCLIENT, ftpbuf2, ftplen2);
1885  FAIL_IF(r != 0);
1886 
1887  FtpState *ftp_state = f.alstate;
1888  FAIL_IF_NULL(ftp_state);
1889  FTPTransaction *tx = TAILQ_FIRST(&ftp_state->tx_list);
1890  FAIL_IF_NULL(tx);
1891  FAIL_IF(TAILQ_NEXT(tx, next) != NULL);
1892 
1893  uint32_t cnt = 0;
1894  FTPResponseWrapper *wrapper;
1895  TAILQ_FOREACH (wrapper, &tx->response_list, next) {
1896  cnt++;
1897  }
1898  FAIL_IF(cnt != nmid + 2);
1899  FAIL_IF(AppLayerDecoderEventsIsEventSet(
1900  tx->tx_data.events, FtpEventTooManyPreliminaryResponses));
1901  FAIL_IF(!tx->done);
1902 
1903  SCFree(ftpbuf2);
1904  FLOW_DESTROY(&f);
1906  StreamTcpFreeConfig(true);
1907  PASS;
1908 }
1909 
1910 /** \test Truncated preliminary responses past the limit raise the too long
1911  * event once */
1912 static int FTPParserTest17(void)
1913 {
1914  Flow f;
1915  uint8_t ftpbuf1[] = "USER anonymous\r\n";
1916  const uint8_t ppr[] = "150 \r\n";
1917  const uint32_t nlong = 3;
1918  TcpSession ssn;
1919 
1920  /* the limit in short replies, then replies longer than the line limit */
1921  const uint32_t long_len = ftp_max_line_len + 16;
1922  uint32_t ftplen2 = ftp_max_preliminary_responses * (sizeof(ppr) - 1) + nlong * (long_len + 2);
1923  uint8_t *ftpbuf2 = SCMalloc(ftplen2);
1924  FAIL_IF_NULL(ftpbuf2);
1925  uint8_t *p = ftpbuf2;
1926  for (uint32_t i = 0; i < ftp_max_preliminary_responses; i++) {
1927  memcpy(p, ppr, sizeof(ppr) - 1);
1928  p += sizeof(ppr) - 1;
1929  }
1930  for (uint32_t i = 0; i < nlong; i++) {
1931  memcpy(p, "150 ", 4);
1932  memset(p + 4, 'a', long_len - 4);
1933  p += long_len;
1934  memcpy(p, "\r\n", 2);
1935  p += 2;
1936  }
1937 
1939 
1940  memset(&f, 0, sizeof(f));
1941  memset(&ssn, 0, sizeof(ssn));
1942 
1943  f.protoctx = (void *)&ssn;
1944  f.proto = IPPROTO_TCP;
1945  f.alproto = ALPROTO_FTP;
1946 
1947  StreamTcpInitConfig(true);
1948 
1949  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER | STREAM_START,
1950  ftpbuf1, sizeof(ftpbuf1) - 1);
1951  FAIL_IF(r != 0);
1952 
1953  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOCLIENT, ftpbuf2, ftplen2);
1954  FAIL_IF(r != 0);
1955 
1956  FtpState *ftp_state = f.alstate;
1957  FAIL_IF_NULL(ftp_state);
1958  FTPTransaction *tx = TAILQ_FIRST(&ftp_state->tx_list);
1959  FAIL_IF_NULL(tx);
1960 
1961  FAIL_IF(tx->preliminary_response_cnt != ftp_max_preliminary_responses);
1962  FAIL_IF(!AppLayerDecoderEventsIsEventSet(
1963  tx->tx_data.events, FtpEventTooManyPreliminaryResponses));
1964  FAIL_IF(!AppLayerDecoderEventsIsEventSet(tx->tx_data.events, FtpEventResponseCommandTooLong));
1965  /* each event once, however many lines were dropped */
1966  FAIL_IF(tx->tx_data.events->cnt != 2);
1967 
1968  SCFree(ftpbuf2);
1969  FLOW_DESTROY(&f);
1971  StreamTcpFreeConfig(true);
1972  PASS;
1973 }
1974 
1975 /** \test With a memcap set, reducing a buffer's size succeeds and lowers memuse */
1976 static int FTPParserTest18(void)
1977 {
1978  const uint64_t saved_memcap = ftp_config_memcap;
1979  ftp_config_memcap = 1024 * 1024;
1980 
1981  void *ptr = FTPCalloc(1, 1024);
1982  FAIL_IF_NULL(ptr);
1983  const uint64_t memuse = SC_ATOMIC_GET(ftp_memuse);
1984 
1985  void *smaller = FTPRealloc(ptr, 1024, 512);
1986  FAIL_IF_NULL(smaller);
1987  FAIL_IF(SC_ATOMIC_GET(ftp_memuse) != memuse - 512);
1988 
1989  FTPFree(smaller, 512);
1990  ftp_config_memcap = saved_memcap;
1991  PASS;
1992 }
1993 #endif /* UNITTESTS */
1994 
1996 {
1997 #ifdef UNITTESTS
1998  UtRegisterTest("FTPParserTest01", FTPParserTest01);
1999  UtRegisterTest("FTPParserTest11", FTPParserTest11);
2000  UtRegisterTest("FTPParserTest12", FTPParserTest12);
2001  UtRegisterTest("FTPParserTest13", FTPParserTest13);
2002  UtRegisterTest("FTPParserTest14", FTPParserTest14);
2003  UtRegisterTest("FTPParserTest15", FTPParserTest15);
2004  UtRegisterTest("FTPParserTest16", FTPParserTest16);
2005  UtRegisterTest("FTPParserTest17", FTPParserTest17);
2006  UtRegisterTest("FTPParserTest18", FTPParserTest18);
2007 #endif /* UNITTESTS */
2008 }
FTPTransaction_::request_truncated
bool request_truncated
Definition: app-layer-ftp.h:73
PmqReset
void PmqReset(PrefilterRuleStore *pmq)
Reset a Pmq for reusage. Meant to be called after a single search.
Definition: util-prefilter.c:102
StreamSlice
Definition: app-layer-parser.h:126
AppLayerParserRegisterGetStateProgressFunc
void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto, int(*StateGetProgress)(void *alstate, uint8_t direction))
Definition: app-layer-parser.c:544
len
uint8_t len
Definition: app-layer-dnp3.h:2
AppLayerTxData::flags
uint8_t flags
Definition: app-layer-parser.h:182
SCAppLayerParserStateIssetFlag
uint16_t SCAppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint16_t flag)
Definition: app-layer-parser.c:2079
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
FTPTransaction_::request
uint8_t * request
Definition: app-layer-ftp.h:72
AppLayerGetTxIterState::ptr
void * ptr
Definition: app-layer-parser.h:150
FTPThreadCtx_
Definition: app-layer-ftp.c:49
StreamingBufferConfig_::buf_size
uint32_t buf_size
Definition: util-streaming-buffer.h:66
AppLayerParserRegisterLocalStorageFunc
void AppLayerParserRegisterLocalStorageFunc(uint8_t ipproto, AppProto alproto, void *(*LocalStorageAlloc)(void), void(*LocalStorageFree)(void *))
Definition: app-layer-parser.c:504
SCFileFlowFlagsToFlags
uint16_t SCFileFlowFlagsToFlags(const uint16_t flow_file_flags, uint8_t direction)
Definition: util-file.c:215
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
FtpState_::active
bool active
Definition: app-layer-ftp.h:92
flow-util.h
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
FileContainerAlloc
FileContainer * FileContainerAlloc(void)
allocate a FileContainer
Definition: util-file.c:479
FtpDataState_::state
uint8_t state
Definition: app-layer-ftp.h:120
StreamingBufferConfig_::Calloc
void *(* Calloc)(size_t n, size_t size)
Definition: util-streaming-buffer.h:69
MpmThreadCtx_
Definition: util-mpm.h:62
stream-tcp.h
FtpState
struct FtpState_ FtpState
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
SCFlowGetStorageById
void * SCFlowGetStorageById(const Flow *f, SCFlowStorageId id)
Definition: flow-storage.c:37
PrefilterRuleStore_
structure for storing potential rule matches
Definition: util-prefilter.h:34
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
SCAppLayerTxDataCleanup
void SCAppLayerTxDataCleanup(AppLayerTxData *txd)
Definition: app-layer-parser.c:831
FtpCommandInfo_
Definition: app-layer-ftp.h:58
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
Flow_::proto
uint8_t proto
Definition: flow.h:381
AppProto
uint16_t AppProto
Definition: app-layer-protos.h:87
FtpState_::command
FtpRequestCommand command
Definition: app-layer-ftp.h:101
ALPROTO_POP3
@ ALPROTO_POP3
Definition: app-layer-protos.h:71
SCAppLayerProtoDetectPMRegisterPatternCI
int SCAppLayerProtoDetectPMRegisterPatternCI(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction)
Registers a case-insensitive pattern for protocol detection.
Definition: app-layer-detect-proto.c:1660
STREAMING_BUFFER_CONFIG_INITIALIZER
#define STREAMING_BUFFER_CONFIG_INITIALIZER
Definition: util-streaming-buffer.h:74
ssn
TcpSession ssn
Definition: fuzz_dataset.c:31
AppLayerStateData
Definition: app-layer-parser.h:155
FileContainerFree
void FileContainerFree(FileContainer *ffc, const StreamingBufferConfig *cfg)
Free a FileContainer.
Definition: util-file.c:515
Flow_
Flow data structure.
Definition: flow.h:359
FTPTransaction_::done
bool done
Definition: app-layer-ftp.h:79
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
AppLayerParserRegisterStateProgressCompletionStatus
void AppLayerParserRegisterStateProgressCompletionStatus(AppProto alproto, const int ts, const int tc)
Definition: app-layer-parser.c:592
FtpState_::current_line_truncated_tc
bool current_line_truncated_tc
Definition: app-layer-ftp.h:99
AppLayerParserRegisterTxFreeFunc
void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, void(*StateTransactionFree)(void *, uint64_t))
Definition: app-layer-parser.c:554
FTPTransaction_::tx_data
AppLayerTxData tx_data
Definition: app-layer-ftp.h:67
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
FtpDataState_::command
FtpRequestCommand command
Definition: app-layer-ftp.h:119
FTPMemuseGlobalCounter
uint64_t FTPMemuseGlobalCounter(void)
Definition: app-layer-ftp.c:92
AppLayerParserThreadCtxFree
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
Definition: app-layer-parser.c:364
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
rust.h
MIN
#define MIN(x, y)
Definition: suricata-common.h:413
FtpLineState_::buf
const uint8_t * buf
Definition: app-layer-ftp.h:39
ALPROTO_FTP
@ ALPROTO_FTP
Definition: app-layer-protos.h:37
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:122
p
Packet * p
Definition: fuzz_dataset.c:30
FtpDataState_::file_len
int16_t file_len
Definition: app-layer-ftp.h:118
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
FTPResponseWrapper_
Definition: app-layer-ftp.h:47
app-layer-ftp.h
Flow_::protoctx
void * protoctx
Definition: flow.h:438
AppLayerGetTxIterTuple::tx_ptr
void * tx_ptr
Definition: app-layer-parser.h:160
FtpDataState_::direction
uint8_t direction
Definition: app-layer-ftp.h:121
FtpInput_::len
int32_t len
Definition: app-layer-ftp.c:289
SC_ELIMIT
@ SC_ELIMIT
Definition: util-error.h:31
SC_ATOMIC_DECLARE
SC_ATOMIC_DECLARE(uint64_t, ftp_memuse)
SCAppLayerProtoDetectPMRegisterPatternCSwPP
int SCAppLayerProtoDetectPMRegisterPatternCSwPP(uint8_t ipproto, AppProto alproto, const char *pattern, uint16_t depth, uint16_t offset, uint8_t direction, ProbingParserFPtr PPFunc, uint16_t pp_min_depth, uint16_t pp_max_depth)
Definition: app-layer-detect-proto.c:1638
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
FTPParserCleanup
void FTPParserCleanup(void)
Free memory allocated for global FTP parser state.
Definition: app-layer-ftp.c:1555
SCAppLayerDecoderEventsSetEventRaw
void SCAppLayerDecoderEventsSetEventRaw(AppLayerDecoderEvents **sevents, uint8_t event)
Set an app layer decoder event.
Definition: app-layer-events.c:97
util-memcmp.h
SCAppLayerProtoDetectConfProtoDetectionEnabled
int SCAppLayerProtoDetectConfProtoDetectionEnabled(const char *ipproto, const char *alproto)
Given a protocol name, checks if proto detection is enabled in the conf file.
Definition: app-layer-detect-proto.c:1989
MpmInitCtx
void MpmInitCtx(MpmCtx *mpm_ctx, uint8_t matcher)
Definition: util-mpm.c:208
ftp_max_line_len
uint32_t ftp_max_line_len
Definition: app-layer-ftp.c:60
AppLayerResult
Definition: app-layer-parser.h:120
SCAppLayerParserTriggerRawStreamInspection
void SCAppLayerParserTriggerRawStreamInspection(Flow *f, int direction)
Definition: app-layer-parser.c:1787
FtpState_
Definition: app-layer-ftp.h:91
app-layer-expectation.h
app-layer-detect-proto.h
StreamTcpInitConfig
void StreamTcpInitConfig(bool)
To initialize the stream global configuration data.
Definition: stream-tcp.c:496
APP_LAYER_INCOMPLETE
#define APP_LAYER_INCOMPLETE(c, n)
Definition: app-layer-parser.h:70
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
MpmDestroyThreadCtx
void MpmDestroyThreadCtx(MpmThreadCtx *mpm_thread_ctx, const uint16_t matcher)
Definition: util-mpm.c:201
util-debug.h
JB_SET_STRING
#define JB_SET_STRING(jb, key, val)
Definition: rust.h:36
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
FtpState_::port_line
uint8_t * port_line
Definition: app-layer-ftp.h:105
AppLayerParserState_
Definition: app-layer-parser.c:160
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
util-error.h
AppLayerTxData
Definition: app-layer-parser.h:172
FTPThreadCtx_::pmq
PrefilterRuleStore * pmq
Definition: app-layer-ftp.c:51
SCAppLayerParserConfParserEnabled
int SCAppLayerParserConfParserEnabled(const char *ipproto, const char *alproto_name)
check if a parser is enabled in the config Returns enabled always if: were running unittests
Definition: app-layer-parser.c:385
SC_FILENAME_MAX
#define SC_FILENAME_MAX
Definition: util-file.h:129
SCAppLayerParserRegisterLogger
void SCAppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
Definition: app-layer-parser.c:535
FtpDataState_::initialized
bool initialized
Definition: app-layer-ftp.h:124
FtpState_::curr_tx
FTPTransaction * curr_tx
Definition: app-layer-ftp.h:94
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
FtpDataState_::state_data
AppLayerStateData state_data
Definition: app-layer-ftp.h:123
alp_tctx
AppLayerParserThreadCtx * alp_tctx
Definition: fuzz_applayerparserparse.c:24
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
AppLayerParserRegisterStateFuncs
void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, void *(*StateAlloc)(void *, AppProto), void(*StateFree)(void *))
Definition: app-layer-parser.c:493
app-layer-parser.h
Flow_::todstbytecnt
uint64_t todstbytecnt
Definition: flow.h:502
SCFlowFreeStorageById
void SCFlowFreeStorageById(Flow *f, SCFlowStorageId id)
Definition: flow-storage.c:47
AppLayerDecoderEvents_::cnt
uint8_t cnt
Definition: app-layer-events.h:37
AppLayerParserRegisterGetEventInfo
void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfo)(const char *event_name, uint8_t *event_id, AppLayerEventType *event_type))
Definition: app-layer-parser.c:671
FtpState_::state_data
AppLayerStateData state_data
Definition: app-layer-ftp.h:109
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
FTPParserRegisterTests
void FTPParserRegisterTests(void)
Definition: app-layer-ftp.c:1995
RegisterFTPParsers
void RegisterFTPParsers(void)
Definition: app-layer-ftp.c:1404
FTPTransaction_::command_descriptor
FtpCommandInfo command_descriptor
Definition: app-layer-ftp.h:76
AppLayerParserRegisterProtocolUnittests
void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, void(*RegisterUnittests)(void))
Definition: app-layer-parser.c:2090
AppLayerExpectationCreate
int AppLayerExpectationCreate(Flow *f, int direction, Port src, Port dst, AppProto alproto, void *data)
Definition: app-layer-expectation.c:218
AppLayerGetTxIterState
Definition: app-layer-parser.h:148
FtpCommandInfo_::command_code
FtpRequestCommand command_code
Definition: app-layer-ftp.h:60
ALPROTO_IMAP
@ ALPROTO_IMAP
Definition: app-layer-protos.h:41
FtpLineState_::truncated
bool truncated
Definition: app-layer-ftp.h:44
ftp_config_memcap
uint64_t ftp_config_memcap
Definition: app-layer-ftp.c:58
AppLayerRegisterExpectationProto
void AppLayerRegisterExpectationProto(uint8_t proto, AppProto alproto)
Definition: app-layer-detect-proto.c:2165
FTPResponseWrapper_::response
FTPResponseLine * response
Definition: app-layer-ftp.h:48
FileOpenFileWithId
int FileOpenFileWithId(FileContainer *ffc, const StreamingBufferConfig *sbcfg, uint32_t track_id, const uint8_t *name, uint16_t name_len, const uint8_t *data, uint32_t data_len, uint16_t flags)
Open a new File.
Definition: util-file.c:966
AppLayerParserRegisterGetTxFilesFunc
void AppLayerParserRegisterGetTxFilesFunc(uint8_t ipproto, AppProto alproto, AppLayerGetFileState(*GetTxFiles)(void *, uint8_t))
Definition: app-layer-parser.c:516
AppLayerProtoDetectRegisterProtocol
void AppLayerProtoDetectRegisterProtocol(AppProto alproto, const char *alproto_name)
Registers a protocol for protocol detection phase.
Definition: app-layer-detect-proto.c:1769
AppLayerGetTxIterTuple
Definition: app-layer-parser.h:159
FtpDataState
struct FtpDataState_ FtpDataState
FileAppendData
int FileAppendData(FileContainer *ffc, const StreamingBufferConfig *sbcfg, const uint8_t *data, uint32_t data_len)
Store/handle a chunk of file data in the File structure The last file in the FileContainer will be us...
Definition: util-file.c:765
FTPMemcapGlobalCounter
uint64_t FTPMemcapGlobalCounter(void)
Definition: app-layer-ftp.c:98
MpmTableElmt_::Search
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition: util-mpm.h:200
FtpInput
struct FtpInput_ FtpInput
FtpDataState_::tx_data
AppLayerTxData tx_data
Definition: app-layer-ftp.h:122
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
AppLayerParserRegisterParser
int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, uint8_t direction, AppLayerParserFPtr Parser)
Register app layer parser for the protocol.
Definition: app-layer-parser.c:460
FtpInput_::orig_len
int32_t orig_len
Definition: app-layer-ftp.c:290
SCRealloc
#define SCRealloc(ptr, sz)
Definition: util-mem.h:50
SCAppLayerProtoDetectPPRegister
void SCAppLayerProtoDetectPPRegister(uint8_t ipproto, const char *portstr, AppProto alproto, uint16_t min_depth, uint16_t max_depth, uint8_t direction, ProbingParserFPtr ProbingParser1, ProbingParserFPtr ProbingParser2)
register parser at a port
Definition: app-layer-detect-proto.c:1528
AppLayerParserThreadCtxAlloc
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol's parser thread context.
Definition: app-layer-parser.c:337
AppLayerParserRegisterGetTx
void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, void *(StateGetTx)(void *alstate, uint64_t tx_id))
Definition: app-layer-parser.c:574
util-prefilter.h
APP_LAYER_OK
#define APP_LAYER_OK
Definition: app-layer-parser.h:58
flow-storage.h
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
SCReturnStruct
#define SCReturnStruct(x)
Definition: util-debug.h:304
FTPTransaction_::request_length
uint32_t request_length
Definition: app-layer-ftp.h:70
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
util-mpm.h
StreamTcpFreeConfig
void StreamTcpFreeConfig(bool quiet)
Definition: stream-tcp.c:864
flags
uint8_t flags
Definition: decode-gre.h:0
AppLayerParserParse
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, const uint8_t *input, uint32_t input_len)
Definition: app-layer-parser.c:1528
AppLayerGetFileState
Definition: util-file.h:44
FtpLineState_
Definition: app-layer-ftp.h:36
suricata-common.h
AppLayerGetFileState::fc
FileContainer * fc
Definition: util-file.h:45
util-streaming-buffer.h
ftp_config_maxtx
uint32_t ftp_config_maxtx
Definition: app-layer-ftp.c:59
AppLayerTxData::updated_tc
bool updated_tc
Definition: app-layer-parser.h:179
SCAppLayerParserRegisterParserAcceptableDataDirection
void SCAppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppProto alproto, uint8_t direction)
Definition: app-layer-parser.c:472
FtpState_::port_line_len
uint32_t port_line_len
Definition: app-layer-ftp.h:103
FtpState_::dyn_port
uint16_t dyn_port
Definition: app-layer-ftp.h:107
AppLayerTxData::files_opened
uint32_t files_opened
track file open/logs so we can know how long to keep the tx
Definition: app-layer-parser.h:188
TAILQ_NEXT
#define TAILQ_NEXT(elm, field)
Definition: queue.h:307
ALPROTO_FTPDATA
@ ALPROTO_FTPDATA
Definition: app-layer-protos.h:53
AppLayerParserRegisterStateDataFunc
void AppLayerParserRegisterStateDataFunc(uint8_t ipproto, AppProto alproto, AppLayerStateData *(*GetStateData)(void *state))
Definition: app-layer-parser.c:692
AppLayerParserRegisterTxDataFunc
void AppLayerParserRegisterTxDataFunc(uint8_t ipproto, AppProto alproto, AppLayerTxData *(*GetTxData)(void *tx))
Definition: app-layer-parser.c:682
Flow_::parent_id
int64_t parent_id
Definition: flow.h:435
SCAppLayerRequestProtocolTLSUpgrade
bool SCAppLayerRequestProtocolTLSUpgrade(Flow *f)
request applayer to wrap up this protocol and rerun protocol detection with expectation of TLS....
Definition: app-layer-detect-proto.c:1860
FtpDataState_
Definition: app-layer-ftp.h:113
app-layer-events.h
util-validate.h
StreamingBufferConfig_
Definition: util-streaming-buffer.h:65
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
FTPSetMemcap
int FTPSetMemcap(uint64_t size)
Definition: app-layer-ftp.c:104
FtpLineState_::delim_len
uint8_t delim_len
Definition: app-layer-ftp.h:41
AppLayerParserRegisterGetTxIterator
void AppLayerParserRegisterGetTxIterator(uint8_t ipproto, AppProto alproto, AppLayerGetTxIteratorFunc Func)
Definition: app-layer-parser.c:584
MpmTableElmt_::Prepare
int(* Prepare)(MpmConfig *, struct MpmCtx_ *)
Definition: util-mpm.h:193
MpmTableElmt_::DestroyCtx
void(* DestroyCtx)(struct MpmCtx_ *)
Definition: util-mpm.h:172
AppLayerResult::status
int32_t status
Definition: app-layer-parser.h:121
FileCloseFile
int FileCloseFile(FileContainer *ffc, const StreamingBufferConfig *sbcfg, const uint8_t *data, uint32_t data_len, uint16_t flags)
Close a File.
Definition: util-file.c:1050
SCFree
#define SCFree(p)
Definition: util-mem.h:61
Flow_::alproto_ts
AppProto alproto_ts
Definition: flow.h:456
Flow_::alstate
void * alstate
Definition: flow.h:484
SCAppLayerProtoDetectPPParseConfPorts
int SCAppLayerProtoDetectPPParseConfPorts(const char *ipproto_name, uint8_t ipproto, const char *alproto_name, AppProto alproto, uint16_t min_depth, uint16_t max_depth, ProbingParserFPtr ProbingParserTs, ProbingParserFPtr ProbingParserTc)
Definition: app-layer-detect-proto.c:1564
StreamingBufferConfig_::Free
void(* Free)(void *ptr, size_t size)
Definition: util-streaming-buffer.h:71
FtpCommandInfo_::command_index
uint8_t command_index
Definition: app-layer-ftp.h:59
FtpState_::tx_cnt
uint64_t tx_cnt
Definition: app-layer-ftp.h:96
FtpState_::current_line_truncated_ts
bool current_line_truncated_ts
Definition: app-layer-ftp.h:98
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
MpmInitThreadCtx
void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, MpmCtx *mpm_ctx, uint16_t matcher)
Definition: util-mpm.c:194
ALPROTO_UNKNOWN
@ ALPROTO_UNKNOWN
Definition: app-layer-protos.h:29
ALPROTO_FAILED
@ ALPROTO_FAILED
Definition: app-layer-protos.h:33
EveFTPDataAddMetadata
bool EveFTPDataAddMetadata(const void *vtx, void *jb)
Definition: app-layer-ftp.c:1515
mpm_table
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition: util-mpm.c:46
AppLayerExpectationGetFlowId
SCFlowStorageId AppLayerExpectationGetFlowId(void)
Definition: app-layer-expectation.c:289
app-layer-protos.h
FtpLineState_::len
uint32_t len
Definition: app-layer-ftp.h:40
AppLayerParserRegisterGetTxCnt
void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, uint64_t(*StateGetTxCnt)(void *alstate))
Definition: app-layer-parser.c:564
APP_LAYER_ERROR
#define APP_LAYER_ERROR
Definition: app-layer-parser.h:62
FtpState_::port_line_size
uint32_t port_line_size
Definition: app-layer-ftp.h:104
PmqFree
void PmqFree(PrefilterRuleStore *pmq)
Cleanup and free a Pmq.
Definition: util-prefilter.c:126
FTP_MPM
#define FTP_MPM
Definition: app-layer-ftp.c:54
FTPTransaction_::tx_id
uint64_t tx_id
Definition: app-layer-ftp.h:65
FTPTransaction_::dyn_port
uint16_t dyn_port
Definition: app-layer-ftp.h:78
FTPTransaction_::active
bool active
Definition: app-layer-ftp.h:80
AppLayerTxData::events
AppLayerDecoderEvents * events
Definition: app-layer-parser.h:224
AppLayerParserRegisterGetEventInfoById
void AppLayerParserRegisterGetEventInfoById(uint8_t ipproto, AppProto alproto, int(*StateGetEventInfoById)(uint8_t event_id, const char **event_name, AppLayerEventType *event_type))
Definition: app-layer-parser.c:607
FtpDataState_::files
FileContainer * files
Definition: app-layer-ftp.h:116
likely
#define likely(expr)
Definition: util-optimize.h:32
AppLayerParserThreadCtx_
Definition: app-layer-parser.c:72
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
MpmCtx_
Definition: util-mpm.h:111
TcpSession_
Definition: stream-tcp-private.h:290
JsonGetNextLineFromBuffer
uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len)
Definition: app-layer-ftp.c:1505
AppLayerTxData::file_flags
uint16_t file_flags
Definition: app-layer-parser.h:192
flow.h
FTPTransaction_
Definition: app-layer-ftp.h:63
FTPThreadCtx
struct FTPThreadCtx_ FTPThreadCtx
Flow_::alproto_tc
AppProto alproto_tc
Definition: flow.h:457
FtpInput_::buf
const uint8_t * buf
Definition: app-layer-ftp.c:287
Flow_::alproto
AppProto alproto
application level protocol
Definition: flow.h:455
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
SCMemcmp
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:289
SCMutex
#define SCMutex
Definition: threads-debug.h:114
FTPThreadCtx_::ftp_mpm_thread_ctx
MpmThreadCtx * ftp_mpm_thread_ctx
Definition: app-layer-ftp.c:50
SCLogDebugEnabled
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition: util-debug.c:775
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
FtpInput_::consumed
int32_t consumed
Definition: app-layer-ftp.c:288
FLOW_DESTROY
#define FLOW_DESTROY(f)
Definition: flow-util.h:119
PmqSetup
int PmqSetup(PrefilterRuleStore *pmq)
Setup a pmq.
Definition: util-prefilter.c:37
AppLayerStateData::file_flags
uint16_t file_flags
Definition: app-layer-parser.h:156
StreamingBufferConfig_::Realloc
void *(* Realloc)(void *ptr, size_t orig_size, size_t size)
Definition: util-streaming-buffer.h:70
FTPTransaction_::preliminary_response_cnt
uint32_t preliminary_response_cnt
Definition: app-layer-ftp.h:71
FtpDataState_::file_name
uint8_t * file_name
Definition: app-layer-ftp.h:115
FtpInput_
Definition: app-layer-ftp.c:286
AppLayerGetTxIterState::un
union AppLayerGetTxIterState::@7 un
AppLayerTxData::file_tx
uint8_t file_tx
Definition: app-layer-parser.h:199
AppLayerTxData::updated_ts
bool updated_ts
Definition: app-layer-parser.h:180
PrefilterRuleStore_::rule_id_array
SigIntId * rule_id_array
Definition: util-prefilter.h:38
f
Flow f
Definition: fuzz_dataset.c:32