]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-ftp.c
erspan: respect vlan.use-for-tracking setting
[people/ms/suricata.git] / src / app-layer-ftp.c
CommitLineData
5532af46 1/* Copyright (C) 2007-2013 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
PR
21 * \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
22 *
23 * App Layer Parser for FTP
24 */
25
26#include "suricata-common.h"
27#include "debug.h"
28#include "decode.h"
29#include "threads.h"
30
31#include "util-print.h"
32#include "util-pool.h"
33
cc76aa4b
WM
34#include "flow-util.h"
35
0e4235cc
WM
36#include "detect-engine-state.h"
37
f2f9b832
PR
38#include "stream-tcp-private.h"
39#include "stream-tcp-reassemble.h"
6a53ab9c 40#include "stream-tcp.h"
f2f9b832
PR
41#include "stream.h"
42
429c6388 43#include "app-layer.h"
f2f9b832
PR
44#include "app-layer-protos.h"
45#include "app-layer-parser.h"
46#include "app-layer-ftp.h"
47
705471e4 48#include "util-spm.h"
f2f9b832
PR
49#include "util-unittest.h"
50#include "util-debug.h"
1859ed54 51#include "util-memcmp.h"
f2f9b832 52
6ea8ac44
AS
53static int FTPGetLineForDirection(FtpState *state, FtpLineState *line_state)
54{
1f07d152 55 void *ptmp;
6ea8ac44
AS
56 if (line_state->current_line_lf_seen == 1) {
57 /* we have seen the lf for the previous line. Clear the parser
58 * details to parse new line */
59 line_state->current_line_lf_seen = 0;
60 if (line_state->current_line_db == 1) {
61 line_state->current_line_db = 0;
62 SCFree(line_state->db);
63 line_state->db = NULL;
64 line_state->db_len = 0;
65 state->current_line = NULL;
66 state->current_line_len = 0;
67 }
68 }
69
70 uint8_t *lf_idx = memchr(state->input, 0x0a, state->input_len);
71
72 if (lf_idx == NULL) {
73 /* fragmented lines. Decoder event for special cases. Not all
74 * fragmented lines should be treated as a possible evasion
75 * attempt. With multi payload ftp chunks we can have valid
76 * cases of fragmentation. But within the same segment chunk
77 * if we see fragmentation then it's definitely something you
78 * should alert about */
79 if (line_state->current_line_db == 0) {
80 line_state->db = SCMalloc(state->input_len);
81 if (line_state->db == NULL) {
82 return -1;
83 }
84 line_state->current_line_db = 1;
85 memcpy(line_state->db, state->input, state->input_len);
86 line_state->db_len = state->input_len;
87 } else {
1f07d152
EL
88 ptmp = SCRealloc(line_state->db,
89 (line_state->db_len + state->input_len));
90 if (ptmp == NULL) {
91 SCFree(line_state->db);
92 line_state->db = NULL;
93 line_state->db_len = 0;
6ea8ac44
AS
94 return -1;
95 }
1f07d152
EL
96 line_state->db = ptmp;
97
6ea8ac44
AS
98 memcpy(line_state->db + line_state->db_len,
99 state->input, state->input_len);
100 line_state->db_len += state->input_len;
101 }
102 state->input += state->input_len;
103 state->input_len = 0;
104
105 return -1;
106
107 } else {
108 line_state->current_line_lf_seen = 1;
109
110 if (line_state->current_line_db == 1) {
1f07d152
EL
111 ptmp = SCRealloc(line_state->db,
112 (line_state->db_len + (lf_idx + 1 - state->input)));
113 if (ptmp == NULL) {
114 SCFree(line_state->db);
115 line_state->db = NULL;
116 line_state->db_len = 0;
6ea8ac44
AS
117 return -1;
118 }
1f07d152
EL
119 line_state->db = ptmp;
120
6ea8ac44
AS
121 memcpy(line_state->db + line_state->db_len,
122 state->input, (lf_idx + 1 - state->input));
123 line_state->db_len += (lf_idx + 1 - state->input);
124
125 if (line_state->db_len > 1 &&
126 line_state->db[line_state->db_len - 2] == 0x0D) {
127 line_state->db_len -= 2;
128 state->current_line_delimiter_len = 2;
129 } else {
130 line_state->db_len -= 1;
131 state->current_line_delimiter_len = 1;
132 }
133
134 state->current_line = line_state->db;
135 state->current_line_len = line_state->db_len;
136
137 } else {
138 state->current_line = state->input;
139 state->current_line_len = lf_idx - state->input;
140
141 if (state->input != lf_idx &&
142 *(lf_idx - 1) == 0x0D) {
143 state->current_line_len--;
144 state->current_line_delimiter_len = 2;
145 } else {
146 state->current_line_delimiter_len = 1;
147 }
148 }
149
150 state->input_len -= (lf_idx - state->input) + 1;
151 state->input = (lf_idx + 1);
152
153 return 0;
154 }
155
156}
157
158static int FTPGetLine(FtpState *state)
159{
160 SCEnter();
161
162 /* we have run out of input */
163 if (state->input_len <= 0)
164 return -1;
165
166 /* toserver */
167 if (state->direction == 0)
168 return FTPGetLineForDirection(state, &state->line_state[0]);
169 else
170 return FTPGetLineForDirection(state, &state->line_state[1]);
171}
172
f2f9b832
PR
173/**
174 * \brief This function is called to determine and set which command is being
175 * transfered to the ftp server
176 * \param ftp_state the ftp state structure for the parser
177 * \param input input line of the command
178 * \param len of the command
179 *
180 * \retval 1 when the command is parsed, 0 otherwise
181 */
182static int FTPParseRequestCommand(void *ftp_state, uint8_t *input,
8f1d7503
KS
183 uint32_t input_len)
184{
f2f9b832
PR
185 SCEnter();
186 FtpState *fstate = (FtpState *)ftp_state;
6ea8ac44 187 fstate->command = FTP_COMMAND_UNKNOWN;
f2f9b832 188
f2f9b832 189 if (input_len >= 4) {
1859ed54 190 if (SCMemcmpLowercase("port", input, 4) == 0) {
f2f9b832
PR
191 fstate->command = FTP_COMMAND_PORT;
192 }
9d7baa7a 193
f2f9b832
PR
194 /* else {
195 * Add the ftp commands you need here
196 * }
197 */
198 }
199 return 1;
200}
201
f2f9b832
PR
202/**
203 * \brief This function is called to retrieve a ftp request
204 * \param ftp_state the ftp state structure for the parser
205 * \param input input line of the command
206 * \param input_len length of the request
207 * \param output the resulting output
208 *
209 * \retval 1 when the command is parsed, 0 otherwise
210 */
9a6aef45 211static int FTPParseRequest(Flow *f, void *ftp_state,
9634e60e 212 AppLayerParserState *pstate,
9a6aef45 213 uint8_t *input, uint32_t input_len,
429c6388 214 void *local_data)
9a6aef45 215{
f2f9b832 216 SCEnter();
f2f9b832
PR
217 /* PrintRawDataFp(stdout, input,input_len); */
218
6ea8ac44 219 FtpState *state = (FtpState *)ftp_state;
1f07d152 220 void *ptmp;
6ea8ac44 221
4e7cb7b8
VJ
222 if (input == NULL && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
223 SCReturnInt(1);
224 }
225
6ea8ac44
AS
226 state->input = input;
227 state->input_len = input_len;
228 /* toserver stream */
229 state->direction = 0;
230
231 while (FTPGetLine(state) >= 0) {
232 FTPParseRequestCommand(state,
233 state->current_line, state->current_line_len);
234 if (state->command == FTP_COMMAND_PORT) {
235 if (state->current_line_len > state->port_line_size) {
1f07d152
EL
236 ptmp = SCRealloc(state->port_line, state->current_line_len);
237 if (ptmp == NULL) {
238 SCFree(state->port_line);
239 state->port_line = NULL;
6ea8ac44
AS
240 state->port_line_size = 0;
241 return 0;
242 }
1f07d152
EL
243 state->port_line = ptmp;
244
6ea8ac44
AS
245 state->port_line_size = state->current_line_len;
246 }
247 memcpy(state->port_line, state->current_line,
248 state->current_line_len);
249 state->port_line_len = state->current_line_len;
250 }
f2f9b832 251 }
f2f9b832 252
f2f9b832
PR
253 return 1;
254}
255
256/**
257 * \brief This function is called to retrieve a ftp response
258 * \param ftp_state the ftp state structure for the parser
259 * \param input input line of the command
260 * \param input_len length of the request
261 * \param output the resulting output
262 *
263 * \retval 1 when the command is parsed, 0 otherwise
264 */
9634e60e 265static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
f2f9b832 266 uint8_t *input, uint32_t input_len,
429c6388 267 void *local_data)
9a6aef45 268{
f2f9b832
PR
269 return 1;
270}
271
272#ifdef DEBUG
5532af46 273static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
f2f9b832
PR
274static uint64_t ftp_state_memuse = 0;
275static uint64_t ftp_state_memcnt = 0;
276#endif
277
8f1d7503
KS
278static void *FTPStateAlloc(void)
279{
25a3a5c6 280 void *s = SCMalloc(sizeof(FtpState));
e176be6f 281 if (unlikely(s == NULL))
f2f9b832
PR
282 return NULL;
283
284 memset(s, 0, sizeof(FtpState));
285
286#ifdef DEBUG
287 SCMutexLock(&ftp_state_mem_lock);
288 ftp_state_memcnt++;
289 ftp_state_memuse+=sizeof(FtpState);
290 SCMutexUnlock(&ftp_state_mem_lock);
291#endif
292 return s;
293}
294
8f1d7503
KS
295static void FTPStateFree(void *s)
296{
18954a2c
PR
297 FtpState *fstate = (FtpState *) s;
298 if (fstate->port_line != NULL)
299 SCFree(fstate->port_line);
b4ab9a0a
VJ
300 if (fstate->line_state[0].db)
301 SCFree(fstate->line_state[0].db);
302 if (fstate->line_state[1].db)
303 SCFree(fstate->line_state[1].db);
25a3a5c6 304 SCFree(s);
f2f9b832
PR
305#ifdef DEBUG
306 SCMutexLock(&ftp_state_mem_lock);
307 ftp_state_memcnt--;
308 ftp_state_memuse-=sizeof(FtpState);
309 SCMutexUnlock(&ftp_state_mem_lock);
310#endif
311}
312
429c6388
AS
313static int FTPRegisterPatternsForProtocolDetection(void)
314{
315 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
316 "USER ", 5, 0, STREAM_TOSERVER) < 0)
317 {
318 return -1;
319 }
320 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
321 "PASS ", 5, 0, STREAM_TOSERVER) < 0)
322 {
323 return -1;
324 }
325 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
326 "PORT ", 5, 0, STREAM_TOSERVER) < 0)
327 {
328 return -1;
329 }
330
331 return 0;
332}
333
8f1d7503
KS
334void RegisterFTPParsers(void)
335{
10966245
AS
336 char *proto_name = "ftp";
337
000ce98c 338 /** FTP */
429c6388
AS
339 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
340 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);
341 if (FTPRegisterPatternsForProtocolDetection() < 0 )
342 return;
ddde572f
AS
343 }
344
429c6388
AS
345 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
346 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
347 FTPParseRequest);
348 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
349 FTPParseResponse);
350 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
351 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
ddde572f
AS
352 } else {
353 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
354 "still on.", proto_name);
355 }
9faa4b74 356#ifdef UNITTESTS
429c6388 357 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
9faa4b74 358#endif
f2f9b832
PR
359}
360
8f1d7503
KS
361void FTPAtExitPrintStats(void)
362{
f2f9b832
PR
363#ifdef DEBUG
364 SCMutexLock(&ftp_state_mem_lock);
365 SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
366 ftp_state_memcnt, ftp_state_memuse);
367 SCMutexUnlock(&ftp_state_mem_lock);
368#endif
369}
370
371/* UNITTESTS */
372#ifdef UNITTESTS
373
374/** \test Send a get request in one chunk. */
8f1d7503
KS
375int FTPParserTest01(void)
376{
f2f9b832
PR
377 int result = 1;
378 Flow f;
379 uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
380 uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
381 TcpSession ssn;
8dbf7a0d 382 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
383
384 memset(&f, 0, sizeof(f));
385 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
386
387 FLOW_INITIALIZE(&f);
f2f9b832 388 f.protoctx = (void *)&ssn;
429c6388 389 f.proto = IPPROTO_TCP;
f2f9b832 390
6a53ab9c 391 StreamTcpInitConfig(TRUE);
6a53ab9c 392
cd3e32ce 393 SCMutexLock(&f.m);
429c6388 394 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf, ftplen);
f2f9b832 395 if (r != 0) {
f729d6f7 396 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 397 result = 0;
cd3e32ce 398 SCMutexUnlock(&f.m);
f2f9b832
PR
399 goto end;
400 }
cd3e32ce 401 SCMutexUnlock(&f.m);
f2f9b832 402
06904c90 403 FtpState *ftp_state = f.alstate;
f2f9b832 404 if (ftp_state == NULL) {
f729d6f7 405 SCLogDebug("no ftp state: ");
f2f9b832
PR
406 result = 0;
407 goto end;
408 }
409
410 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 411 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
412 result = 0;
413 goto end;
414 }
415
416end:
429c6388 417 if (alp_tctx != NULL)
fdefb65b 418 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 419 StreamTcpFreeConfig(TRUE);
0e4235cc 420 FLOW_DESTROY(&f);
f2f9b832
PR
421 return result;
422}
423
424/** \test Send a splitted get request. */
8f1d7503
KS
425int FTPParserTest03(void)
426{
f2f9b832
PR
427 int result = 1;
428 Flow f;
429 uint8_t ftpbuf1[] = "POR";
430 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
431 uint8_t ftpbuf2[] = "T 192,168,1";
432 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
433 uint8_t ftpbuf3[] = "1,1,10,20\r\n";
434 uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
435 TcpSession ssn;
8dbf7a0d 436 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
437
438 memset(&f, 0, sizeof(f));
439 memset(&ssn, 0, sizeof(ssn));
f2f9b832 440 f.protoctx = (void *)&ssn;
429c6388 441 f.proto = IPPROTO_TCP;
f2f9b832 442
6a53ab9c 443 StreamTcpInitConfig(TRUE);
6a53ab9c 444
cd3e32ce 445 SCMutexLock(&f.m);
429c6388 446 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
f2f9b832 447 if (r != 0) {
f729d6f7 448 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 449 result = 0;
cd3e32ce 450 SCMutexUnlock(&f.m);
f2f9b832
PR
451 goto end;
452 }
cd3e32ce 453 SCMutexUnlock(&f.m);
f2f9b832 454
cd3e32ce 455 SCMutexLock(&f.m);
429c6388 456 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
f2f9b832 457 if (r != 0) {
f729d6f7 458 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 459 result = 0;
cd3e32ce 460 SCMutexUnlock(&f.m);
f2f9b832
PR
461 goto end;
462 }
cd3e32ce 463 SCMutexUnlock(&f.m);
f2f9b832 464
cd3e32ce 465 SCMutexLock(&f.m);
429c6388 466 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf3, ftplen3);
f2f9b832 467 if (r != 0) {
f729d6f7 468 SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
f2f9b832 469 result = 0;
cd3e32ce 470 SCMutexUnlock(&f.m);
f2f9b832
PR
471 goto end;
472 }
cd3e32ce 473 SCMutexUnlock(&f.m);
f2f9b832 474
06904c90 475 FtpState *ftp_state = f.alstate;
f2f9b832 476 if (ftp_state == NULL) {
f729d6f7 477 SCLogDebug("no ftp state: ");
f2f9b832
PR
478 result = 0;
479 goto end;
480 }
481
482 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 483 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
484 result = 0;
485 goto end;
486 }
487
488end:
429c6388 489 if (alp_tctx != NULL)
fdefb65b 490 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 491 StreamTcpFreeConfig(TRUE);
f2f9b832
PR
492 return result;
493}
494
495/** \test See how it deals with an incomplete request. */
8f1d7503
KS
496int FTPParserTest06(void)
497{
f2f9b832
PR
498 int result = 1;
499 Flow f;
500 uint8_t ftpbuf1[] = "PORT";
501 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
502 TcpSession ssn;
8dbf7a0d 503 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
504
505 memset(&f, 0, sizeof(f));
506 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
507
508 FLOW_INITIALIZE(&f);
f2f9b832 509 f.protoctx = (void *)&ssn;
429c6388 510 f.proto = IPPROTO_TCP;
f2f9b832 511
6a53ab9c 512 StreamTcpInitConfig(TRUE);
6a53ab9c 513
cd3e32ce 514 SCMutexLock(&f.m);
429c6388 515 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, ftpbuf1, ftplen1);
f2f9b832 516 if (r != 0) {
f729d6f7 517 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 518 result = 0;
cd3e32ce 519 SCMutexUnlock(&f.m);
f2f9b832
PR
520 goto end;
521 }
cd3e32ce 522 SCMutexUnlock(&f.m);
f2f9b832 523
06904c90 524 FtpState *ftp_state = f.alstate;
f2f9b832 525 if (ftp_state == NULL) {
f729d6f7 526 SCLogDebug("no ftp state: ");
f2f9b832
PR
527 result = 0;
528 goto end;
529 }
530
531 if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
f729d6f7 532 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
f2f9b832
PR
533 result = 0;
534 goto end;
535 }
536
537end:
429c6388 538 if (alp_tctx != NULL)
fdefb65b 539 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 540 StreamTcpFreeConfig(TRUE);
0e4235cc 541 FLOW_DESTROY(&f);
f2f9b832
PR
542 return result;
543}
544
545/** \test See how it deals with an incomplete request in multiple chunks. */
8f1d7503
KS
546int FTPParserTest07(void)
547{
f2f9b832
PR
548 int result = 1;
549 Flow f;
550 uint8_t ftpbuf1[] = "PO";
551 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
552 uint8_t ftpbuf2[] = "RT\r\n";
553 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
554 TcpSession ssn;
8dbf7a0d 555 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
556
557 memset(&f, 0, sizeof(f));
558 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
559
560 FLOW_INITIALIZE(&f);
f2f9b832 561 f.protoctx = (void *)&ssn;
429c6388 562 f.proto = IPPROTO_TCP;
f2f9b832 563
6a53ab9c 564 StreamTcpInitConfig(TRUE);
6a53ab9c 565
cd3e32ce 566 SCMutexLock(&f.m);
429c6388 567 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
f2f9b832 568 if (r != 0) {
f729d6f7 569 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 570 result = 0;
cd3e32ce 571 SCMutexUnlock(&f.m);
f2f9b832
PR
572 goto end;
573 }
cd3e32ce 574 SCMutexUnlock(&f.m);
f2f9b832 575
cd3e32ce 576 SCMutexLock(&f.m);
429c6388 577 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf2, ftplen2);
f2f9b832 578 if (r != 0) {
f729d6f7 579 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 580 result = 0;
cd3e32ce 581 SCMutexUnlock(&f.m);
f2f9b832
PR
582 goto end;
583 }
cd3e32ce 584 SCMutexUnlock(&f.m);
f2f9b832 585
06904c90 586 FtpState *ftp_state = f.alstate;
f2f9b832 587 if (ftp_state == NULL) {
f729d6f7 588 SCLogDebug("no ftp state: ");
f2f9b832
PR
589 result = 0;
590 goto end;
591 }
592
6ea8ac44
AS
593 if (ftp_state->command != FTP_COMMAND_PORT) {
594 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
595 FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
596 result = 0;
597 goto end;
598 }
599
600end:
429c6388 601 if (alp_tctx != NULL)
fdefb65b 602 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 603 StreamTcpFreeConfig(TRUE);
0e4235cc 604 FLOW_DESTROY(&f);
f2f9b832
PR
605 return result;
606}
607
608/** \test Test case where chunks are smaller than the delim length and the
609 * last chunk is supposed to match the delim. */
8f1d7503
KS
610int FTPParserTest10(void)
611{
f2f9b832
PR
612 int result = 1;
613 Flow f;
614 uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
615 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
616 TcpSession ssn;
8dbf7a0d 617 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
618 int r = 0;
619 memset(&f, 0, sizeof(f));
620 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
621
622 FLOW_INITIALIZE(&f);
f2f9b832 623 f.protoctx = (void *)&ssn;
429c6388 624 f.proto = IPPROTO_TCP;
f2f9b832 625
6a53ab9c 626 StreamTcpInitConfig(TRUE);
6a53ab9c 627
f2f9b832
PR
628 uint32_t u;
629 for (u = 0; u < ftplen1; u++) {
630 uint8_t flags = 0;
631
632 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
633 else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
634 else flags = STREAM_TOSERVER;
635
cd3e32ce 636 SCMutexLock(&f.m);
429c6388 637 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, flags, &ftpbuf1[u], 1);
f2f9b832 638 if (r != 0) {
f729d6f7 639 SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
f2f9b832 640 result = 0;
cd3e32ce 641 SCMutexUnlock(&f.m);
f2f9b832
PR
642 goto end;
643 }
cd3e32ce 644 SCMutexUnlock(&f.m);
f2f9b832
PR
645 }
646
06904c90 647 FtpState *ftp_state = f.alstate;
f2f9b832 648 if (ftp_state == NULL) {
f729d6f7 649 SCLogDebug("no ftp state: ");
f2f9b832
PR
650 result = 0;
651 goto end;
652 }
653
654 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 655 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
656 result = 0;
657 goto end;
658 }
659
660end:
429c6388 661 if (alp_tctx != NULL)
fdefb65b 662 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 663 StreamTcpFreeConfig(TRUE);
0e4235cc 664 FLOW_DESTROY(&f);
f2f9b832
PR
665 return result;
666}
667#endif /* UNITTESTS */
668
8f1d7503
KS
669void FTPParserRegisterTests(void)
670{
f2f9b832
PR
671#ifdef UNITTESTS
672 UtRegisterTest("FTPParserTest01", FTPParserTest01, 1);
673 UtRegisterTest("FTPParserTest03", FTPParserTest03, 1);
674 UtRegisterTest("FTPParserTest06", FTPParserTest06, 1);
675 UtRegisterTest("FTPParserTest07", FTPParserTest07, 1);
676 UtRegisterTest("FTPParserTest10", FTPParserTest10, 1);
677#endif /* UNITTESTS */
678}
679