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