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