]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-ftp.c
app-layer: improve EOF handling
[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
AS
221
222 state->input = input;
223 state->input_len = input_len;
224 /* toserver stream */
225 state->direction = 0;
226
227 while (FTPGetLine(state) >= 0) {
228 FTPParseRequestCommand(state,
229 state->current_line, state->current_line_len);
230 if (state->command == FTP_COMMAND_PORT) {
231 if (state->current_line_len > state->port_line_size) {
1f07d152
EL
232 ptmp = SCRealloc(state->port_line, state->current_line_len);
233 if (ptmp == NULL) {
234 SCFree(state->port_line);
235 state->port_line = NULL;
6ea8ac44
AS
236 state->port_line_size = 0;
237 return 0;
238 }
1f07d152
EL
239 state->port_line = ptmp;
240
6ea8ac44
AS
241 state->port_line_size = state->current_line_len;
242 }
243 memcpy(state->port_line, state->current_line,
244 state->current_line_len);
245 state->port_line_len = state->current_line_len;
246 }
f2f9b832 247 }
f2f9b832 248
f2f9b832
PR
249 return 1;
250}
251
252/**
253 * \brief This function is called to retrieve a ftp response
254 * \param ftp_state the ftp state structure for the parser
255 * \param input input line of the command
256 * \param input_len length of the request
257 * \param output the resulting output
258 *
259 * \retval 1 when the command is parsed, 0 otherwise
260 */
9634e60e 261static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate,
f2f9b832 262 uint8_t *input, uint32_t input_len,
429c6388 263 void *local_data)
9a6aef45 264{
f2f9b832
PR
265 return 1;
266}
267
268#ifdef DEBUG
5532af46 269static SCMutex ftp_state_mem_lock = SCMUTEX_INITIALIZER;
f2f9b832
PR
270static uint64_t ftp_state_memuse = 0;
271static uint64_t ftp_state_memcnt = 0;
272#endif
273
8f1d7503
KS
274static void *FTPStateAlloc(void)
275{
25a3a5c6 276 void *s = SCMalloc(sizeof(FtpState));
e176be6f 277 if (unlikely(s == NULL))
f2f9b832
PR
278 return NULL;
279
280 memset(s, 0, sizeof(FtpState));
281
282#ifdef DEBUG
283 SCMutexLock(&ftp_state_mem_lock);
284 ftp_state_memcnt++;
285 ftp_state_memuse+=sizeof(FtpState);
286 SCMutexUnlock(&ftp_state_mem_lock);
287#endif
288 return s;
289}
290
8f1d7503
KS
291static void FTPStateFree(void *s)
292{
18954a2c
PR
293 FtpState *fstate = (FtpState *) s;
294 if (fstate->port_line != NULL)
295 SCFree(fstate->port_line);
b4ab9a0a
VJ
296 if (fstate->line_state[0].db)
297 SCFree(fstate->line_state[0].db);
298 if (fstate->line_state[1].db)
299 SCFree(fstate->line_state[1].db);
25a3a5c6 300 SCFree(s);
f2f9b832
PR
301#ifdef DEBUG
302 SCMutexLock(&ftp_state_mem_lock);
303 ftp_state_memcnt--;
304 ftp_state_memuse-=sizeof(FtpState);
305 SCMutexUnlock(&ftp_state_mem_lock);
306#endif
307}
308
429c6388
AS
309static int FTPRegisterPatternsForProtocolDetection(void)
310{
311 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
312 "USER ", 5, 0, STREAM_TOSERVER) < 0)
313 {
314 return -1;
315 }
316 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
317 "PASS ", 5, 0, STREAM_TOSERVER) < 0)
318 {
319 return -1;
320 }
321 if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_FTP,
322 "PORT ", 5, 0, STREAM_TOSERVER) < 0)
323 {
324 return -1;
325 }
326
327 return 0;
328}
329
8f1d7503
KS
330void RegisterFTPParsers(void)
331{
10966245
AS
332 char *proto_name = "ftp";
333
000ce98c 334 /** FTP */
429c6388
AS
335 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
336 AppLayerProtoDetectRegisterProtocol(ALPROTO_FTP, proto_name);
337 if (FTPRegisterPatternsForProtocolDetection() < 0 )
338 return;
ddde572f
AS
339 }
340
429c6388
AS
341 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
342 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER,
343 FTPParseRequest);
344 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOCLIENT,
345 FTPParseResponse);
346 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_FTP, FTPStateAlloc, FTPStateFree);
347 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_FTP, STREAM_TOSERVER | STREAM_TOCLIENT);
ddde572f
AS
348 } else {
349 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
350 "still on.", proto_name);
351 }
9faa4b74 352#ifdef UNITTESTS
429c6388 353 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_FTP, FTPParserRegisterTests);
9faa4b74 354#endif
f2f9b832
PR
355}
356
8f1d7503
KS
357void FTPAtExitPrintStats(void)
358{
f2f9b832
PR
359#ifdef DEBUG
360 SCMutexLock(&ftp_state_mem_lock);
361 SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"",
362 ftp_state_memcnt, ftp_state_memuse);
363 SCMutexUnlock(&ftp_state_mem_lock);
364#endif
365}
366
367/* UNITTESTS */
368#ifdef UNITTESTS
369
370/** \test Send a get request in one chunk. */
8f1d7503
KS
371int FTPParserTest01(void)
372{
f2f9b832
PR
373 int result = 1;
374 Flow f;
375 uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n";
376 uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */
377 TcpSession ssn;
8dbf7a0d 378 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
379
380 memset(&f, 0, sizeof(f));
381 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
382
383 FLOW_INITIALIZE(&f);
f2f9b832 384 f.protoctx = (void *)&ssn;
429c6388 385 f.proto = IPPROTO_TCP;
f2f9b832 386
6a53ab9c 387 StreamTcpInitConfig(TRUE);
6a53ab9c 388
cd3e32ce 389 SCMutexLock(&f.m);
429c6388 390 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf, ftplen);
f2f9b832 391 if (r != 0) {
f729d6f7 392 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 393 result = 0;
cd3e32ce 394 SCMutexUnlock(&f.m);
f2f9b832
PR
395 goto end;
396 }
cd3e32ce 397 SCMutexUnlock(&f.m);
f2f9b832 398
06904c90 399 FtpState *ftp_state = f.alstate;
f2f9b832 400 if (ftp_state == NULL) {
f729d6f7 401 SCLogDebug("no ftp state: ");
f2f9b832
PR
402 result = 0;
403 goto end;
404 }
405
406 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 407 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
408 result = 0;
409 goto end;
410 }
411
412end:
429c6388 413 if (alp_tctx != NULL)
fdefb65b 414 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 415 StreamTcpFreeConfig(TRUE);
0e4235cc 416 FLOW_DESTROY(&f);
f2f9b832
PR
417 return result;
418}
419
420/** \test Send a splitted get request. */
8f1d7503
KS
421int FTPParserTest03(void)
422{
f2f9b832
PR
423 int result = 1;
424 Flow f;
425 uint8_t ftpbuf1[] = "POR";
426 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
427 uint8_t ftpbuf2[] = "T 192,168,1";
428 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
429 uint8_t ftpbuf3[] = "1,1,10,20\r\n";
430 uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */
431 TcpSession ssn;
8dbf7a0d 432 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
433
434 memset(&f, 0, sizeof(f));
435 memset(&ssn, 0, sizeof(ssn));
f2f9b832 436 f.protoctx = (void *)&ssn;
429c6388 437 f.proto = IPPROTO_TCP;
f2f9b832 438
6a53ab9c 439 StreamTcpInitConfig(TRUE);
6a53ab9c 440
cd3e32ce 441 SCMutexLock(&f.m);
429c6388 442 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
f2f9b832 443 if (r != 0) {
f729d6f7 444 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 445 result = 0;
cd3e32ce 446 SCMutexUnlock(&f.m);
f2f9b832
PR
447 goto end;
448 }
cd3e32ce 449 SCMutexUnlock(&f.m);
f2f9b832 450
cd3e32ce 451 SCMutexLock(&f.m);
429c6388 452 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2);
f2f9b832 453 if (r != 0) {
f729d6f7 454 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 455 result = 0;
cd3e32ce 456 SCMutexUnlock(&f.m);
f2f9b832
PR
457 goto end;
458 }
cd3e32ce 459 SCMutexUnlock(&f.m);
f2f9b832 460
cd3e32ce 461 SCMutexLock(&f.m);
429c6388 462 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf3, ftplen3);
f2f9b832 463 if (r != 0) {
f729d6f7 464 SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
f2f9b832 465 result = 0;
cd3e32ce 466 SCMutexUnlock(&f.m);
f2f9b832
PR
467 goto end;
468 }
cd3e32ce 469 SCMutexUnlock(&f.m);
f2f9b832 470
06904c90 471 FtpState *ftp_state = f.alstate;
f2f9b832 472 if (ftp_state == NULL) {
f729d6f7 473 SCLogDebug("no ftp state: ");
f2f9b832
PR
474 result = 0;
475 goto end;
476 }
477
478 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 479 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
480 result = 0;
481 goto end;
482 }
483
484end:
429c6388 485 if (alp_tctx != NULL)
fdefb65b 486 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 487 StreamTcpFreeConfig(TRUE);
f2f9b832
PR
488 return result;
489}
490
491/** \test See how it deals with an incomplete request. */
8f1d7503
KS
492int FTPParserTest06(void)
493{
f2f9b832
PR
494 int result = 1;
495 Flow f;
496 uint8_t ftpbuf1[] = "PORT";
497 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
498 TcpSession ssn;
8dbf7a0d 499 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
500
501 memset(&f, 0, sizeof(f));
502 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
503
504 FLOW_INITIALIZE(&f);
f2f9b832 505 f.protoctx = (void *)&ssn;
429c6388 506 f.proto = IPPROTO_TCP;
f2f9b832 507
6a53ab9c 508 StreamTcpInitConfig(TRUE);
6a53ab9c 509
cd3e32ce 510 SCMutexLock(&f.m);
429c6388 511 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, ftpbuf1, ftplen1);
f2f9b832 512 if (r != 0) {
f729d6f7 513 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 514 result = 0;
cd3e32ce 515 SCMutexUnlock(&f.m);
f2f9b832
PR
516 goto end;
517 }
cd3e32ce 518 SCMutexUnlock(&f.m);
f2f9b832 519
06904c90 520 FtpState *ftp_state = f.alstate;
f2f9b832 521 if (ftp_state == NULL) {
f729d6f7 522 SCLogDebug("no ftp state: ");
f2f9b832
PR
523 result = 0;
524 goto end;
525 }
526
527 if (ftp_state->command != FTP_COMMAND_UNKNOWN) {
f729d6f7 528 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command);
f2f9b832
PR
529 result = 0;
530 goto end;
531 }
532
533end:
429c6388 534 if (alp_tctx != NULL)
fdefb65b 535 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 536 StreamTcpFreeConfig(TRUE);
0e4235cc 537 FLOW_DESTROY(&f);
f2f9b832
PR
538 return result;
539}
540
541/** \test See how it deals with an incomplete request in multiple chunks. */
8f1d7503
KS
542int FTPParserTest07(void)
543{
f2f9b832
PR
544 int result = 1;
545 Flow f;
546 uint8_t ftpbuf1[] = "PO";
547 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
548 uint8_t ftpbuf2[] = "RT\r\n";
549 uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */
550 TcpSession ssn;
8dbf7a0d 551 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
552
553 memset(&f, 0, sizeof(f));
554 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
555
556 FLOW_INITIALIZE(&f);
f2f9b832 557 f.protoctx = (void *)&ssn;
429c6388 558 f.proto = IPPROTO_TCP;
f2f9b832 559
6a53ab9c 560 StreamTcpInitConfig(TRUE);
6a53ab9c 561
cd3e32ce 562 SCMutexLock(&f.m);
429c6388 563 int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1);
f2f9b832 564 if (r != 0) {
f729d6f7 565 SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
f2f9b832 566 result = 0;
cd3e32ce 567 SCMutexUnlock(&f.m);
f2f9b832
PR
568 goto end;
569 }
cd3e32ce 570 SCMutexUnlock(&f.m);
f2f9b832 571
cd3e32ce 572 SCMutexLock(&f.m);
429c6388 573 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf2, ftplen2);
f2f9b832 574 if (r != 0) {
f729d6f7 575 SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
f2f9b832 576 result = 0;
cd3e32ce 577 SCMutexUnlock(&f.m);
f2f9b832
PR
578 goto end;
579 }
cd3e32ce 580 SCMutexUnlock(&f.m);
f2f9b832 581
06904c90 582 FtpState *ftp_state = f.alstate;
f2f9b832 583 if (ftp_state == NULL) {
f729d6f7 584 SCLogDebug("no ftp state: ");
f2f9b832
PR
585 result = 0;
586 goto end;
587 }
588
6ea8ac44
AS
589 if (ftp_state->command != FTP_COMMAND_PORT) {
590 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ",
591 FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
592 result = 0;
593 goto end;
594 }
595
596end:
429c6388 597 if (alp_tctx != NULL)
fdefb65b 598 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 599 StreamTcpFreeConfig(TRUE);
0e4235cc 600 FLOW_DESTROY(&f);
f2f9b832
PR
601 return result;
602}
603
604/** \test Test case where chunks are smaller than the delim length and the
605 * last chunk is supposed to match the delim. */
8f1d7503
KS
606int FTPParserTest10(void)
607{
f2f9b832
PR
608 int result = 1;
609 Flow f;
610 uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n";
611 uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */
612 TcpSession ssn;
8dbf7a0d 613 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
f2f9b832
PR
614 int r = 0;
615 memset(&f, 0, sizeof(f));
616 memset(&ssn, 0, sizeof(ssn));
cc76aa4b
WM
617
618 FLOW_INITIALIZE(&f);
f2f9b832 619 f.protoctx = (void *)&ssn;
429c6388 620 f.proto = IPPROTO_TCP;
f2f9b832 621
6a53ab9c 622 StreamTcpInitConfig(TRUE);
6a53ab9c 623
f2f9b832
PR
624 uint32_t u;
625 for (u = 0; u < ftplen1; u++) {
626 uint8_t flags = 0;
627
628 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
629 else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
630 else flags = STREAM_TOSERVER;
631
cd3e32ce 632 SCMutexLock(&f.m);
429c6388 633 r = AppLayerParserParse(alp_tctx, &f, ALPROTO_FTP, flags, &ftpbuf1[u], 1);
f2f9b832 634 if (r != 0) {
f729d6f7 635 SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r);
f2f9b832 636 result = 0;
cd3e32ce 637 SCMutexUnlock(&f.m);
f2f9b832
PR
638 goto end;
639 }
cd3e32ce 640 SCMutexUnlock(&f.m);
f2f9b832
PR
641 }
642
06904c90 643 FtpState *ftp_state = f.alstate;
f2f9b832 644 if (ftp_state == NULL) {
f729d6f7 645 SCLogDebug("no ftp state: ");
f2f9b832
PR
646 result = 0;
647 goto end;
648 }
649
650 if (ftp_state->command != FTP_COMMAND_PORT) {
f729d6f7 651 SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command);
f2f9b832
PR
652 result = 0;
653 goto end;
654 }
655
656end:
429c6388 657 if (alp_tctx != NULL)
fdefb65b 658 AppLayerParserThreadCtxFree(alp_tctx);
6a53ab9c 659 StreamTcpFreeConfig(TRUE);
0e4235cc 660 FLOW_DESTROY(&f);
f2f9b832
PR
661 return result;
662}
663#endif /* UNITTESTS */
664
8f1d7503
KS
665void FTPParserRegisterTests(void)
666{
f2f9b832
PR
667#ifdef UNITTESTS
668 UtRegisterTest("FTPParserTest01", FTPParserTest01, 1);
669 UtRegisterTest("FTPParserTest03", FTPParserTest03, 1);
670 UtRegisterTest("FTPParserTest06", FTPParserTest06, 1);
671 UtRegisterTest("FTPParserTest07", FTPParserTest07, 1);
672 UtRegisterTest("FTPParserTest10", FTPParserTest10, 1);
673#endif /* UNITTESTS */
674}
675