@@ -480,7 +480,8 @@ static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserSt
480480 * For ftp active mode, data connection direction is opposite to
481481 * control direction.
482482 */
483- if ((state -> active && state -> command == FTP_COMMAND_STOR ) ||
483+ if ((state -> active &&
484+ (state -> command == FTP_COMMAND_STOR || state -> command == FTP_COMMAND_APPE )) ||
484485 (!state -> active &&
485486 (state -> command == FTP_COMMAND_RETR || state -> command == FTP_COMMAND_NLST ||
486487 state -> command == FTP_COMMAND_LIST ||
@@ -515,6 +516,8 @@ static AppLayerResult FTPParseRequest(Flow *f, void *ftp_state, AppLayerParserSt
515516 case FTP_COMMAND_RETR :
516517 // fallthrough
517518 case FTP_COMMAND_STOR :
519+ // fallthrough
520+ case FTP_COMMAND_APPE :
518521 /* Ensure that there is a negotiated dyn port and a file
519522 * name -- need more than 5 chars: cmd [4], space, <filename>
520523 */
@@ -1094,6 +1097,11 @@ static AppLayerResult FTPDataParse(Flow *f, FtpDataState *ftpdata_state,
10941097 SCLogDebug ("STOR data to %s" ,
10951098 (ftpdata_state -> direction & STREAM_TOSERVER ) ? "toserver" : "toclient" );
10961099 break ;
1100+ case FTP_COMMAND_APPE :
1101+ ftpdata_state -> direction = data -> direction ;
1102+ SCLogDebug ("APPE data to %s" ,
1103+ (ftpdata_state -> direction & STREAM_TOSERVER ) ? "toserver" : "toclient" );
1104+ break ;
10971105 case FTP_COMMAND_RETR :
10981106 ftpdata_state -> direction = data -> direction ;
10991107 SCLogDebug ("RETR data to %s" ,
@@ -1465,6 +1473,9 @@ bool EveFTPDataAddMetadata(void *vtx, SCJsonBuilder *jb)
14651473 case FTP_COMMAND_STOR :
14661474 JB_SET_STRING (jb , "command" , "STOR" );
14671475 break ;
1476+ case FTP_COMMAND_APPE :
1477+ JB_SET_STRING (jb , "command" , "APPE" );
1478+ break ;
14681479 case FTP_COMMAND_RETR :
14691480 JB_SET_STRING (jb , "command" , "RETR" );
14701481 break ;
@@ -1624,6 +1635,50 @@ static int FTPParserTest12(void)
16241635 StreamTcpFreeConfig (true);
16251636 PASS ;
16261637}
1638+
1639+ /** \test Supply APPE without a filename */
1640+ static int FTPParserTest13 (void )
1641+ {
1642+ Flow f ;
1643+ uint8_t ftpbuf1 [] = "PORT 192,168,1,1,0,80\r\n" ;
1644+ uint8_t ftpbuf2 [] = "APPE\r\n" ;
1645+ uint8_t ftpbuf3 [] = "227 OK\r\n" ;
1646+ TcpSession ssn ;
1647+
1648+ AppLayerParserThreadCtx * alp_tctx = AppLayerParserThreadCtxAlloc ();
1649+
1650+ memset (& f , 0 , sizeof (f ));
1651+ memset (& ssn , 0 , sizeof (ssn ));
1652+
1653+ f .protoctx = (void * )& ssn ;
1654+ f .proto = IPPROTO_TCP ;
1655+ f .alproto = ALPROTO_FTP ;
1656+
1657+ StreamTcpInitConfig (true);
1658+
1659+ int r = AppLayerParserParse (NULL , alp_tctx , & f , ALPROTO_FTP , STREAM_TOSERVER | STREAM_START ,
1660+ ftpbuf1 , sizeof (ftpbuf1 ) - 1 );
1661+ FAIL_IF (r != 0 );
1662+
1663+ /* Response */
1664+ r = AppLayerParserParse (
1665+ NULL , alp_tctx , & f , ALPROTO_FTP , STREAM_TOCLIENT , ftpbuf3 , sizeof (ftpbuf3 ) - 1 );
1666+ FAIL_IF (r != 0 );
1667+
1668+ r = AppLayerParserParse (
1669+ NULL , alp_tctx , & f , ALPROTO_FTP , STREAM_TOSERVER , ftpbuf2 , sizeof (ftpbuf2 ) - 1 );
1670+ FAIL_IF (r != 0 );
1671+
1672+ FtpState * ftp_state = f .alstate ;
1673+ FAIL_IF_NULL (ftp_state );
1674+
1675+ FAIL_IF (ftp_state -> command != FTP_COMMAND_APPE );
1676+
1677+ FLOW_DESTROY (& f );
1678+ AppLayerParserThreadCtxFree (alp_tctx );
1679+ StreamTcpFreeConfig (true);
1680+ PASS ;
1681+ }
16271682#endif /* UNITTESTS */
16281683
16291684void FTPParserRegisterTests (void )
@@ -1632,6 +1687,7 @@ void FTPParserRegisterTests(void)
16321687 UtRegisterTest ("FTPParserTest01" , FTPParserTest01 );
16331688 UtRegisterTest ("FTPParserTest11" , FTPParserTest11 );
16341689 UtRegisterTest ("FTPParserTest12" , FTPParserTest12 );
1690+ UtRegisterTest ("FTPParserTest13" , FTPParserTest13 );
16351691#endif /* UNITTESTS */
16361692}
16371693
0 commit comments