]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-ftp.c
HTTP new parser warning for Ambiguous C-L
[people/ms/suricata.git] / src / app-layer-ftp.c
CommitLineData
b0a69344 1/* Copyright (C) 2007-2017 Open Information Security Foundation
ce019275
WM
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.
f2f9b832 6 *
ce019275
WM
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/**
f2f9b832 19 * \file
ce019275 20 *
f2f9b832 21 * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
b0a69344 22 * \author Eric Leblond <eric@regit.org>
1930b1f5 23 * \author Jeff Lucovsky <jeff@lucovsky.org>
f2f9b832
PR
24 *
25 * App Layer Parser for FTP
26 */
27
28#include "suricata-common.h"
29#include "debug.h"
30#include "decode.h"
31#include "threads.h"
32
33#include "util-print.h"
34#include "util-pool.h"
35
cc76aa4b 36#include "flow-util.h"
b0a69344 37#include "flow-storage.h"
cc76aa4b 38
0e4235cc
WM
39#include "detect-engine-state.h"
40
f2f9b832
PR
41#include "stream-tcp-private.h"
42#include "stream-tcp-reassemble.h"
6a53ab9c 43#include "stream-tcp.h"
f2f9b832
PR
44#include "stream.h"
45
429c6388 46#include "app-layer.h"
f2f9b832
PR
47#include "app-layer-protos.h"
48#include "app-layer-parser.h"
49#include "app-layer-ftp.h"
b0a69344 50#include "app-layer-expectation.h"
f2f9b832 51
705471e4 52#include "util-spm.h"
09ab032a 53#include "util-mpm.h"
f2f9b832
PR
54#include "util-unittest.h"
55#include "util-debug.h"
1859ed54 56#include "util-memcmp.h"
b0a69344
EL
57#include "util-memrchr.h"
58#include "util-byte.h"
711b6fb3
EL
59#include "util-mem.h"
60#include "util-misc.h"
b0a69344 61
b0a69344 62#include "rust-ftp-mod-gen.h"
f2f9b832 63
7e1235c9
VJ
64#include "output-json.h"
65
09ab032a
JL
66typedef struct FTPThreadCtx_ {
67 MpmThreadCtx *ftp_mpm_thread_ctx;
68 PrefilterRuleStore *pmq;
69} FTPThreadCtx;
70
71#define FTP_MPM mpm_default_matcher
72
73static MpmCtx *ftp_mpm_ctx = NULL;
74
1930b1f5
JL
75const FtpCommand FtpCommands[FTP_COMMAND_MAX + 1] = {
76 /* Parsed and handled */
09ab032a
JL
77 { FTP_COMMAND_PORT, "PORT", 4},
78 { FTP_COMMAND_EPRT, "EPRT", 4},
79 { FTP_COMMAND_AUTH_TLS, "AUTH TLS", 8},
80 { FTP_COMMAND_PASV, "PASV", 4},
81 { FTP_COMMAND_RETR, "RETR", 4},
82 { FTP_COMMAND_EPSV, "EPSV", 4},
83 { FTP_COMMAND_STOR, "STOR", 4},
1930b1f5
JL
84
85 /* Parsed, but not handled */
09ab032a
JL
86 { FTP_COMMAND_ABOR, "ABOR", 4},
87 { FTP_COMMAND_ACCT, "ACCT", 4},
88 { FTP_COMMAND_ALLO, "ALLO", 4},
89 { FTP_COMMAND_APPE, "APPE", 4},
90 { FTP_COMMAND_CDUP, "CDUP", 4},
91 { FTP_COMMAND_CHMOD, "CHMOD", 5},
92 { FTP_COMMAND_CWD, "CWD", 3},
93 { FTP_COMMAND_DELE, "DELE", 4},
94 { FTP_COMMAND_HELP, "HELP", 4},
95 { FTP_COMMAND_IDLE, "IDLE", 4},
96 { FTP_COMMAND_LIST, "LIST", 4},
97 { FTP_COMMAND_MAIL, "MAIL", 4},
98 { FTP_COMMAND_MDTM, "MDTM", 4},
99 { FTP_COMMAND_MKD, "MKD", 3},
100 { FTP_COMMAND_MLFL, "MLFL", 4},
101 { FTP_COMMAND_MODE, "MODE", 4},
102 { FTP_COMMAND_MRCP, "MRCP", 4},
103 { FTP_COMMAND_MRSQ, "MRSQ", 4},
104 { FTP_COMMAND_MSAM, "MSAM", 4},
105 { FTP_COMMAND_MSND, "MSND", 4},
106 { FTP_COMMAND_MSOM, "MSOM", 4},
107 { FTP_COMMAND_NLST, "NLST", 4},
108 { FTP_COMMAND_NOOP, "NOOP", 4},
109 { FTP_COMMAND_PASS, "PASS", 4},
110 { FTP_COMMAND_PWD, "PWD", 3},
111 { FTP_COMMAND_QUIT, "QUIT", 4},
112 { FTP_COMMAND_REIN, "REIN", 4},
113 { FTP_COMMAND_REST, "REST", 4},
114 { FTP_COMMAND_RMD, "RMD", 3},
115 { FTP_COMMAND_RNFR, "RNFR", 4},
116 { FTP_COMMAND_RNTO, "RNTO", 4},
117 { FTP_COMMAND_SITE, "SITE", 4},
118 { FTP_COMMAND_SIZE, "SIZE", 4},
119 { FTP_COMMAND_SMNT, "SMNT", 4},
120 { FTP_COMMAND_STAT, "STAT", 4},
121 { FTP_COMMAND_STOU, "STOU", 4},
122 { FTP_COMMAND_STRU, "STRU", 4},
123 { FTP_COMMAND_SYST, "SYST", 4},
124 { FTP_COMMAND_TYPE, "TYPE", 4},
125 { FTP_COMMAND_UMASK, "UMASK", 5},
126 { FTP_COMMAND_USER, "USER", 4},
127 { FTP_COMMAND_UNKNOWN, NULL, 0}
1930b1f5 128};
711b6fb3
EL
129uint64_t ftp_config_memcap = 0;
130
131SC_ATOMIC_DECLARE(uint64_t, ftp_memuse);
132SC_ATOMIC_DECLARE(uint64_t, ftp_memcap);
133
8ae69115 134static FTPTransaction *FTPGetOldestTx(FtpState *);
2149807b 135
711b6fb3
EL
136static void FTPParseMemcap(void)
137{
138 const char *conf_val;
139
140 /** set config values for memcap, prealloc and hash_size */
141 if ((ConfGet("app-layer.protocols.ftp.memcap", &conf_val)) == 1)
142 {
143 if (ParseSizeStringU64(conf_val, &ftp_config_memcap) < 0) {
144 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing ftp.memcap "
145 "from conf file - %s. Killing engine",
146 conf_val);
147 exit(EXIT_FAILURE);
148 }
149 SCLogInfo("FTP memcap: %"PRIu64, ftp_config_memcap);
150 } else {
151 /* default to unlimited */
152 ftp_config_memcap = 0;
153 }
154
155 SC_ATOMIC_INIT(ftp_memuse);
156 SC_ATOMIC_INIT(ftp_memcap);
157}
158
159static void FTPIncrMemuse(uint64_t size)
160{
161 (void) SC_ATOMIC_ADD(ftp_memuse, size);
162 return;
163}
164
165static void FTPDecrMemuse(uint64_t size)
166{
167 (void) SC_ATOMIC_SUB(ftp_memuse, size);
168 return;
169}
170
171uint64_t FTPMemuseGlobalCounter(void)
172{
173 uint64_t tmpval = SC_ATOMIC_GET(ftp_memuse);
174 return tmpval;
175}
176
177uint64_t FTPMemcapGlobalCounter(void)
178{
179 uint64_t tmpval = SC_ATOMIC_GET(ftp_memcap);
180 return tmpval;
181}
182
183/**
184 * \brief Check if alloc'ing "size" would mean we're over memcap
185 *
186 * \retval 1 if in bounds
187 * \retval 0 if not in bounds
188 */
189static int FTPCheckMemcap(uint64_t size)
190{
191 if (ftp_config_memcap == 0 || size + SC_ATOMIC_GET(ftp_memuse) <= ftp_config_memcap)
192 return 1;
193 (void) SC_ATOMIC_ADD(ftp_memcap, 1);
194 return 0;
195}
196
197static void *FTPMalloc(size_t size)
198{
199 void *ptr = NULL;
200
201 if (FTPCheckMemcap((uint32_t)size) == 0)
202 return NULL;
203
204 ptr = SCMalloc(size);
205
206 if (unlikely(ptr == NULL))
207 return NULL;
208
209 FTPIncrMemuse((uint64_t)size);
210
211 return ptr;
212}
213
214static void *FTPCalloc(size_t n, size_t size)
215{
216 void *ptr = NULL;
217
218 if (FTPCheckMemcap((uint32_t)(n * size)) == 0)
219 return NULL;
220
221 ptr = SCCalloc(n, size);
222
223 if (unlikely(ptr == NULL))
224 return NULL;
225
226 FTPIncrMemuse((uint64_t)(n * size));
227
228 return ptr;
229}
230
231static void *FTPRealloc(void *ptr, size_t orig_size, size_t size)
232{
233 void *rptr = NULL;
234
235 if (FTPCheckMemcap((uint32_t)(size - orig_size)) == 0)
236 return NULL;
237
238 rptr = SCRealloc(ptr, size);
239 if (rptr == NULL)
240 return NULL;
241
242 if (size > orig_size) {
243 FTPIncrMemuse(size - orig_size);
244 } else {
245 FTPDecrMemuse(orig_size - size);
246 }
247
248 return rptr;
249}
250
251static void FTPFree(void *ptr, size_t size)
252{
253 SCFree(ptr);
254
255 FTPDecrMemuse((uint64_t)size);
256}
257
1930b1f5
JL
258static FTPString *FTPStringAlloc(void)
259{
260 return FTPCalloc(1, sizeof(FTPString));
261}
262
263static void FTPStringFree(FTPString *str)
264{
265 if (str->str) {
266 FTPFree(str->str, str->len);
267 }
268
269 FTPFree(str, sizeof(FTPString));
270}
271
09ab032a
JL
272static void *FTPLocalStorageAlloc(void)
273{
274 /* needed by the mpm */
275 FTPThreadCtx *td = SCCalloc(1, sizeof(*td));
276 if (td == NULL) {
277 exit(EXIT_FAILURE);
278 }
279
280 td->pmq = SCCalloc(1, sizeof(*td->pmq));
281 if (td->pmq == NULL) {
282 exit(EXIT_FAILURE);
283 }
284 PmqSetup(td->pmq);
285
286 td->ftp_mpm_thread_ctx = SCCalloc(1, sizeof(MpmThreadCtx));
287 if (unlikely(td->ftp_mpm_thread_ctx == NULL)) {
288 exit(EXIT_FAILURE);
289 }
290 MpmInitThreadCtx(td->ftp_mpm_thread_ctx, FTP_MPM);
291 return td;
292}
293
294static void FTPLocalStorageFree(void *ptr)
295{
296 FTPThreadCtx *td = ptr;
297 if (td != NULL) {
298 if (td->pmq != NULL) {
299 PmqFree(td->pmq);
300 SCFree(td->pmq);
301 }
302
303 if (td->ftp_mpm_thread_ctx != NULL) {
304 mpm_table[FTP_MPM].DestroyThreadCtx(ftp_mpm_ctx, td->ftp_mpm_thread_ctx);
305 SCFree(td->ftp_mpm_thread_ctx);
306 }
307
308 SCFree(td);
309 }
310
311 return;
312}
1930b1f5
JL
313static FTPTransaction *FTPTransactionCreate(FtpState *state)
314{
315 SCEnter();
316 FTPTransaction *tx = FTPCalloc(1, sizeof(*tx));
317 if (tx == NULL) {
318 return NULL;
319 }
320
321 TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
322 tx->tx_id = state->tx_cnt++;
323
324 TAILQ_INIT(&tx->response_list);
325
326 SCLogDebug("new transaction %p (state tx cnt %"PRIu64")", tx, state->tx_cnt);
327 return tx;
328}
329
330static void FTPTransactionFree(FTPTransaction *tx)
331{
332 SCEnter();
333
334 if (tx->de_state != NULL) {
335 DetectEngineStateFree(tx->de_state);
336 }
337
338 if (tx->request) {
339 FTPFree(tx->request, tx->request_length);
340 }
1930b1f5
JL
341
342 FTPString *str = NULL;
343 while ((str = TAILQ_FIRST(&tx->response_list))) {
344 TAILQ_REMOVE(&tx->response_list, str, next);
345 FTPStringFree(str);
346 }
347
348 SCFree(tx);
349}
350
6ea8ac44
AS
351static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
352{
1f07d152 353 void *ptmp;
6ea8ac44
AS
354 if (line_state->current_line_lf_seen == 1) {
355 /* we have seen the lf for the previous line. Clear the parser
356 * details to parse new line */
357 line_state->current_line_lf_seen = 0;
358 if (line_state->current_line_db == 1) {
359 line_state->current_line_db = 0;
711b6fb3 360 FTPFree(line_state->db, line_state->db_len);
6ea8ac44
AS
361 line_state->db = NULL;
362 line_state->db_len = 0;
363 state->current_line = NULL;
364 state->current_line_len = 0;
365 }
366 }
367
368 uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
369
370 if (lf_idx == NULL) {
371 /* fragmented lines. Decoder event for special cases. Not all
372 * fragmented lines should be treated as a possible evasion
373 * attempt. With multi payload ftp chunks we can have valid
374 * cases of fragmentation. But within the same segment chunk
375 * if we see fragmentation then it's definitely something you
376 * should alert about */
377 if (line_state->current_line_db == 0) {
711b6fb3 378 line_state->db = FTPMalloc(state->input_len);
6ea8ac44
AS
379 if (line_state->db == NULL) {
380 return -1;
381 }
382 line_state->current_line_db = 1;
383 memcpy(line_state->db, state->input, state->input_len);
384 line_state->db_len = state->input_len;
385 } else {
711b6fb3 386 ptmp = FTPRealloc(line_state->db, line_state->db_len,
1f07d152
EL
387 (line_state->db_len + state->input_len));
388 if (ptmp == NULL) {
711b6fb3 389 FTPFree(line_state->db, line_state->db_len);
1f07d152
EL
390 line_state->db = NULL;
391 line_state->db_len = 0;
6ea8ac44
AS
392 return -1;
393 }
1f07d152
EL
394 line_state->db = ptmp;
395
6ea8ac44
AS
396 memcpy(line_state->db + line_state->db_len,
397 state->input, state->input_len);
398 line_state->db_len += state->input_len;
399 }
400 state->input += state->input_len;
401 state->input_len = 0;
402
403 return -1;
404
405 } else {
406 line_state->current_line_lf_seen = 1;
407
408 if (line_state->current_line_db == 1) {
711b6fb3 409 ptmp = FTPRealloc(line_state->db, line_state->db_len,
1f07d152
EL
410 (line_state->db_len + (lf_idx + 1 - state->input)));
411 if (ptmp == NULL) {
711b6fb3 412 FTPFree(line_state->db, line_state->db_len);
1f07d152
EL
413 line_state->db = NULL;
414 line_state->db_len = 0;
6ea8ac44
AS
415 return -1;
416 }
1f07d152
EL
417 line_state->db = ptmp;
418
6ea8ac44
AS
419 memcpy(line_state->db + line_state->db_len,
420 state->input, (lf_idx + 1 - state->input));
421 line_state->db_len += (lf_idx + 1 - state->input);
422
423 if (line_state->db_len > 1 &&
424 line_state->db[line_state->db_len - 2] == 0x0D) {
425 line_state->db_len -= 2;
426 state->current_line_delimiter_len = 2;
427 } else {
428 line_state->db_len -= 1;
429 state->current_line_delimiter_len = 1;
430 }
431
432 state->current_line = line_state->db;
433 state->current_line_len = line_state->db_len;
434
435 } else {
436 state->current_line = state->input;
437 state->current_line_len = lf_idx - state->input;
438
439 if (state->input != lf_idx &&
440 *(lf_idx - 1) == 0x0D) {
441 state->current_line_len--;
442 state->current_line_delimiter_len = 2;
443 } else {
444 state->current_line_delimiter_len = 1;
445 }
446 }
447
448 state->input_len -= (lf_idx - state->input) + 1;
449 state->input = (lf_idx + 1);
450
451 return 0;
452 }
453
454}
455
456static int FTPGetLine(FtpState *state)
457{
458 SCEnter();
459
460 /* we have run out of input */
461 if (state->input_len <= 0)
462 return -1;
463
464 /* toserver */
465 if (state->direction == 0)
466 return FTPGetLineForDirection(state, &state->line_state[0]);
467 else
468 return FTPGetLineForDirection(state, &state->line_state[1]);
469}
470
f2f9b832
PR
471/**
472 * \brief This function is called to determine and set which command is being
1930b1f5 473 * transferred to the ftp server
86fabef0 474 * \param thread context
f2f9b832
PR
475 * \param input input line of the command
476 * \param len of the command
1930b1f5 477 * \param cmd_descriptor when the command has been parsed
f2f9b832
PR
478 *
479 * \retval 1 when the command is parsed, 0 otherwise
480 */
86fabef0 481static int FTPParseRequestCommand(FTPThreadCtx *td,
579cc9f0 482 const uint8_t *input, uint32_t input_len,
09ab032a 483 const FtpCommand **cmd_descriptor)
8f1d7503 484{
f2f9b832 485 SCEnter();
11b9e6fd 486
09ab032a
JL
487 /* I don't like this pmq reset here. We'll devise a method later, that
488 * should make the use of the mpm very efficient */
489 PmqReset(td->pmq);
490 int mpm_cnt = mpm_table[FTP_MPM].Search(ftp_mpm_ctx, td->ftp_mpm_thread_ctx,
491 td->pmq, input, input_len);
492 if (mpm_cnt) {
493 *cmd_descriptor = &FtpCommands[td->pmq->rule_id_array[0]];
86fabef0 494 SCReturnInt(1);
b0a69344 495 }
09ab032a
JL
496
497 *cmd_descriptor = NULL;
86fabef0 498 SCReturnInt(0);
f2f9b832
PR
499}
500
b0a69344
EL
501struct FtpTransferCmd {
502 /** Need to look like a ExpectationData so DFree must
503 * be first field . */
504 void (*DFree)(void *);
505 uint64_t flow_id;
506 uint8_t *file_name;
507 uint16_t file_len;
508 FtpRequestCommand cmd;
509};
510
511static void FtpTransferCmdFree(void *data)
512{
513 struct FtpTransferCmd *cmd = (struct FtpTransferCmd *) data;
514 if (cmd == NULL)
515 return;
516 if (cmd->file_name) {
de983fb7 517 FTPFree(cmd->file_name, cmd->file_len);
b0a69344 518 }
711b6fb3 519 FTPFree(cmd, sizeof(struct FtpTransferCmd));
b0a69344
EL
520}
521
579cc9f0 522static uint32_t CopyCommandLine(uint8_t **dest, const uint8_t *src, uint32_t length)
1930b1f5
JL
523{
524 if (likely(length)) {
1588cd87 525 uint8_t *where = FTPCalloc(length + 1, sizeof(char));
1930b1f5
JL
526 if (unlikely(where == NULL)) {
527 return 0;
528 }
529 memcpy(where, src, length);
530
531 /* Remove trailing newlines/carriage returns */
1588cd87
ZK
532 while (length && isspace((unsigned char) where[length - 1])) {
533 length--;
1930b1f5 534 }
1588cd87
ZK
535
536 where[length] = '\0';
1930b1f5
JL
537 *dest = where;
538 }
539 /* either 0 or actual */
540 return length;
541}
542
4f33b8c1
JL
543static uint16_t ftp_validate_port(int computed_port_value)
544{
545 unsigned int port_val = computed_port_value;
546
547 if (port_val && port_val > UINT16_MAX)
548 return 0;
549
550 return ((uint16_t) (port_val));
551}
552
553/**
554 * \brief This function extracts a port number from the command input line for IPv6 FTP usage
555 * \param input input line of the command
556 * \param input_len length of the request
557 *
558 * \retval 0 if a port number could not be extracted; otherwise, the dynamic port number
559 */
579cc9f0 560static uint16_t FTPGetV6PortNumber(const uint8_t *input, uint32_t input_len)
4f33b8c1 561{
94a976d4
PA
562 uint16_t res;
563
4f33b8c1
JL
564 uint8_t *ptr = memrchr(input, '|', input_len);
565 if (ptr == NULL) {
566 return 0;
567 }
568
569 int n_length = ptr - input - 1;
570 if (n_length < 4)
571 return 0;
572
573 ptr = memrchr(input, '|', n_length);
574 if (ptr == NULL)
575 return 0;
576
94a976d4
PA
577 if (ByteExtractStringUint16(&res, 10, 0, (const char *)ptr + 1) < 0) {
578 return 0;
579 }
580 return res;
4f33b8c1
JL
581}
582
583/**
584 * \brief This function extracts a port number from the command input line for IPv4 FTP usage
585 * \param input input line of the command
586 * \param input_len length of the request
587 *
588 * \retval 0 if a port number could not be extracted; otherwise, the dynamic port number
589 */
579cc9f0 590static uint16_t FTPGetV4PortNumber(const uint8_t *input, uint32_t input_len)
4f33b8c1
JL
591{
592 uint16_t part1, part2;
593 uint8_t *ptr = memrchr(input, ',', input_len);
594 if (ptr == NULL)
595 return 0;
596
597 part2 = atoi((char *)ptr + 1);
598 ptr = memrchr(input, ',', (ptr - input) - 1);
599 if (ptr == NULL)
600 return 0;
601 part1 = atoi((char *)ptr + 1);
602
603 return ftp_validate_port(256 * part1 + part2);
604}
605
606
f2f9b832
PR
607/**
608 * \brief This function is called to retrieve a ftp request
609 * \param ftp_state the ftp state structure for the parser
610 * \param input input line of the command
611 * \param input_len length of the request
612 * \param output the resulting output
613 *
614 * \retval 1 when the command is parsed, 0 otherwise
615 */
9a6aef45 616static int FTPParseRequest(Flow *f, void *ftp_state,
9634e60e 617 AppLayerParserState *pstate,
579cc9f0 618 const uint8_t *input, uint32_t input_len,
7bc3c3ac 619 void *local_data, const uint8_t flags)
9a6aef45 620{
09ab032a
JL
621 FTPThreadCtx *thread_data = local_data;
622
f2f9b832 623 SCEnter();
f2f9b832
PR
624 /* PrintRawDataFp(stdout, input,input_len); */
625
6ea8ac44 626 FtpState *state = (FtpState *)ftp_state;
1f07d152 627 void *ptmp;
6ea8ac44 628
4e7cb7b8
VJ
629 if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
630 SCReturnInt(1);
f4f53924
VJ
631 } else if (input == NULL || input_len == 0) {
632 SCReturnInt(-1);
4e7cb7b8
VJ
633 }
634
6ea8ac44
AS
635 state->input = input;
636 state->input_len = input_len;
637 /* toserver stream */
638 state->direction = 0;
639
b0a69344 640 int direction = STREAM_TOSERVER;
6ea8ac44 641 while (FTPGetLine(state) >= 0) {
1930b1f5
JL
642 const FtpCommand *cmd_descriptor;
643
86fabef0 644 if (!FTPParseRequestCommand(thread_data, state->current_line, state->current_line_len, &cmd_descriptor)) {
1930b1f5
JL
645 state->command = FTP_COMMAND_UNKNOWN;
646 continue;
647 }
648
649 state->command = cmd_descriptor->command;
650
2149807b
JL
651 FTPTransaction *tx = FTPTransactionCreate(state);
652 if (unlikely(tx == NULL))
653 return -1;
654 state->curr_tx = tx;
1930b1f5
JL
655
656 tx->command_descriptor = cmd_descriptor;
657 tx->request_length = CopyCommandLine(&tx->request, state->current_line, state->current_line_len);
658
b0a69344 659 switch (state->command) {
4f33b8c1
JL
660 case FTP_COMMAND_EPRT:
661 // fallthrough
b0a69344 662 case FTP_COMMAND_PORT:
3ae2edb2 663 if (state->current_line_len + 1 > state->port_line_size) {
4f33b8c1 664 /* Allocate an extra byte for a NULL terminator */
711b6fb3 665 ptmp = FTPRealloc(state->port_line, state->port_line_size,
4f33b8c1 666 state->current_line_len + 1);
b0a69344 667 if (ptmp == NULL) {
4f33b8c1
JL
668 if (state->port_line) {
669 FTPFree(state->port_line, state->port_line_size);
670 state->port_line = NULL;
671 state->port_line_size = 0;
672 }
b0a69344
EL
673 return 0;
674 }
675 state->port_line = ptmp;
4f33b8c1 676 state->port_line_size = state->current_line_len + 1;
6ea8ac44 677 }
b0a69344
EL
678 memcpy(state->port_line, state->current_line,
679 state->current_line_len);
3ae2edb2 680 state->port_line[state->current_line_len] = '\0';
b0a69344
EL
681 state->port_line_len = state->current_line_len;
682 break;
683 case FTP_COMMAND_RETR:
684 /* change direction (default to server) so expectation will handle
685 * the correct message when expectation will match.
686 */
687 direction = STREAM_TOCLIENT;
688 // fallthrough
689 case FTP_COMMAND_STOR:
690 {
691 /* No dyn port negotiated so get out */
692 if (state->dyn_port == 0) {
693 SCReturnInt(-1);
694 }
711b6fb3 695 struct FtpTransferCmd *data = FTPCalloc(1, sizeof(struct FtpTransferCmd));
b0a69344
EL
696 if (data == NULL)
697 SCReturnInt(-1);
698 data->DFree = FtpTransferCmdFree;
699 /* Min size has been checked in FTPParseRequestCommand */
711b6fb3 700 data->file_name = FTPCalloc(state->current_line_len - 4, sizeof(char));
b0a69344 701 if (data->file_name == NULL) {
de983fb7 702 FtpTransferCmdFree(data);
b0a69344
EL
703 SCReturnInt(-1);
704 }
705 data->file_name[state->current_line_len - 5] = 0;
706 data->file_len = state->current_line_len - 5;
707 memcpy(data->file_name, state->current_line + 5, state->current_line_len - 5);
708 data->cmd = state->command;
709 data->flow_id = FlowGetId(f);
4f33b8c1
JL
710 int ret = AppLayerExpectationCreate(f,
711 state->active ? STREAM_TOSERVER : direction,
712 0, state->dyn_port, ALPROTO_FTPDATA, data);
b0a69344 713 if (ret == -1) {
de983fb7 714 FtpTransferCmdFree(data);
b0a69344
EL
715 SCLogDebug("No expectation created.");
716 SCReturnInt(-1);
717 } else {
4f33b8c1
JL
718 SCLogDebug("Expectation created [direction: %s, dynamic port %"PRIu16"].",
719 state->active ? "to server" : "to client",
720 state->dyn_port);
b0a69344 721 }
4f33b8c1 722
b0a69344
EL
723 /* reset the dyn port to avoid duplicate */
724 state->dyn_port = 0;
4f33b8c1
JL
725 /* reset active/passive indicator */
726 state->active = false;
b0a69344
EL
727 }
728 break;
729 default:
730 break;
6ea8ac44 731 }
f2f9b832 732 }
f2f9b832 733
f2f9b832
PR
734 return 1;
735}
736
579cc9f0 737static int FTPParsePassiveResponse(Flow *f, FtpState *state, const uint8_t *input, uint32_t input_len)
b0a69344 738{
f79316d7 739 uint16_t dyn_port = rs_ftp_pasv_response(input, input_len);
b0a69344
EL
740 if (dyn_port == 0) {
741 return -1;
742 }
4f33b8c1
JL
743 SCLogDebug("FTP passive mode (v4): dynamic port %"PRIu16"", dyn_port);
744 state->active = false;
b0a69344 745 state->dyn_port = dyn_port;
1930b1f5
JL
746 state->curr_tx->dyn_port = dyn_port;
747 state->curr_tx->active = false;
b0a69344
EL
748
749 return 0;
750}
751
579cc9f0 752static int FTPParsePassiveResponseV6(Flow *f, FtpState *state, const uint8_t *input, uint32_t input_len)
b0a69344 753{
f79316d7 754 uint16_t dyn_port = rs_ftp_epsv_response(input, input_len);
b0a69344
EL
755 if (dyn_port == 0) {
756 return -1;
757 }
4f33b8c1
JL
758 SCLogDebug("FTP passive mode (v6): dynamic port %"PRIu16"", dyn_port);
759 state->active = false;
b0a69344 760 state->dyn_port = dyn_port;
1930b1f5
JL
761 state->curr_tx->dyn_port = dyn_port;
762 state->curr_tx->active = false;
b0a69344
EL
763 return 0;
764}
765
1930b1f5
JL
766/**
767 * \brief Handle preliminary replies -- keep tx open
768 * \retval: True for a positive preliminary reply; false otherwise
769 *
770 * 1yz Positive Preliminary reply
771 *
772 * The requested action is being initiated; expect another
773 * reply before proceeding with a new command
774 */
579cc9f0 775static inline bool FTPIsPPR(const uint8_t *input, uint32_t input_len)
1930b1f5
JL
776{
777 return input_len >= 4 && isdigit(input[0]) && input[0] == '1' &&
778 isdigit(input[1]) && isdigit(input[2]) && isspace(input[3]);
779}
780
f2f9b832
PR
781/**
782 * \brief This function is called to retrieve a ftp response
783 * \param ftp_state the ftp state structure for the parser
784 * \param input input line of the command
785 * \param input_len length of the request
786 * \param output the resulting output
787 *
788 * \retval 1 when the command is parsed, 0 otherwise
789 */
9634e60e 790static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
579cc9f0 791 const uint8_t *input, uint32_t input_len,
7bc3c3ac 792 void *local_data, const uint8_t flags)
9a6aef45 793{
11b9e6fd 794 FtpState *state = (FtpState *)ftp_state;
1930b1f5
JL
795 int retcode = 1;
796
b595da6c
VJ
797 if (unlikely(input_len == 0)) {
798 return 1;
799 }
a04b1c16 800
b595da6c
VJ
801 FTPTransaction *tx = FTPGetOldestTx(state);
802 if (tx == NULL) {
803 tx = FTPTransactionCreate(state);
804 }
805 if (unlikely(tx == NULL)) {
806 return -1;
807 }
808 if (state->command == FTP_COMMAND_UNKNOWN || tx->command_descriptor == NULL) {
a04b1c16
JL
809 /* unknown */
810 tx->command_descriptor = &FtpCommands[FTP_COMMAND_MAX -1];
1930b1f5 811 }
11b9e6fd 812
fb019213 813 state->curr_tx = tx;
11b9e6fd
MK
814 if (state->command == FTP_COMMAND_AUTH_TLS) {
815 if (input_len >= 4 && SCMemcmp("234 ", input, 4) == 0) {
6f42ae91 816 AppLayerRequestProtocolTLSUpgrade(f);
11b9e6fd
MK
817 }
818 }
819
4f33b8c1
JL
820 if (state->command == FTP_COMMAND_EPRT) {
821 uint16_t dyn_port = FTPGetV6PortNumber(state->port_line, state->port_line_len);
822 if (dyn_port == 0) {
1930b1f5
JL
823 retcode = 0;
824 goto tx_complete;
4f33b8c1
JL
825 }
826 state->dyn_port = dyn_port;
827 state->active = true;
1930b1f5
JL
828 tx->dyn_port = dyn_port;
829 tx->active = true;
4f33b8c1
JL
830 SCLogDebug("FTP active mode (v6): dynamic port %"PRIu16"", dyn_port);
831 }
832
833 if (state->command == FTP_COMMAND_PORT) {
834 if ((flags & STREAM_TOCLIENT)) {
835 uint16_t dyn_port = FTPGetV4PortNumber(state->port_line, state->port_line_len);
836 if (dyn_port == 0) {
1930b1f5
JL
837 retcode = 0;
838 goto tx_complete;
4f33b8c1
JL
839 }
840 state->dyn_port = dyn_port;
841 state->active = true;
1930b1f5
JL
842 tx->dyn_port = state->dyn_port;
843 tx->active = true;
4f33b8c1
JL
844 SCLogDebug("FTP active mode (v4): dynamic port %"PRIu16"", dyn_port);
845 }
846 }
1930b1f5 847
b0a69344
EL
848 if (state->command == FTP_COMMAND_PASV) {
849 if (input_len >= 4 && SCMemcmp("227 ", input, 4) == 0) {
850 FTPParsePassiveResponse(f, ftp_state, input, input_len);
851 }
852 }
853
854 if (state->command == FTP_COMMAND_EPSV) {
855 if (input_len >= 4 && SCMemcmp("229 ", input, 4) == 0) {
856 FTPParsePassiveResponseV6(f, ftp_state, input, input_len);
857 }
858 }
859
1930b1f5
JL
860 if (likely(input_len)) {
861 FTPString *response = FTPStringAlloc();
862 if (likely(response)) {
863 response->len = CopyCommandLine(&response->str, input, input_len);
2149807b 864 TAILQ_INSERT_TAIL(&tx->response_list, response, next);
1930b1f5
JL
865 }
866 }
867
911d423a
JL
868 /* Handle preliminary replies -- keep tx open */
869 if (FTPIsPPR(input, input_len)) {
343ba459
VJ
870 return retcode;
871 }
872
1930b1f5
JL
873tx_complete:
874 tx->done = true;
875 return retcode;
f2f9b832
PR
876}
877
911d423a 878
f2f9b832 879#ifdef DEBUG
5532af46 880static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
f2f9b832
PR
881static uint64_t ftp_state_memuse = 0;
882static uint64_t ftp_state_memcnt = 0;
883#endif
884
8f1d7503
KS
885static void *FTPStateAlloc(void)
886{
1930b1f5 887 void *s = FTPCalloc(1, sizeof(FtpState));
e176be6f 888 if (unlikely(s == NULL))
f2f9b832
PR
889 return NULL;
890
1930b1f5
JL
891 FtpState *ftp_state = (FtpState *) s;
892 TAILQ_INIT(&ftp_state->tx_list);
f2f9b832
PR
893
894#ifdef DEBUG
895 SCMutexLock(&ftp_state_mem_lock);
896 ftp_state_memcnt++;
897 ftp_state_memuse+=sizeof(FtpState);
898 SCMutexUnlock(&ftp_state_mem_lock);
899#endif
900 return s;
901}
902
8f1d7503
KS
903static void FTPStateFree(void *s)
904{
18954a2c
PR
905 FtpState *fstate = (FtpState *) s;
906 if (fstate->port_line != NULL)
711b6fb3 907 FTPFree(fstate->port_line, fstate->port_line_size);
b4ab9a0a 908 if (fstate->line_state[0].db)
711b6fb3 909 FTPFree(fstate->line_state[0].db, fstate->line_state[0].db_len);
b4ab9a0a 910 if (fstate->line_state[1].db)
711b6fb3 911 FTPFree(fstate->line_state[1].db, fstate->line_state[1].db_len);
402eb645
VJ
912
913 //AppLayerDecoderEventsFreeEvents(&s->decoder_events);
914
1930b1f5
JL
915 FTPTransaction *tx = NULL;
916 while ((tx = TAILQ_FIRST(&fstate->tx_list))) {
917 TAILQ_REMOVE(&fstate->tx_list, tx, next);
2149807b 918 SCLogDebug("[%s] state %p id %"PRIu64", Freeing %d bytes at %p",
09ab032a 919 tx->command_descriptor->command_name,
2149807b
JL
920 s, tx->tx_id,
921 tx->request_length, tx->request);
1930b1f5 922 FTPTransactionFree(tx);
402eb645
VJ
923 }
924
711b6fb3 925 FTPFree(s, sizeof(FtpState));
f2f9b832
PR
926#ifdef DEBUG
927 SCMutexLock(&ftp_state_mem_lock);
928 ftp_state_memcnt--;
929 ftp_state_memuse-=sizeof(FtpState);
930 SCMutexUnlock(&ftp_state_mem_lock);
931#endif
932}
933
7548944b 934static int FTPSetTxDetectState(void *vtx, DetectEngineState *de_state)
402eb645 935{
1930b1f5
JL
936 FTPTransaction *tx = (FTPTransaction *)vtx;
937 tx->de_state = de_state;
402eb645
VJ
938 return 0;
939}
940
2149807b
JL
941/**
942 * \brief This function returns the oldest open transaction; if none
943 * are open, then the oldest transaction is returned
944 * \param ftp_state the ftp state structure for the parser
945 *
946 * \retval transaction pointer when a transaction was found; NULL otherwise.
947 */
8ae69115 948static FTPTransaction *FTPGetOldestTx(FtpState *ftp_state)
2149807b
JL
949{
950 if (unlikely(!ftp_state)) {
951 SCLogDebug("NULL state object; no transactions available");
952 return NULL;
953 }
954 FTPTransaction *tx = NULL;
955 FTPTransaction *lasttx = NULL;
956 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
957 /* Return oldest open tx */
958 if (!tx->done) {
959 SCLogDebug("Returning tx %p id %"PRIu64, tx, tx->tx_id);
960 return tx;
961 }
962 /* save for the end */
963 lasttx = tx;
964 }
965 /* All tx are closed; return last element */
966 if (lasttx)
967 SCLogDebug("Returning OLDEST tx %p id %"PRIu64, lasttx, lasttx->tx_id);
968 return lasttx;
969}
970
1930b1f5
JL
971static void *FTPGetTx(void *state, uint64_t tx_id)
972{
973 FtpState *ftp_state = (FtpState *)state;
974 if (ftp_state) {
975 FTPTransaction *tx = NULL;
976
977 if (ftp_state->curr_tx == NULL)
978 return NULL;
979 if (ftp_state->curr_tx->tx_id == tx_id)
980 return ftp_state->curr_tx;
981
982 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
983 if (tx->tx_id == tx_id)
984 return tx;
985 }
986 }
987 return NULL;
1930b1f5
JL
988}
989
402eb645
VJ
990static DetectEngineState *FTPGetTxDetectState(void *vtx)
991{
1930b1f5
JL
992 FTPTransaction *tx = (FTPTransaction *)vtx;
993 return tx->de_state;
402eb645
VJ
994}
995
1930b1f5
JL
996
997static uint64_t FTPGetTxDetectFlags(void *vtx, uint8_t dir)
402eb645 998{
1930b1f5
JL
999 FTPTransaction *tx = (FTPTransaction *)vtx;
1000 if (dir & STREAM_TOSERVER) {
1001 return tx->detect_flags_ts;
1002 } else {
1003 return tx->detect_flags_tc;
1004 }
402eb645
VJ
1005}
1006
1930b1f5 1007static void FTPSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
402eb645 1008{
1930b1f5
JL
1009 FTPTransaction *tx = (FTPTransaction *)vtx;
1010 if (dir & STREAM_TOSERVER) {
1011 tx->detect_flags_ts = flags;
1012 } else {
1013 tx->detect_flags_tc = flags;
1014 }
1015}
1016
1017static void FTPStateTransactionFree(void *state, uint64_t tx_id)
1018{
1019 FtpState *ftp_state = state;
1020 FTPTransaction *tx = NULL;
1021 TAILQ_FOREACH(tx, &ftp_state->tx_list, next) {
1022 if (tx_id < tx->tx_id)
1023 break;
1024 else if (tx_id > tx->tx_id)
1025 continue;
1026
1027 if (tx == ftp_state->curr_tx)
1028 ftp_state->curr_tx = NULL;
1029 TAILQ_REMOVE(&ftp_state->tx_list, tx, next);
1030 FTPTransactionFree(tx);
1031 break;
1032 }
402eb645
VJ
1033}
1034
1035static uint64_t FTPGetTxCnt(void *state)
1036{
1930b1f5
JL
1037 uint64_t cnt = 0;
1038 FtpState *ftp_state = state;
1039 if (ftp_state) {
1040 cnt = ftp_state->tx_cnt;
1041 }
1042 SCLogDebug("returning state %p %"PRIu64, state, cnt);
1043 return cnt;
402eb645
VJ
1044}
1045
1046static int FTPGetAlstateProgressCompletionStatus(uint8_t direction)
1047{
1048 return FTP_STATE_FINISHED;
1049}
1050
1930b1f5 1051static int FTPGetAlstateProgress(void *vtx, uint8_t direction)
402eb645 1052{
1930b1f5
JL
1053 SCLogDebug("tx %p", vtx);
1054 FTPTransaction *tx = vtx;
402eb645
VJ
1055
1056 if (direction == STREAM_TOSERVER &&
1930b1f5 1057 tx->command_descriptor->command == FTP_COMMAND_PORT) {
402eb645
VJ
1058 return FTP_STATE_PORT_DONE;
1059 }
1060
dc80d520
VJ
1061 if (!tx->done)
1062 return FTP_STATE_IN_PROGRESS;
402eb645 1063
dc80d520 1064 return FTP_STATE_FINISHED;
402eb645
VJ
1065}
1066
1067
429c6388
AS
1068static int FTPRegisterPatternsForProtocolDetection(void)
1069{
8125f78f
MK
1070 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1071 "220 (", 5, 0, STREAM_TOCLIENT) < 0)
1072 {
1073 return -1;
1074 }
1075 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1076 "FEAT", 4, 0, STREAM_TOSERVER) < 0)
1077 {
1078 return -1;
1079 }
429c6388
AS
1080 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1081 "USER ", 5, 0, STREAM_TOSERVER) < 0)
1082 {
1083 return -1;
1084 }
1085 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1086 "PASS ", 5, 0, STREAM_TOSERVER) < 0)
1087 {
1088 return -1;
1089 }
1090 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
1091 "PORT ", 5, 0, STREAM_TOSERVER) < 0)
1092 {
1093 return -1;
1094 }
1095
1096 return 0;
1097}
1098
b0a69344
EL
1099
1100static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
1101
1102/**
1103 * \brief This function is called to retrieve a ftp request
1104 * \param ftp_state the ftp state structure for the parser
1105 * \param input input line of the command
1106 * \param input_len length of the request
1107 * \param output the resulting output
1108 *
1109 * \retval 1 when the command is parsed, 0 otherwise
1110 */
1111static int FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
1112 AppLayerParserState *pstate,
579cc9f0 1113 const uint8_t *input, uint32_t input_len,
b0a69344
EL
1114 void *local_data, int direction)
1115{
1116 uint16_t flags = FileFlowToFlags(f, direction);
1117 int ret = 0;
1118 /* we depend on detection engine for file pruning */
1119 flags |= FILE_USE_DETECT;
1120 if (ftpdata_state->files == NULL) {
1121 struct FtpTransferCmd *data = (struct FtpTransferCmd *)FlowGetStorageById(f, AppLayerExpectationGetDataId());
1122 if (data == NULL) {
1123 SCReturnInt(-1);
1124 }
1125
1126 ftpdata_state->files = FileContainerAlloc();
1127 if (ftpdata_state->files == NULL) {
1128 FlowFreeStorageById(f, AppLayerExpectationGetDataId());
1129 SCReturnInt(-1);
1130 }
1131
1132 ftpdata_state->file_name = data->file_name;
1133 ftpdata_state->file_len = data->file_len;
1134 data->file_name = NULL;
1135 data->file_len = 0;
1136 f->parent_id = data->flow_id;
1137 ftpdata_state->command = data->cmd;
2515c892
EL
1138 switch (data->cmd) {
1139 case FTP_COMMAND_STOR:
1140 ftpdata_state->direction = STREAM_TOSERVER;
1141 break;
1142 case FTP_COMMAND_RETR:
1143 ftpdata_state->direction = STREAM_TOCLIENT;
1144 break;
1145 default:
1146 break;
1147 }
b0a69344 1148
9132e403
VJ
1149 /* open with fixed track_id 0 as we can have just one
1150 * file per ftp-data flow. */
1151 if (FileOpenFileWithId(ftpdata_state->files, &sbcfg,
1152 0ULL, (uint8_t *) ftpdata_state->file_name,
b0a69344 1153 ftpdata_state->file_len,
9132e403 1154 input, input_len, flags) != 0) {
b0a69344
EL
1155 SCLogDebug("Can't open file");
1156 ret = -1;
1157 }
1158 FlowFreeStorageById(f, AppLayerExpectationGetDataId());
1159 } else {
1160 if (input_len != 0) {
1161 ret = FileAppendData(ftpdata_state->files, input, input_len);
1162 if (ret == -2) {
1163 ret = 0;
1164 SCLogDebug("FileAppendData() - file no longer being extracted");
1165 goto out;
1166 } else if (ret < 0) {
1167 SCLogDebug("FileAppendData() failed: %d", ret);
1168 ret = -2;
1169 goto out;
1170 }
1171 } else {
1172 ret = FileCloseFile(ftpdata_state->files, NULL, 0, flags);
1173 ftpdata_state->state = FTPDATA_STATE_FINISHED;
1174 if (ret < 0)
1175 goto out;
1176 }
1177 }
1178
1179 if (input_len && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
1180 ret = FileCloseFile(ftpdata_state->files, (uint8_t *) NULL, 0, flags);
1181 ftpdata_state->state = FTPDATA_STATE_FINISHED;
1182 }
1183
1184out:
1185 if (ftpdata_state->files) {
1186 FilePrune(ftpdata_state->files);
1187 }
1188 return ret;
1189}
1190
1930b1f5
JL
1191static void FTPStateSetTxLogged(void *state, void *vtx, LoggerId logged)
1192{
1193 FTPTransaction *tx = vtx;
1194 tx->logged = logged;
1195}
1196
1197static LoggerId FTPStateGetTxLogged(void *state, void *vtx)
1198{
1199 FTPTransaction *tx = vtx;
1200 return tx->logged;
1201}
b0a69344
EL
1202static int FTPDataParseRequest(Flow *f, void *ftp_state,
1203 AppLayerParserState *pstate,
579cc9f0 1204 const uint8_t *input, uint32_t input_len,
7bc3c3ac 1205 void *local_data, const uint8_t flags)
b0a69344
EL
1206{
1207 return FTPDataParse(f, ftp_state, pstate, input, input_len,
1208 local_data, STREAM_TOSERVER);
1209}
1210
1211static int FTPDataParseResponse(Flow *f, void *ftp_state,
1212 AppLayerParserState *pstate,
579cc9f0 1213 const uint8_t *input, uint32_t input_len,
7bc3c3ac 1214 void *local_data, const uint8_t flags)
b0a69344
EL
1215{
1216 return FTPDataParse(f, ftp_state, pstate, input, input_len,
1217 local_data, STREAM_TOCLIENT);
1218}
1219
1220#ifdef DEBUG
1221static SCMutex ftpdata_state_mem_lock = SCMUTEX_INITIALIZER;
1222static uint64_t ftpdata_state_memuse = 0;
1223static uint64_t ftpdata_state_memcnt = 0;
1224#endif
1225
1226static void *FTPDataStateAlloc(void)
1227{
1930b1f5 1228 void *s = FTPCalloc(1, sizeof(FtpDataState));
b0a69344
EL
1229 if (unlikely(s == NULL))
1230 return NULL;
1231
1930b1f5
JL
1232 FtpDataState *state = (FtpDataState *) s;
1233 state->state = FTPDATA_STATE_IN_PROGRESS;
b0a69344
EL
1234
1235#ifdef DEBUG
1236 SCMutexLock(&ftpdata_state_mem_lock);
1237 ftpdata_state_memcnt++;
1238 ftpdata_state_memuse+=sizeof(FtpDataState);
1239 SCMutexUnlock(&ftpdata_state_mem_lock);
1240#endif
1241 return s;
1242}
1243
1244static void FTPDataStateFree(void *s)
1245{
1246 FtpDataState *fstate = (FtpDataState *) s;
1247
1248 if (fstate->de_state != NULL) {
1249 DetectEngineStateFree(fstate->de_state);
1250 }
1251 if (fstate->file_name != NULL) {
711b6fb3 1252 FTPFree(fstate->file_name, fstate->file_len);
b0a69344
EL
1253 }
1254
1255 FileContainerFree(fstate->files);
1256
1257 SCFree(s);
1258#ifdef DEBUG
1259 SCMutexLock(&ftpdata_state_mem_lock);
1260 ftpdata_state_memcnt--;
1261 ftpdata_state_memuse-=sizeof(FtpDataState);
1262 SCMutexUnlock(&ftpdata_state_mem_lock);
1263#endif
1264}
1265
7548944b 1266static int FTPDataSetTxDetectState(void *vtx, DetectEngineState *de_state)
b0a69344 1267{
7548944b 1268 FtpDataState *ftp_state = (FtpDataState *)vtx;
b0a69344
EL
1269 ftp_state->de_state = de_state;
1270 return 0;
1271}
1272
1273static DetectEngineState *FTPDataGetTxDetectState(void *vtx)
1274{
1275 FtpDataState *ftp_state = (FtpDataState *)vtx;
1276 return ftp_state->de_state;
1277}
1278
1279static void FTPDataStateTransactionFree(void *state, uint64_t tx_id)
1280{
1281 /* do nothing */
1282}
1283
1284static void *FTPDataGetTx(void *state, uint64_t tx_id)
1285{
1286 FtpDataState *ftp_state = (FtpDataState *)state;
1287 return ftp_state;
1288}
1289
1290static uint64_t FTPDataGetTxCnt(void *state)
1291{
1292 /* ftp-data is single tx */
1293 return 1;
1294}
1295
1296static int FTPDataGetAlstateProgressCompletionStatus(uint8_t direction)
1297{
1298 return FTPDATA_STATE_FINISHED;
1299}
1300
1301static int FTPDataGetAlstateProgress(void *tx, uint8_t direction)
1302{
1303 FtpDataState *ftpdata_state = (FtpDataState *)tx;
1304 return ftpdata_state->state;
1305}
1306
1307static FileContainer *FTPDataStateGetFiles(void *state, uint8_t direction)
1308{
1309 FtpDataState *ftpdata_state = (FtpDataState *)state;
1310
2515c892
EL
1311 if (direction != ftpdata_state->direction)
1312 SCReturnPtr(NULL, "FileContainer");
1313
b0a69344
EL
1314 SCReturnPtr(ftpdata_state->files, "FileContainer");
1315}
1316
09ab032a
JL
1317static void FTPSetMpmState(void)
1318{
1319 ftp_mpm_ctx = SCMalloc(sizeof(MpmCtx));
1320 if (unlikely(ftp_mpm_ctx == NULL)) {
1321 exit(EXIT_FAILURE);
1322 }
1323 memset(ftp_mpm_ctx, 0, sizeof(MpmCtx));
1324 MpmInitCtx(ftp_mpm_ctx, FTP_MPM);
1325
1326 uint32_t i = 0;
1327 for (i = 0; i < sizeof(FtpCommands)/sizeof(FtpCommand) - 1; i++) {
1328 const FtpCommand *cmd = &FtpCommands[i];
86deaefe
JL
1329 if (cmd->command_length == 0)
1330 continue;
1331
09ab032a
JL
1332 MpmAddPatternCI(ftp_mpm_ctx,
1333 (uint8_t *)cmd->command_name,
1334 cmd->command_length,
1335 0 /* defunct */, 0 /* defunct */,
1336 i /* id */, i /* rule id */ , 0 /* no flags */);
1337 }
1338
1339 mpm_table[FTP_MPM].Prepare(ftp_mpm_ctx);
1340
1341}
1342
1343static void FTPFreeMpmState(void)
1344{
1345 if (ftp_mpm_ctx != NULL) {
1346 mpm_table[FTP_MPM].DestroyCtx(ftp_mpm_ctx);
1347 SCFree(ftp_mpm_ctx);
1348 ftp_mpm_ctx = NULL;
1349 }
1350}
1351
8f1d7503
KS
1352void RegisterFTPParsers(void)
1353{
ab1200fb 1354 const char *proto_name = "ftp";
b0a69344 1355 const char *proto_data_name = "ftp-data";
10966245 1356
000ce98c 1357 /** FTP */
429c6388
AS
1358 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
1359 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);
1360 if (FTPRegisterPatternsForProtocolDetection() < 0 )
1361 return;
b0a69344 1362 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTPDATA, proto_data_name);
ddde572f
AS
1363 }
1364
429c6388
AS
1365 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
1366 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
1367 FTPParseRequest);
1368 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
1369 FTPParseResponse);
1370 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
1371 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
402eb645
VJ
1372
1373 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTP, FTPStateTransactionFree);
1374
7548944b
VJ
1375 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_FTP,
1376 FTPGetTxDetectState, FTPSetTxDetectState);
402eb645 1377
1930b1f5
JL
1378 AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_FTP,
1379 FTPGetTxDetectFlags, FTPSetTxDetectFlags);
1380
402eb645 1381 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTP, FTPGetTx);
1930b1f5
JL
1382 AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateGetTxLogged,
1383 FTPStateSetTxLogged);
402eb645 1384
09ab032a
JL
1385 AppLayerParserRegisterLocalStorageFunc(IPPROTO_TCP, ALPROTO_FTP, FTPLocalStorageAlloc,
1386 FTPLocalStorageFree);
402eb645
VJ
1387 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTP, FTPGetTxCnt);
1388
1389 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTP, FTPGetAlstateProgress);
1390
1391 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTP,
1392 FTPGetAlstateProgressCompletionStatus);
b0a69344
EL
1393
1394
1395 AppLayerRegisterExpectationProto(IPPROTO_TCP, ALPROTO_FTPDATA);
1396 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER,
1397 FTPDataParseRequest);
1398 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOCLIENT,
1399 FTPDataParseResponse);
1400 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateAlloc, FTPDataStateFree);
1401 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTPDATA, STREAM_TOSERVER | STREAM_TOCLIENT);
1402 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateTransactionFree);
7548944b 1403 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_FTPDATA,
b0a69344
EL
1404 FTPDataGetTxDetectState, FTPDataSetTxDetectState);
1405
1406 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataStateGetFiles);
1407
1408 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTx);
1409
1410 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetTxCnt);
1411
1412 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_FTPDATA, FTPDataGetAlstateProgress);
1413
1414 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_FTPDATA,
1415 FTPDataGetAlstateProgressCompletionStatus);
1416
1417 sbcfg.buf_size = 4096;
711b6fb3
EL
1418 sbcfg.Malloc = FTPMalloc;
1419 sbcfg.Calloc = FTPCalloc;
1420 sbcfg.Realloc = FTPRealloc;
1421 sbcfg.Free = FTPFree;
b0a69344 1422
711b6fb3 1423 FTPParseMemcap();
ddde572f
AS
1424 } else {
1425 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
1426 "still on.", proto_name);
1427 }
09ab032a
JL
1428
1429 FTPSetMpmState();
1430
9faa4b74 1431#ifdef UNITTESTS
429c6388 1432 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
9faa4b74 1433#endif
f2f9b832
PR
1434}
1435
8f1d7503
KS
1436void FTPAtExitPrintStats(void)
1437{
f2f9b832
PR
1438#ifdef DEBUG
1439 SCMutexLock(&ftp_state_mem_lock);
1440 SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
1441 ftp_state_memcnt, ftp_state_memuse);
1442 SCMutexUnlock(&ftp_state_mem_lock);
1443#endif
1444}
1445
b0a69344 1446
3df2b343
JL
1447/*
1448 * \brief Returns the ending offset of the next line from a multi-line buffer.
1449 *
1450 * "Buffer" refers to a FTP response in a single buffer containing multiple lines.
1451 * Here, "next line" is defined as terminating on
1452 * - Newline character
1453 * - Null character
1454 *
1455 * \param buffer Contains zero or more characters.
1456 * \param len Size, in bytes, of buffer.
1457 *
1458 * \retval Offset from the start of buffer indicating the where the
1459 * next "line ends". The characters between the input buffer and this
1460 * value comprise the line.
1461 *
86fabef0 1462 * NULL is found first or a newline isn't found, then UINT16_MAX is returned.
3df2b343
JL
1463 */
1464uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len)
1465{
86fabef0
JL
1466 if (!buffer || *buffer == '\0') {
1467 return UINT16_MAX;
1468 }
3df2b343 1469
86fabef0
JL
1470 char *c = strchr(buffer, '\n');
1471 return c == NULL ? len : c - buffer + 1;
3df2b343
JL
1472}
1473
b0a69344
EL
1474json_t *JsonFTPDataAddMetadata(const Flow *f)
1475{
1476 const FtpDataState *ftp_state = NULL;
1477 if (f->alstate == NULL)
1478 return NULL;
1479 ftp_state = (FtpDataState *)f->alstate;
1480 json_t *ftpd = json_object();
1481 if (ftpd == NULL)
1482 return NULL;
1483 if (ftp_state->file_name) {
7e1235c9
VJ
1484 size_t size = ftp_state->file_len * 2 + 1;
1485 char string[size];
1486 BytesToStringBuffer(ftp_state->file_name, ftp_state->file_len, string, size);
1487 json_object_set_new(ftpd, "filename", SCJsonString(string));
b0a69344
EL
1488 }
1489 switch (ftp_state->command) {
1490 case FTP_COMMAND_STOR:
1491 json_object_set_new(ftpd, "command", json_string("STOR"));
1492 break;
1493 case FTP_COMMAND_RETR:
1494 json_object_set_new(ftpd, "command", json_string("RETR"));
1495 break;
1496 default:
1497 break;
1498 }
1499 return ftpd;
1500}
b0a69344 1501
09ab032a
JL
1502/**
1503 * \brief Free memory allocated for global SMTP parser state.
1504 */
1505void FTPParserCleanup(void)
1506{
1507 FTPFreeMpmState();
1508}
1509
f2f9b832
PR
1510/* UNITTESTS */
1511#ifdef UNITTESTS
1512
1513/** \test Send a get request in one chunk. */
ab1200fb 1514static int FTPParserTest01(void)
8f1d7503 1515{
f2f9b832
PR
1516 int result = 1;
1517 Flow f;
1518 uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
1519 uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
1520 TcpSession ssn;
8dbf7a0d 1521 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1522
1523 memset(&f, 0, sizeof(f));
1524 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1525
1526 FLOW_INITIALIZE(&f);
f2f9b832 1527 f.protoctx = (void *)&ssn;
429c6388 1528 f.proto = IPPROTO_TCP;
5c01b409 1529 f.alproto = ALPROTO_FTP;
f2f9b832 1530
6a53ab9c 1531 StreamTcpInitConfig(TRUE);
6a53ab9c 1532
6530c3d0 1533 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1534 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1535 STREAM_TOSERVER | STREAM_EOF, ftpbuf, ftplen);
f2f9b832 1536 if (r != 0) {
f729d6f7 1537 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1538 result = 0;
6530c3d0 1539 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1540 goto end;
1541 }
6530c3d0 1542 FLOWLOCK_UNLOCK(&f);
f2f9b832 1543
06904c90 1544 FtpState *ftp_state = f.alstate;
f2f9b832 1545 if (ftp_state == NULL) {
f729d6f7 1546 SCLogDebug("no ftp state: ");
f2f9b832
PR
1547 result = 0;
1548 goto end;
1549 }
1550
1551 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1552 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1553 result = 0;
1554 goto end;
1555 }
1556
1557end:
429c6388 1558 if (alp_tctx != NULL)
fdefb65b 1559 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1560 StreamTcpFreeConfig(TRUE);
0e4235cc 1561 FLOW_DESTROY(&f);
f2f9b832
PR
1562 return result;
1563}
1564
1930b1f5 1565/** \test Send a split get request. */
ab1200fb 1566static int FTPParserTest03(void)
8f1d7503 1567{
f2f9b832
PR
1568 int result = 1;
1569 Flow f;
1570 uint8_t ftpbuf1[] = "POR";
1571 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1572 uint8_t ftpbuf2[] = "T 192,168,1";
1573 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1574 uint8_t ftpbuf3[] = "1,1,10,20\r\n";
1575 uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
1576 TcpSession ssn;
8dbf7a0d 1577 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1578
1579 memset(&f, 0, sizeof(f));
1580 memset(&ssn, 0, sizeof(ssn));
48b3cb04
VJ
1581
1582 FLOW_INITIALIZE(&f);
f2f9b832 1583 f.protoctx = (void *)&ssn;
429c6388 1584 f.proto = IPPROTO_TCP;
5c01b409 1585 f.alproto = ALPROTO_FTP;
f2f9b832 1586
6a53ab9c 1587 StreamTcpInitConfig(TRUE);
6a53ab9c 1588
6530c3d0 1589 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1590 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1591 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1592 ftplen1);
f2f9b832 1593 if (r != 0) {
f729d6f7 1594 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1595 result = 0;
6530c3d0 1596 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1597 goto end;
1598 }
6530c3d0 1599 FLOWLOCK_UNLOCK(&f);
f2f9b832 1600
6530c3d0 1601 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1602 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER,
1603 ftpbuf2, ftplen2);
f2f9b832 1604 if (r != 0) {
f729d6f7 1605 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1606 result = 0;
6530c3d0 1607 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1608 goto end;
1609 }
6530c3d0 1610 FLOWLOCK_UNLOCK(&f);
f2f9b832 1611
6530c3d0 1612 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1613 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1614 STREAM_TOSERVER | STREAM_EOF, ftpbuf3, ftplen3);
f2f9b832 1615 if (r != 0) {
f729d6f7 1616 SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1617 result = 0;
6530c3d0 1618 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1619 goto end;
1620 }
6530c3d0 1621 FLOWLOCK_UNLOCK(&f);
f2f9b832 1622
06904c90 1623 FtpState *ftp_state = f.alstate;
f2f9b832 1624 if (ftp_state == NULL) {
f729d6f7 1625 SCLogDebug("no ftp state: ");
f2f9b832
PR
1626 result = 0;
1627 goto end;
1628 }
1629
1630 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1631 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1632 result = 0;
1633 goto end;
1634 }
1635
1636end:
429c6388 1637 if (alp_tctx != NULL)
fdefb65b 1638 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1639 StreamTcpFreeConfig(TRUE);
f2f9b832
PR
1640 return result;
1641}
1642
1643/** \test See how it deals with an incomplete request. */
ab1200fb 1644static int FTPParserTest06(void)
8f1d7503 1645{
f2f9b832
PR
1646 int result = 1;
1647 Flow f;
1648 uint8_t ftpbuf1[] = "PORT";
1649 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1650 TcpSession ssn;
8dbf7a0d 1651 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1652
1653 memset(&f, 0, sizeof(f));
1654 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1655
1656 FLOW_INITIALIZE(&f);
f2f9b832 1657 f.protoctx = (void *)&ssn;
429c6388 1658 f.proto = IPPROTO_TCP;
5c01b409 1659 f.alproto = ALPROTO_FTP;
f2f9b832 1660
6a53ab9c 1661 StreamTcpInitConfig(TRUE);
6a53ab9c 1662
6530c3d0 1663 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1664 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1665 STREAM_TOSERVER | STREAM_START | STREAM_EOF,
1666 ftpbuf1,
1667 ftplen1);
f2f9b832 1668 if (r != 0) {
f729d6f7 1669 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1670 result = 0;
6530c3d0 1671 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1672 goto end;
1673 }
6530c3d0 1674 FLOWLOCK_UNLOCK(&f);
f2f9b832 1675
06904c90 1676 FtpState *ftp_state = f.alstate;
f2f9b832 1677 if (ftp_state == NULL) {
f729d6f7 1678 SCLogDebug("no ftp state: ");
f2f9b832
PR
1679 result = 0;
1680 goto end;
1681 }
1682
1683 if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
f729d6f7 1684 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
f2f9b832
PR
1685 result = 0;
1686 goto end;
1687 }
1688
1689end:
429c6388 1690 if (alp_tctx != NULL)
fdefb65b 1691 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1692 StreamTcpFreeConfig(TRUE);
0e4235cc 1693 FLOW_DESTROY(&f);
f2f9b832
PR
1694 return result;
1695}
1696
1697/** \test See how it deals with an incomplete request in multiple chunks. */
ab1200fb 1698static int FTPParserTest07(void)
8f1d7503 1699{
f2f9b832
PR
1700 int result = 1;
1701 Flow f;
1702 uint8_t ftpbuf1[] = "PO";
1703 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1704 uint8_t ftpbuf2[] = "RT\r\n";
1705 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
1706 TcpSession ssn;
8dbf7a0d 1707 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1708
1709 memset(&f, 0, sizeof(f));
1710 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1711
1712 FLOW_INITIALIZE(&f);
f2f9b832 1713 f.protoctx = (void *)&ssn;
429c6388 1714 f.proto = IPPROTO_TCP;
5c01b409 1715 f.alproto = ALPROTO_FTP;
f2f9b832 1716
6a53ab9c 1717 StreamTcpInitConfig(TRUE);
6a53ab9c 1718
6530c3d0 1719 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1720 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1721 STREAM_TOSERVER | STREAM_START, ftpbuf1,
1722 ftplen1);
f2f9b832 1723 if (r != 0) {
f729d6f7 1724 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1725 result = 0;
6530c3d0 1726 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1727 goto end;
1728 }
6530c3d0 1729 FLOWLOCK_UNLOCK(&f);
f2f9b832 1730
6530c3d0 1731 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1732 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP,
1733 STREAM_TOSERVER | STREAM_EOF, ftpbuf2, ftplen2);
f2f9b832 1734 if (r != 0) {
f729d6f7 1735 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 1736 result = 0;
6530c3d0 1737 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1738 goto end;
1739 }
6530c3d0 1740 FLOWLOCK_UNLOCK(&f);
f2f9b832 1741
06904c90 1742 FtpState *ftp_state = f.alstate;
f2f9b832 1743 if (ftp_state == NULL) {
f729d6f7 1744 SCLogDebug("no ftp state: ");
f2f9b832
PR
1745 result = 0;
1746 goto end;
1747 }
1748
6ea8ac44
AS
1749 if (ftp_state->command != FTP_COMMAND_PORT) {
1750 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
1751 FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1752 result = 0;
1753 goto end;
1754 }
1755
1756end:
429c6388 1757 if (alp_tctx != NULL)
fdefb65b 1758 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1759 StreamTcpFreeConfig(TRUE);
0e4235cc 1760 FLOW_DESTROY(&f);
f2f9b832
PR
1761 return result;
1762}
1763
1764/** \test Test case where chunks are smaller than the delim length and the
1765 * last chunk is supposed to match the delim. */
ab1200fb 1766static int FTPParserTest10(void)
8f1d7503 1767{
f2f9b832
PR
1768 int result = 1;
1769 Flow f;
1770 uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
1771 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
1772 TcpSession ssn;
8dbf7a0d 1773 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
1774 int r = 0;
1775 memset(&f, 0, sizeof(f));
1776 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
1777
1778 FLOW_INITIALIZE(&f);
f2f9b832 1779 f.protoctx = (void *)&ssn;
429c6388 1780 f.proto = IPPROTO_TCP;
5c01b409 1781 f.alproto = ALPROTO_FTP;
f2f9b832 1782
6a53ab9c 1783 StreamTcpInitConfig(TRUE);
6a53ab9c 1784
f2f9b832
PR
1785 uint32_t u;
1786 for (u = 0; u < ftplen1; u++) {
1787 uint8_t flags = 0;
1788
1789 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1790 else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1791 else flags = STREAM_TOSERVER;
1792
6530c3d0 1793 FLOWLOCK_WRLOCK(&f);
675fa564
GL
1794 r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_FTP, flags,
1795 &ftpbuf1[u], 1);
f2f9b832 1796 if (r != 0) {
f729d6f7 1797 SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
f2f9b832 1798 result = 0;
6530c3d0 1799 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1800 goto end;
1801 }
6530c3d0 1802 FLOWLOCK_UNLOCK(&f);
f2f9b832
PR
1803 }
1804
06904c90 1805 FtpState *ftp_state = f.alstate;
f2f9b832 1806 if (ftp_state == NULL) {
f729d6f7 1807 SCLogDebug("no ftp state: ");
f2f9b832
PR
1808 result = 0;
1809 goto end;
1810 }
1811
1812 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 1813 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
1814 result = 0;
1815 goto end;
1816 }
1817
1818end:
429c6388 1819 if (alp_tctx != NULL)
fdefb65b 1820 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 1821 StreamTcpFreeConfig(TRUE);
0e4235cc 1822 FLOW_DESTROY(&f);
f2f9b832
PR
1823 return result;
1824}
1825#endif /* UNITTESTS */
1826
8f1d7503
KS
1827void FTPParserRegisterTests(void)
1828{
f2f9b832 1829#ifdef UNITTESTS
796dd522
JI
1830 UtRegisterTest("FTPParserTest01", FTPParserTest01);
1831 UtRegisterTest("FTPParserTest03", FTPParserTest03);
1832 UtRegisterTest("FTPParserTest06", FTPParserTest06);
1833 UtRegisterTest("FTPParserTest07", FTPParserTest07);
1834 UtRegisterTest("FTPParserTest10", FTPParserTest10);
f2f9b832
PR
1835#endif /* UNITTESTS */
1836}
1837