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