#include "ftp_decoder_inner.h" #include "ftp_decoder.h" #include "ftp_decoder_util.h" #include "ftp_decoder_hash.h" #include #include /* ------------- |/---------\| || User || -------- ||Interface|<--->| User | |\----^----/| -------- ---------- | | | |/------\| FTP Commands |/----V----\| ||Server|<---------------->| User || || PI || FTP Replies || PI || |\--^---/| |\----^----/| | | | | | | -------- |/--V---\| Data |/----V----\| -------- | File |<--->|Server|<---------------->| User |<--->| File | |System| || DTP || Connection || DTP || |System| -------- |\------/| |\---------/| -------- ---------- ------------- Server-FTP USER-FTP */ /* ctrl link command data link tcp/ip stack event --------------------------------------------------------------------------------------------------- PORT 192,168,38,2,202,95 listen tcp port: 51807 200 PORT command successful. LIST server use src tcp port 20 connecting to client tcp port 51807 data connection is established send 'ls -l' result to cliet 226 Directory send OK. close the tcp data connection waiting for next command... */ static const struct iovec G_FTP_EMPTY_IOV = {NULL, 0}; static struct ftp_context *ftp_get_declare_datalink_ctx(struct session *sess, struct ftp_context *ctrl_fctx, struct ftp_decoder_env *fenv) { int thread_id = session_get_current_thread_id(sess); struct ftp_context *declare_data_link_ftx = (struct ftp_context *)ftp_hash_search(fenv->data_link_table[thread_id], &ctrl_fctx->last_data_link_key); if (NULL == declare_data_link_ftx) { ftp_runtime_log(RLOG_LV_FATAL, "declare data link not found, key=%s", ftp_hash_key_to_str(&ctrl_fctx->last_data_link_key)); return NULL; } return declare_data_link_ftx; } static void ftp_assemble_uri(struct session *sess, const struct iovec *raw_arg, struct iovec *uri) { char dip_str[INET6_ADDRSTRLEN] = {}; char tmp_url[2048] = {}; enum session_addr_type saddr_type; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); if (saddr_type == SESSION_ADDR_TYPE_IPV4_TCP) { inet_ntop(AF_INET, &saddr->ipv4.daddr, dip_str, INET_ADDRSTRLEN); } else { inet_ntop(AF_INET6, saddr->ipv6.daddr, dip_str, INET6_ADDRSTRLEN); } int ret = snprintf(tmp_url, sizeof(tmp_url), "ftp://%s/%.*s", dip_str, IOVEC_PRINT(*raw_arg)); if (uri->iov_base) { free(uri->iov_base); } uri->iov_base = calloc(1, ret); memcpy(uri->iov_base, tmp_url, ret); uri->iov_len = ret; } int ftp_cmd_handler_do_user(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.arg_refer, &fctx->parse_result.result_array[FTP_ACCOUNT]); ftp_runtime_log(RLOG_LV_DEBUG, "'%s': USER: %.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->parse_result.result_array[FTP_ACCOUNT])); return 0; } int ftp_cmd_handler_do_pass(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.arg_refer, &fctx->parse_result.result_array[FTP_PASSWORD]); ftp_runtime_log(RLOG_LV_DEBUG, "'%s': PASS: %.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->parse_result.result_array[FTP_PASSWORD])); return 0; } int ftp_parse_ipv4_port_style(const fstring *cmd_str, unsigned int *ipv4_net, unsigned short *port_net) { unsigned int fields[6]; char raw_cmd_str_tmp[cmd_str->iov_len + 1] = {}; memcpy(raw_cmd_str_tmp, cmd_str->iov_base, cmd_str->iov_len); char only_integer_str_tmp[cmd_str->iov_len + 1] = {}; if (sscanf(raw_cmd_str_tmp, "%*[a-zA-Z (]%[0-9,]", only_integer_str_tmp) <= 0) { if (sscanf(raw_cmd_str_tmp, "%[1234567890,]", only_integer_str_tmp) <= 0) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_ipv4_port_style parse error: %.*s", IOVEC_PRINT_PTR(cmd_str)); return -1; } } int ret = sscanf(only_integer_str_tmp, "%u,%u,%u,%u,%u,%u", &fields[0], &fields[1], &fields[2], &fields[3], &fields[4], &fields[5]); if (ret != 6) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_ipv4_port_style parse error: %.*s", cmd_str->iov_len, only_integer_str_tmp); return -1; } unsigned int dst_ip_host = (fields[0] << 24) | (fields[1] << 16) | (fields[2] << 8) | fields[3]; unsigned short dst_port_host = (fields[4] << 8) | fields[5]; *ipv4_net = htonl(dst_ip_host); *port_net = htons(dst_port_host); return 0; } /* PORT command only support IPv4, arg pattern: h1,h2,h3,h4,p1,p2 */ int ftp_cmd_handler_do_port(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.cmd_refer, &fctx->parse_result.result_array[FTP_TRANS_MODE]); fctx->parse_result.push_result_flags[FTP_TRANS_MODE] = 0; unsigned int dst_ip_net; unsigned short dst_port_net; if (ftp_parse_ipv4_port_style(&fctx->cmd_result.arg_refer, &dst_ip_net, &dst_port_net) < 0) { return -1; } enum session_addr_type saddr_type; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); fctx->last_data_link_key.addr_type = SESSION_ADDR_TYPE_IPV4_TCP; // in active mode, new data link src ip address is server ip address ftp_make_hkey_v4(&fctx->last_data_link_key.tuplev4, saddr->ipv4.daddr, dst_ip_net, dst_port_net); struct ftp_context *new_data_link_ctx = ftp_decoder_context_deep_dup(fctx); int thread_id = session_get_current_thread_id(sess); ftp_hash_add(fenv->data_link_table[thread_id], &fctx->last_data_link_key.tuplev4, sizeof(struct session_addr_ipv4), new_data_link_ctx); ftp_decoder_stat_incrby(thread_id, fenv, FTPD_STAT_NEGOTIATE_DATA_LINK, 1); ftp_runtime_log(RLOG_LV_DEBUG, "do_port_cb: %.*s, parsed dip:%x, dport:%u", IOVEC_PRINT(fctx->parse_result.result_array[FTP_TRANS_MODE]), dst_ip_net, dst_port_net); return 0; } /* PASV command only support IPv4, the response like: '227 Entering Passive Mode (218,13,32,6,78,40).' */ int ftp_cmd_handler_do_pasv(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.cmd_refer, &fctx->parse_result.result_array[FTP_TRANS_MODE]); fctx->parse_result.push_result_flags[FTP_TRANS_MODE] = 0; ftp_runtime_log(RLOG_LV_DEBUG, "'%s' PASV command", session_get0_readable_addr(sess)); return 0; } int ftp_cmd_handler_do_list(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.cmd_refer, &fctx->parse_result.result_array[FTP_TRANS_DIR]); fstring_safe_dup(&G_FTP_EMPTY_IOV, &fctx->parse_result.result_array[FTP_URI]); struct ftp_context *declare_data_link_ftx = ftp_get_declare_datalink_ctx(sess, fctx, fenv); if (declare_data_link_ftx) { fstring_safe_dup(&fctx->cmd_result.cmd_refer, &declare_data_link_ftx->parse_result.result_array[FTP_TRANS_DIR]); fstring_safe_dup(&G_FTP_EMPTY_IOV, &declare_data_link_ftx->parse_result.result_array[FTP_URI]); declare_data_link_ftx->data_link_presentation = FTP_INVENTORY; } ftp_runtime_log(RLOG_LV_DEBUG, "'%s': LIST command", session_get0_readable_addr(sess)); return 0; } static void ftp_cmd_handler_do_stor_retr_common(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.cmd_refer, &fctx->parse_result.result_array[FTP_TRANS_DIR]); ftp_assemble_uri(sess, &fctx->cmd_result.arg_refer, &fctx->parse_result.result_array[FTP_URI]); fctx->parse_result.push_result_flags[FTP_TRANS_DIR] = 0; fctx->parse_result.push_result_flags[FTP_URI] = 0; struct ftp_context *declare_data_link_ftx = ftp_get_declare_datalink_ctx(sess, fctx, fenv); if (declare_data_link_ftx) { declare_data_link_ftx->data_link_presentation = FTP_FILE_CONTENT; fstring_safe_dup(&fctx->parse_result.result_array[FTP_TRANS_DIR], &declare_data_link_ftx->parse_result.result_array[FTP_TRANS_DIR]); fstring_safe_dup(&fctx->parse_result.result_array[FTP_URI], &declare_data_link_ftx->parse_result.result_array[FTP_URI]); // ftp_update_parse_result(declare_data_link_ftx, &fctx->parse_result.result_array[FTP_URI], FTP_URI); } } int ftp_cmd_handler_do_stor(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { ftp_cmd_handler_do_stor_retr_common(sess, fctx, fenv); ftp_runtime_log(RLOG_LV_DEBUG, "'%s', STOR uri:%.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->parse_result.result_array[FTP_URI])); return 0; } int ftp_cmd_handler_do_retr(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { ftp_cmd_handler_do_stor_retr_common(sess, fctx, fenv); ftp_runtime_log(RLOG_LV_DEBUG, "'%s', RETR uri:%.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->parse_result.result_array[FTP_URI])); return 0; } int ftp_parse_eprt_ipport_style(const fstring *arg_str, struct in6_addr *ipd_addr, unsigned short *port_net) { unsigned int port_host; int inet_proto; char ip6_addr_str[INET6_ADDRSTRLEN] = {}; char raw_cmd_str_tmp[arg_str->iov_len + 1] = {}; memcpy(raw_cmd_str_tmp, arg_str->iov_base, arg_str->iov_len); char *save_ptr, *ptr; const char *delim = " |\t"; ptr = strtok_r(raw_cmd_str_tmp, delim, &save_ptr); if (NULL == ptr) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_eprt_ipport_style parse error: %.*s", IOVEC_PRINT_PTR(arg_str)); return -1; } inet_proto = atoi(ptr); if (2 != inet_proto) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_eprt_ipport_style parse error: %.*s, not support inet: %d", IOVEC_PRINT_PTR(arg_str), inet_proto); return -1; } ptr = strtok_r(NULL, delim, &save_ptr); if (NULL == ptr) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_eprt_ipport_style parse error: %.*s", IOVEC_PRINT_PTR(arg_str)); return -1; } strncpy(ip6_addr_str, ptr, INET6_ADDRSTRLEN - 1); ptr = strtok_r(NULL, delim, &save_ptr); if (NULL == ptr) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_eprt_ipport_style parse error: %.*s", IOVEC_PRINT_PTR(arg_str)); return -1; } port_host = (unsigned int)atoi(ptr); while (strtok_r(NULL, "|", &save_ptr)) ; inet_pton(AF_INET6, ip6_addr_str, ipd_addr); *port_net = htons((unsigned short)port_host); return 0; } /* EPRT support IPv4 and IPv6, pattern: EPRT example: EPRT |1|132.235.1.2|6275| EPRT |2|1080::8:800:200C:417A|5282| refer: https://datatracker.ietf.org/doc/html/rfc2428#section-2 */ int ftp_cmd_handler_do_eprt(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { struct in6_addr ipd_addr; unsigned short port_net; fstring_safe_dup(&fctx->cmd_result.cmd_refer, &fctx->parse_result.result_array[FTP_TRANS_MODE]); if (ftp_parse_eprt_ipport_style(&fctx->cmd_result.arg_refer, &ipd_addr, &port_net) < 0) { return -1; } enum session_addr_type saddr_type; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); fctx->last_data_link_key.addr_type = SESSION_ADDR_TYPE_IPV6_TCP; // in active mode, new data link src ip address is server ip address ftp_make_hkey_v6(&fctx->last_data_link_key.tuplev6, (struct in6_addr *)&saddr->ipv6.daddr, &ipd_addr, port_net); struct ftp_context *new_data_link_ctx = ftp_decoder_context_deep_dup(fctx); int thread_id = session_get_current_thread_id(sess); ftp_hash_add(fenv->data_link_table[thread_id], &fctx->last_data_link_key.tuplev6, sizeof(struct session_addr_ipv6), new_data_link_ctx); ftp_decoder_stat_incrby(thread_id, fenv, FTPD_STAT_NEGOTIATE_DATA_LINK, 1); ftp_runtime_log(RLOG_LV_DEBUG, "'%s': EPRT command, port:%u", session_get0_readable_addr(sess), ntohs(port_net)); return 0; } /* LPRT support IPv4 and IPv6, pattern: LPRT af,hal,h1,h2,h3,h4...,pal,p1,p2... example: LPRT 6,16,32,2,81,131,67,131,0,0,0,0,0,0,81,131,67,131,2,4,7 refer: https://www.rfc-editor.org/rfc/rfc1639.html */ int ftp_cmd_handler_do_lprt(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { // todo ftp_runtime_log(RLOG_LV_INFO, "LPRT command not support yet!"); return -1; } /* EPSV support IPv4 and IPv6, refer: https://datatracker.ietf.org/doc/html/rfc2428#autoid-3 */ int ftp_cmd_handler_do_epsv(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { // todo ftp_runtime_log(RLOG_LV_INFO, "EPSV command not support yet!"); return -1; } /* LPSV support IPv4 and IPv6, response is 228 xxxx... refer: https://www.rfc-editor.org/rfc/rfc1639.html */ int ftp_cmd_handler_do_lpsv(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { // todo ftp_runtime_log(RLOG_LV_INFO, "LPSV command not support yet!"); return -1; } static const struct ftp_interact_parser g_ftp_client_cmd_tuple[] = { {FTP_ACCOUNT, "USER", 4, ftp_cmd_handler_do_user}, {FTP_PASSWORD, "PASS", 4, ftp_cmd_handler_do_pass}, {FTP_TRANS_MODE, "PORT", 4, ftp_cmd_handler_do_port}, {FTP_TRANS_MODE, "PASV", 4, ftp_cmd_handler_do_pasv}, {FTP_TRANS_MODE, "EPRT", 4, ftp_cmd_handler_do_eprt}, {FTP_TRANS_MODE, "EPSV", 4, ftp_cmd_handler_do_epsv}, {FTP_TRANS_MODE, "LPRT", 4, ftp_cmd_handler_do_lprt}, {FTP_TRANS_MODE, "LPSV", 4, ftp_cmd_handler_do_lpsv}, {FTP_MSG_MAX, "LIST", 4, ftp_cmd_handler_do_list}, {FTP_TRANS_DIR, "STOR", 4, ftp_cmd_handler_do_stor}, {FTP_TRANS_DIR, "RETR", 4, ftp_cmd_handler_do_retr}, {FTP_MSG_MAX, NULL, 0, NULL}}; int ftp_res_handler_do_220(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { fstring_safe_dup(&fctx->cmd_result.arg_refer, &fctx->parse_result.result_array[FTP_BANNER]); return 0; } int ftp_res_handler_do_200(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { // todo ftp_runtime_log(RLOG_LV_INFO, "'%s': response 200 command not support yet!", session_get0_readable_addr(sess)); return -1; } /* example: 227 Entering Passive Mode (218,13,32,6,78,40). */ int ftp_res_handler_do_227(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { unsigned int dst_ip_net; unsigned short dst_port_net; if (ftp_parse_ipv4_port_style(&fctx->cmd_result.arg_refer, &dst_ip_net, &dst_port_net) < 0) { return -1; } enum session_addr_type saddr_type; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); fctx->last_data_link_key.addr_type = SESSION_ADDR_TYPE_IPV4_TCP; // in passive mode, new data link src ip address is client ip address ftp_make_hkey_v4(&fctx->last_data_link_key.tuplev4, saddr->ipv4.saddr, dst_ip_net, dst_port_net); struct ftp_context *new_data_link_ctx = ftp_decoder_context_deep_dup(fctx); int thread_id = session_get_current_thread_id(sess); ftp_hash_add(fenv->data_link_table[thread_id], &fctx->last_data_link_key.tuplev4, sizeof(struct session_addr_ipv4), new_data_link_ctx); ftp_decoder_stat_incrby(thread_id, fenv, FTPD_STAT_NEGOTIATE_DATA_LINK, 1); ftp_runtime_log(RLOG_LV_DEBUG, "'%s': response 227, %.*s parsed dip:0x%x, dport:%u", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->cmd_result.arg_refer), ntohl(dst_ip_net), ntohs(dst_port_net)); return 0; } /* example: 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...) refer: https://www.rfc-editor.org/rfc/rfc1639.html */ int ftp_res_handler_do_228(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { // todo ftp_runtime_log(RLOG_LV_INFO, "'%s': response 228 command not support yet!", session_get0_readable_addr(sess)); return -1; } int ftp_parse_ipv6_port_style(const fstring *cmd_str, unsigned short *port_net) { unsigned int port_host; char raw_cmd_str_tmp[cmd_str->iov_len + 1] = {}; memcpy(raw_cmd_str_tmp, cmd_str->iov_base, cmd_str->iov_len); char only_integer_str_tmp[cmd_str->iov_len + 1] = {}; if (sscanf(raw_cmd_str_tmp, "%*[a-zA-Z (|]%[0-9]", only_integer_str_tmp) <= 0) { if (sscanf(raw_cmd_str_tmp, "|||%s|", only_integer_str_tmp) <= 0) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_ipv4_port_style parse error: %.*s", IOVEC_PRINT_PTR(cmd_str)); return -1; } } int ret = sscanf(only_integer_str_tmp, "%u", &port_host); if (ret != 1) { ftp_runtime_log(RLOG_LV_FATAL, "ftp_parse_ipv6_port_style parse error: %.*s", cmd_str->iov_len, only_integer_str_tmp); return -1; } *port_net = htons((unsigned short)port_host); return 0; } /* example: 229 229 Entering Extended Passive Mode (|||20987|) refer: https://datatracker.ietf.org/doc/html/rfc2428#autoid-3 */ int ftp_res_handler_do_229(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv) { unsigned short port_net; if (ftp_parse_ipv6_port_style(&fctx->cmd_result.arg_refer, &port_net) < 0) { ftp_runtime_log(RLOG_LV_FATAL, "'%s': response 229 parse error, %.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->cmd_result.arg_refer)); return -1; } enum session_addr_type saddr_type; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); if (saddr_type != SESSION_ADDR_TYPE_IPV6_TCP) { ftp_runtime_log(RLOG_LV_FATAL, "'%s': response 229, but addr type is not ipv6 %.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->cmd_result.arg_refer)); return -1; } fctx->last_data_link_key.addr_type = SESSION_ADDR_TYPE_IPV6_TCP; // in passive mode, new data link src ip address is client ip address ftp_make_hkey_v6(&fctx->last_data_link_key.tuplev6, (struct in6_addr *)saddr->ipv6.saddr, (struct in6_addr *)saddr->ipv6.daddr, port_net); struct ftp_context *new_data_link_ctx = ftp_decoder_context_deep_dup(fctx); int thread_id = session_get_current_thread_id(sess); ftp_hash_add(fenv->data_link_table[thread_id], &fctx->last_data_link_key.tuplev6, sizeof(struct session_addr_ipv6), new_data_link_ctx); ftp_decoder_stat_incrby(thread_id, fenv, FTPD_STAT_NEGOTIATE_DATA_LINK, 1); ftp_runtime_log(RLOG_LV_DEBUG, "'%s': EPSV response 229, port is:%u", session_get0_readable_addr(sess), ntohs(port_net)); return -1; } static const struct ftp_interact_parser g_ftp_server_response_tuple[] = { {FTP_BANNER, "220", 3, ftp_res_handler_do_220}, {FTP_MSG_MAX, "200", 3, ftp_res_handler_do_200}, {FTP_MSG_MAX, "227", 3, ftp_res_handler_do_227}, {FTP_MSG_MAX, "228", 3, ftp_res_handler_do_228}, {FTP_MSG_MAX, "229", 3, ftp_res_handler_do_229}, {FTP_MSG_MAX, NULL, 0, NULL}}; int ftp_ctrl_identify_by_payload(const char *payload, size_t len, u_int8_t curdir) { if (NULL == payload || len < FTPD_IDENTIRY_MIN_LEN) { return 0; } if (curdir == PACKET_DIRECTION_C2S) { if (memcmp(payload, "USER", 4) == 0) { return 1; } } else { if (memcmp(payload, "220", 3) == 0) { int tmplen = MIN(len, FTPD_IDENTIRY_MAX_LEN); char tmp_buffer[tmplen]; memcpy(tmp_buffer, payload, tmplen); ftp_strtolower(tmp_buffer, tmplen); if ((memmem(tmp_buffer, tmplen, "ftp", 3) != NULL)) { return 1; } } } return 0; } int ftp_ctrl_identify_by_addr(struct session *sess) { enum session_addr_type saddr_type = SESSION_ADDR_TYPE_UNKNOWN; struct session_addr *saddr = session_get0_addr(sess, &saddr_type); unsigned short sport_host = 0, dport_host = 0; if (saddr_type == SESSION_ADDR_TYPE_IPV4_TCP) { sport_host = ntohs(saddr->ipv4.sport); dport_host = ntohs(saddr->ipv4.dport); } else if (saddr_type == SESSION_ADDR_TYPE_IPV6_TCP) { sport_host = ntohs(saddr->ipv6.sport); dport_host = ntohs(saddr->ipv6.dport); } else { return 0; } if (sport_host == 21 || dport_host == 21) { return 1; } if (sport_host == 25 || dport_host == 25) { return 0; } if (sport_host == 110 || dport_host == 110) { return 0; } return 0; } int ftp_ctrl_identify(struct session *sess, const char *payload, size_t len, u_int8_t curdir) { if (ftp_ctrl_identify_by_addr(sess)) { return 1; } return ftp_ctrl_identify_by_payload(payload, len, curdir); } struct ftp_context *ftp_data_identify(struct session *sess, struct ftp_decoder_env *fenv) { struct ftp_link_key hash_key = {}; if (ftp_build_hashkey_from_session(sess, &hash_key) < 0) { return NULL; } int thread_id = session_get_current_thread_id(sess); struct ftp_context *declare_fctx = (struct ftp_context *)ftp_hash_search(fenv->data_link_table[thread_id], &hash_key); if (NULL == declare_fctx) { return NULL; } // use tuple3 as hash key, so free it immediately after found! ftp_hash_del(fenv->data_link_table[thread_id], &hash_key); return declare_fctx; } static const struct ftp_interact_parser *ftp_fetch_cmd_parser(const struct ftp_interact_parser *table, const struct ftp_interact_line *cmd_result) { for (int i = 0; table[i].cmd_name != NULL; i++) { if (0 == strncmp(table[i].cmd_name, (char *)cmd_result->cmd_refer.iov_base, table[i].cmd_len)) { return &table[i]; } } return NULL; } int ftp_cmd_process(struct session *sess, struct ftp_context *fctx, struct ftp_decoder_env *fenv, int curdir) { const struct ftp_interact_parser *parser; if (PACKET_DIRECTION_C2S == curdir) { parser = ftp_fetch_cmd_parser(g_ftp_client_cmd_tuple, &fctx->cmd_result); } else { parser = ftp_fetch_cmd_parser(g_ftp_server_response_tuple, &fctx->cmd_result); } if (NULL == parser) { ftp_runtime_log(RLOG_LV_INFO, "ftp_fetch_cmd_parser() failed, '%s', not support cmd %.*s", session_get0_readable_addr(sess), IOVEC_PRINT(fctx->cmd_result.cmd_refer)); return -1; } if (parser->cmd_handler(sess, fctx, fenv) != -1) { fctx->parse_result.push_result_flags[parser->cmd_msg_type] = 0; ftp_decoder_push_msg(sess, fctx, fenv); ftp_decoder_stat_incrby(session_get_current_thread_id(sess), fenv, FTPD_STAT_CTRL_CMD, 1); } return 0; }