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