]>
Commit | Line | Data |
---|---|---|
ce019275 WM |
1 | /* Copyright (C) 2007-2010 Open Information Security Foundation |
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 | ||
43 | #include "app-layer-protos.h" | |
44 | #include "app-layer-parser.h" | |
45 | #include "app-layer-ftp.h" | |
46 | ||
705471e4 | 47 | #include "util-spm.h" |
f2f9b832 PR |
48 | #include "util-unittest.h" |
49 | #include "util-debug.h" | |
1859ed54 | 50 | #include "util-memcmp.h" |
f2f9b832 PR |
51 | |
52 | /** | |
53 | * \brief This function is called to determine and set which command is being | |
54 | * transfered to the ftp server | |
55 | * \param ftp_state the ftp state structure for the parser | |
56 | * \param input input line of the command | |
57 | * \param len of the command | |
58 | * | |
59 | * \retval 1 when the command is parsed, 0 otherwise | |
60 | */ | |
61 | static int FTPParseRequestCommand(void *ftp_state, uint8_t *input, | |
62 | uint32_t input_len) { | |
63 | SCEnter(); | |
64 | FtpState *fstate = (FtpState *)ftp_state; | |
65 | ||
f2f9b832 | 66 | if (input_len >= 4) { |
1859ed54 | 67 | if (SCMemcmpLowercase("port", input, 4) == 0) { |
f2f9b832 PR |
68 | fstate->command = FTP_COMMAND_PORT; |
69 | } | |
9d7baa7a | 70 | |
f2f9b832 PR |
71 | /* else { |
72 | * Add the ftp commands you need here | |
73 | * } | |
74 | */ | |
75 | } | |
76 | return 1; | |
77 | } | |
78 | ||
79 | /** | |
80 | * \brief This function is called to retrieve the request line and parse it | |
81 | * \param ftp_state the ftp state structure for the parser | |
82 | * \param input input line of the command | |
83 | * \param input_len length of the request | |
84 | * \param output the resulting output | |
85 | * | |
86 | * \retval 1 when the command is parsed, 0 otherwise | |
87 | */ | |
88 | static int FTPParseRequestCommandLine(Flow *f, void *ftp_state, AppLayerParserState | |
9a6aef45 AS |
89 | *pstate, uint8_t *input,uint32_t input_len, |
90 | void *local_data, AppLayerParserResult *output) { | |
f2f9b832 PR |
91 | SCEnter(); |
92 | //PrintRawDataFp(stdout, input,input_len); | |
93 | ||
94 | FtpState *fstate = (FtpState *)ftp_state; | |
95 | uint16_t max_fields = 2; | |
96 | uint16_t u = 0; | |
97 | uint32_t offset = 0; | |
98 | ||
99 | if (pstate == NULL) | |
100 | return -1; | |
101 | ||
102 | for (u = pstate->parse_field; u < max_fields; u++) { | |
f2f9b832 PR |
103 | |
104 | switch(u) { | |
105 | case 0: /* REQUEST COMMAND */ | |
106 | { | |
107 | const uint8_t delim[] = { 0x20, }; | |
108 | int r = AlpParseFieldByDelimiter(output, pstate, | |
109 | FTP_FIELD_REQUEST_COMMAND, delim, sizeof(delim), | |
110 | input, input_len, &offset); | |
f2f9b832 PR |
111 | |
112 | if (r == 0) { | |
113 | pstate->parse_field = 0; | |
114 | return 0; | |
115 | } | |
f2f9b832 PR |
116 | fstate->arg_offset = offset; |
117 | FTPParseRequestCommand(ftp_state, input, input_len); | |
118 | break; | |
119 | } | |
120 | case 1: /* REQUEST COMMAND ARG */ | |
121 | { | |
122 | switch (fstate->command) { | |
123 | case FTP_COMMAND_PORT: | |
124 | /* We don't need to parse args, we are going to check | |
125 | * the ftpbounce condition directly from detect-ftpbounce | |
126 | */ | |
127 | if (fstate->port_line != NULL) | |
25a3a5c6 PR |
128 | SCFree(fstate->port_line); |
129 | fstate->port_line = SCMalloc(input_len); | |
9f4fae5b GIG |
130 | if (fstate->port_line == NULL) |
131 | return 0; | |
f2f9b832 PR |
132 | fstate->port_line = memcpy(fstate->port_line, input, |
133 | input_len); | |
134 | fstate->port_line_len = input_len; | |
f2f9b832 PR |
135 | break; |
136 | default: | |
137 | break; | |
138 | } /* end switch command specified args */ | |
139 | ||
140 | break; | |
141 | } | |
142 | } | |
143 | } | |
144 | ||
145 | pstate->parse_field = 0; | |
146 | return 1; | |
147 | } | |
148 | ||
149 | /** | |
150 | * \brief This function is called to retrieve a ftp request | |
151 | * \param ftp_state the ftp state structure for the parser | |
152 | * \param input input line of the command | |
153 | * \param input_len length of the request | |
154 | * \param output the resulting output | |
155 | * | |
156 | * \retval 1 when the command is parsed, 0 otherwise | |
157 | */ | |
9a6aef45 AS |
158 | static int FTPParseRequest(Flow *f, void *ftp_state, |
159 | AppLayerParserState *pstate, | |
160 | uint8_t *input, uint32_t input_len, | |
161 | void *local_data, AppLayerParserResult *output) | |
162 | { | |
f2f9b832 | 163 | SCEnter(); |
f2f9b832 PR |
164 | /* PrintRawDataFp(stdout, input,input_len); */ |
165 | ||
166 | uint32_t offset = 0; | |
167 | ||
168 | if (pstate == NULL) | |
169 | return -1; | |
170 | ||
f2f9b832 PR |
171 | |
172 | //PrintRawDataFp(stdout, pstate->store, pstate->store_len); | |
173 | ||
174 | const uint8_t delim[] = { 0x0D, 0x0A }; | |
175 | int r = AlpParseFieldByDelimiter(output, pstate, FTP_FIELD_REQUEST_LINE, | |
176 | delim, sizeof(delim), input, input_len, | |
177 | &offset); | |
178 | if (r == 0) { | |
179 | pstate->parse_field = 0; | |
180 | return 0; | |
181 | } | |
182 | if (pstate->store_len) | |
183 | PrintRawDataFp(stdout, pstate->store, pstate->store_len); | |
184 | ||
185 | pstate->parse_field = 0; | |
186 | return 1; | |
187 | } | |
188 | ||
189 | /** | |
190 | * \brief This function is called to retrieve a ftp response | |
191 | * \param ftp_state the ftp state structure for the parser | |
192 | * \param input input line of the command | |
193 | * \param input_len length of the request | |
194 | * \param output the resulting output | |
195 | * | |
196 | * \retval 1 when the command is parsed, 0 otherwise | |
197 | */ | |
198 | static int FTPParseResponse(Flow *f, void *ftp_state, AppLayerParserState *pstate, | |
199 | uint8_t *input, uint32_t input_len, | |
9a6aef45 AS |
200 | void *local_data, AppLayerParserResult *output) |
201 | { | |
f2f9b832 | 202 | SCEnter(); |
f2f9b832 PR |
203 | //PrintRawDataFp(stdout, input,input_len); |
204 | ||
205 | uint32_t offset = 0; | |
206 | FtpState *fstate = (FtpState *)ftp_state; | |
207 | ||
208 | if (pstate == NULL) | |
209 | return -1; | |
210 | ||
f2f9b832 PR |
211 | |
212 | const uint8_t delim[] = { 0x0D, 0x0A }; | |
213 | int r = AlpParseFieldByDelimiter(output, pstate, FTP_FIELD_RESPONSE_LINE, | |
214 | delim, sizeof(delim), input, input_len, | |
215 | &offset); | |
216 | if (r == 0) { | |
217 | pstate->parse_field = 0; | |
218 | return 0; | |
219 | } | |
220 | char rcode[5]; | |
221 | memcpy(rcode, input, 4); | |
222 | rcode[4] = '\0'; | |
223 | fstate->response_code = atoi(rcode); | |
224 | SCLogDebug("Response: %u\n", fstate->response_code); | |
225 | ||
226 | pstate->parse_field = 0; | |
227 | return 1; | |
228 | } | |
229 | ||
230 | #ifdef DEBUG | |
231 | static SCMutex ftp_state_mem_lock = PTHREAD_MUTEX_INITIALIZER; | |
232 | static uint64_t ftp_state_memuse = 0; | |
233 | static uint64_t ftp_state_memcnt = 0; | |
234 | #endif | |
235 | ||
236 | static void *FTPStateAlloc(void) { | |
25a3a5c6 | 237 | void *s = SCMalloc(sizeof(FtpState)); |
f2f9b832 PR |
238 | if (s == NULL) |
239 | return NULL; | |
240 | ||
241 | memset(s, 0, sizeof(FtpState)); | |
242 | ||
243 | #ifdef DEBUG | |
244 | SCMutexLock(&ftp_state_mem_lock); | |
245 | ftp_state_memcnt++; | |
246 | ftp_state_memuse+=sizeof(FtpState); | |
247 | SCMutexUnlock(&ftp_state_mem_lock); | |
248 | #endif | |
249 | return s; | |
250 | } | |
251 | ||
252 | static void FTPStateFree(void *s) { | |
18954a2c PR |
253 | FtpState *fstate = (FtpState *) s; |
254 | if (fstate->port_line != NULL) | |
255 | SCFree(fstate->port_line); | |
25a3a5c6 | 256 | SCFree(s); |
f2f9b832 PR |
257 | #ifdef DEBUG |
258 | SCMutexLock(&ftp_state_mem_lock); | |
259 | ftp_state_memcnt--; | |
260 | ftp_state_memuse-=sizeof(FtpState); | |
261 | SCMutexUnlock(&ftp_state_mem_lock); | |
262 | #endif | |
263 | } | |
264 | ||
265 | ||
266 | void RegisterFTPParsers(void) { | |
000ce98c AS |
267 | /** FTP */ |
268 | AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_FTP, "USER ", 5, 0, STREAM_TOSERVER); | |
269 | AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_FTP, "PASS ", 5, 0, STREAM_TOSERVER); | |
270 | AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_FTP, "PORT ", 5, 0, STREAM_TOSERVER); | |
000ce98c | 271 | |
c6e090f7 | 272 | AppLayerRegisterProto("ftp", ALPROTO_FTP, STREAM_TOSERVER, |
f2f9b832 | 273 | FTPParseRequest); |
c6e090f7 | 274 | AppLayerRegisterProto("ftp", ALPROTO_FTP, STREAM_TOCLIENT, |
f2f9b832 PR |
275 | FTPParseResponse); |
276 | AppLayerRegisterParser("ftp.request_command_line", ALPROTO_FTP, | |
277 | FTP_FIELD_REQUEST_LINE, FTPParseRequestCommandLine, | |
278 | "ftp"); | |
279 | AppLayerRegisterStateFuncs(ALPROTO_FTP, FTPStateAlloc, FTPStateFree); | |
280 | } | |
281 | ||
282 | void FTPAtExitPrintStats(void) { | |
283 | #ifdef DEBUG | |
284 | SCMutexLock(&ftp_state_mem_lock); | |
285 | SCLogDebug("ftp_state_memcnt %"PRIu64", ftp_state_memuse %"PRIu64"", | |
286 | ftp_state_memcnt, ftp_state_memuse); | |
287 | SCMutexUnlock(&ftp_state_mem_lock); | |
288 | #endif | |
289 | } | |
290 | ||
291 | /* UNITTESTS */ | |
292 | #ifdef UNITTESTS | |
293 | ||
294 | /** \test Send a get request in one chunk. */ | |
295 | int FTPParserTest01(void) { | |
296 | int result = 1; | |
297 | Flow f; | |
298 | uint8_t ftpbuf[] = "PORT 192,168,1,1,0,80\r\n"; | |
299 | uint32_t ftplen = sizeof(ftpbuf) - 1; /* minus the \0 */ | |
300 | TcpSession ssn; | |
301 | ||
302 | memset(&f, 0, sizeof(f)); | |
303 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
304 | |
305 | FLOW_INITIALIZE(&f); | |
f2f9b832 PR |
306 | f.protoctx = (void *)&ssn; |
307 | ||
6a53ab9c | 308 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 309 | |
9a6aef45 | 310 | int r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf, ftplen); |
f2f9b832 | 311 | if (r != 0) { |
f729d6f7 | 312 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
313 | result = 0; |
314 | goto end; | |
315 | } | |
316 | ||
06904c90 | 317 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 318 | if (ftp_state == NULL) { |
f729d6f7 | 319 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
320 | result = 0; |
321 | goto end; | |
322 | } | |
323 | ||
324 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 325 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
326 | result = 0; |
327 | goto end; | |
328 | } | |
329 | ||
330 | end: | |
6a53ab9c | 331 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 332 | FLOW_DESTROY(&f); |
f2f9b832 PR |
333 | return result; |
334 | } | |
335 | ||
336 | /** \test Send a splitted get request. */ | |
337 | int FTPParserTest03(void) { | |
338 | int result = 1; | |
339 | Flow f; | |
340 | uint8_t ftpbuf1[] = "POR"; | |
341 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
342 | uint8_t ftpbuf2[] = "T 192,168,1"; | |
343 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
344 | uint8_t ftpbuf3[] = "1,1,10,20\r\n"; | |
345 | uint32_t ftplen3 = sizeof(ftpbuf3) - 1; /* minus the \0 */ | |
346 | TcpSession ssn; | |
347 | ||
348 | memset(&f, 0, sizeof(f)); | |
349 | memset(&ssn, 0, sizeof(ssn)); | |
f2f9b832 PR |
350 | f.protoctx = (void *)&ssn; |
351 | ||
6a53ab9c | 352 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 353 | |
9a6aef45 | 354 | int r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1); |
f2f9b832 | 355 | if (r != 0) { |
f729d6f7 | 356 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
357 | result = 0; |
358 | goto end; | |
359 | } | |
360 | ||
9a6aef45 | 361 | r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER, ftpbuf2, ftplen2); |
f2f9b832 | 362 | if (r != 0) { |
f729d6f7 | 363 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
364 | result = 0; |
365 | goto end; | |
366 | } | |
367 | ||
9a6aef45 | 368 | r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf3, ftplen3); |
f2f9b832 | 369 | if (r != 0) { |
f729d6f7 | 370 | SCLogDebug("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
371 | result = 0; |
372 | goto end; | |
373 | } | |
374 | ||
06904c90 | 375 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 376 | if (ftp_state == NULL) { |
f729d6f7 | 377 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
378 | result = 0; |
379 | goto end; | |
380 | } | |
381 | ||
382 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 383 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
384 | result = 0; |
385 | goto end; | |
386 | } | |
387 | ||
388 | end: | |
6a53ab9c | 389 | StreamTcpFreeConfig(TRUE); |
f2f9b832 PR |
390 | return result; |
391 | } | |
392 | ||
393 | /** \test See how it deals with an incomplete request. */ | |
394 | int FTPParserTest06(void) { | |
395 | int result = 1; | |
396 | Flow f; | |
397 | uint8_t ftpbuf1[] = "PORT"; | |
398 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
399 | TcpSession ssn; | |
400 | ||
401 | memset(&f, 0, sizeof(f)); | |
402 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
403 | |
404 | FLOW_INITIALIZE(&f); | |
f2f9b832 PR |
405 | f.protoctx = (void *)&ssn; |
406 | ||
6a53ab9c | 407 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 408 | |
9a6aef45 | 409 | int r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, ftpbuf1, ftplen1); |
f2f9b832 | 410 | if (r != 0) { |
f729d6f7 | 411 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
412 | result = 0; |
413 | goto end; | |
414 | } | |
415 | ||
06904c90 | 416 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 417 | if (ftp_state == NULL) { |
f729d6f7 | 418 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
419 | result = 0; |
420 | goto end; | |
421 | } | |
422 | ||
423 | if (ftp_state->command != FTP_COMMAND_UNKNOWN) { | |
f729d6f7 | 424 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command); |
f2f9b832 PR |
425 | result = 0; |
426 | goto end; | |
427 | } | |
428 | ||
429 | end: | |
6a53ab9c | 430 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 431 | FLOW_DESTROY(&f); |
f2f9b832 PR |
432 | return result; |
433 | } | |
434 | ||
435 | /** \test See how it deals with an incomplete request in multiple chunks. */ | |
436 | int FTPParserTest07(void) { | |
437 | int result = 1; | |
438 | Flow f; | |
439 | uint8_t ftpbuf1[] = "PO"; | |
440 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
441 | uint8_t ftpbuf2[] = "RT\r\n"; | |
442 | uint32_t ftplen2 = sizeof(ftpbuf2) - 1; /* minus the \0 */ | |
443 | TcpSession ssn; | |
444 | ||
445 | memset(&f, 0, sizeof(f)); | |
446 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
447 | |
448 | FLOW_INITIALIZE(&f); | |
f2f9b832 PR |
449 | f.protoctx = (void *)&ssn; |
450 | ||
6a53ab9c | 451 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 452 | |
9a6aef45 | 453 | int r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_START, ftpbuf1, ftplen1); |
f2f9b832 | 454 | if (r != 0) { |
f729d6f7 | 455 | SCLogDebug("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
456 | result = 0; |
457 | goto end; | |
458 | } | |
459 | ||
9a6aef45 | 460 | r = AppLayerParse(NULL, &f, ALPROTO_FTP, STREAM_TOSERVER|STREAM_EOF, ftpbuf2, ftplen2); |
f2f9b832 | 461 | if (r != 0) { |
f729d6f7 | 462 | SCLogDebug("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); |
f2f9b832 PR |
463 | result = 0; |
464 | goto end; | |
465 | } | |
466 | ||
06904c90 | 467 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 468 | if (ftp_state == NULL) { |
f729d6f7 | 469 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
470 | result = 0; |
471 | goto end; | |
472 | } | |
473 | ||
474 | if (ftp_state->command != FTP_COMMAND_UNKNOWN) { | |
f729d6f7 | 475 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_UNKNOWN, ftp_state->command); |
f2f9b832 PR |
476 | result = 0; |
477 | goto end; | |
478 | } | |
479 | ||
480 | end: | |
6a53ab9c | 481 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 482 | FLOW_DESTROY(&f); |
f2f9b832 PR |
483 | return result; |
484 | } | |
485 | ||
486 | /** \test Test case where chunks are smaller than the delim length and the | |
487 | * last chunk is supposed to match the delim. */ | |
488 | int FTPParserTest10(void) { | |
489 | int result = 1; | |
490 | Flow f; | |
491 | uint8_t ftpbuf1[] = "PORT 1,2,3,4,5,6\r\n"; | |
492 | uint32_t ftplen1 = sizeof(ftpbuf1) - 1; /* minus the \0 */ | |
493 | TcpSession ssn; | |
494 | int r = 0; | |
495 | memset(&f, 0, sizeof(f)); | |
496 | memset(&ssn, 0, sizeof(ssn)); | |
cc76aa4b WM |
497 | |
498 | FLOW_INITIALIZE(&f); | |
f2f9b832 PR |
499 | f.protoctx = (void *)&ssn; |
500 | ||
6a53ab9c | 501 | StreamTcpInitConfig(TRUE); |
6a53ab9c | 502 | |
f2f9b832 PR |
503 | uint32_t u; |
504 | for (u = 0; u < ftplen1; u++) { | |
505 | uint8_t flags = 0; | |
506 | ||
507 | if (u == 0) flags = STREAM_TOSERVER|STREAM_START; | |
508 | else if (u == (ftplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF; | |
509 | else flags = STREAM_TOSERVER; | |
510 | ||
9a6aef45 | 511 | r = AppLayerParse(NULL, &f, ALPROTO_FTP, flags, &ftpbuf1[u], 1); |
f2f9b832 | 512 | if (r != 0) { |
f729d6f7 | 513 | SCLogDebug("toserver chunk %" PRIu32 " returned %" PRId32 ", expected 0: ", u, r); |
f2f9b832 PR |
514 | result = 0; |
515 | goto end; | |
516 | } | |
517 | } | |
518 | ||
06904c90 | 519 | FtpState *ftp_state = f.alstate; |
f2f9b832 | 520 | if (ftp_state == NULL) { |
f729d6f7 | 521 | SCLogDebug("no ftp state: "); |
f2f9b832 PR |
522 | result = 0; |
523 | goto end; | |
524 | } | |
525 | ||
526 | if (ftp_state->command != FTP_COMMAND_PORT) { | |
f729d6f7 | 527 | SCLogDebug("expected command %" PRIu32 ", got %" PRIu32 ": ", FTP_COMMAND_PORT, ftp_state->command); |
f2f9b832 PR |
528 | result = 0; |
529 | goto end; | |
530 | } | |
531 | ||
532 | end: | |
6a53ab9c | 533 | StreamTcpFreeConfig(TRUE); |
0e4235cc | 534 | FLOW_DESTROY(&f); |
f2f9b832 PR |
535 | return result; |
536 | } | |
537 | #endif /* UNITTESTS */ | |
538 | ||
539 | void FTPParserRegisterTests(void) { | |
540 | #ifdef UNITTESTS | |
541 | UtRegisterTest("FTPParserTest01", FTPParserTest01, 1); | |
542 | UtRegisterTest("FTPParserTest03", FTPParserTest03, 1); | |
543 | UtRegisterTest("FTPParserTest06", FTPParserTest06, 1); | |
544 | UtRegisterTest("FTPParserTest07", FTPParserTest07, 1); | |
545 | UtRegisterTest("FTPParserTest10", FTPParserTest10, 1); | |
546 | #endif /* UNITTESTS */ | |
547 | } | |
548 |