diff options
Diffstat (limited to 'src/ftp_decoder_proto.cpp')
| -rw-r--r-- | src/ftp_decoder_proto.cpp | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/src/ftp_decoder_proto.cpp b/src/ftp_decoder_proto.cpp new file mode 100644 index 0000000..dd5f14a --- /dev/null +++ b/src/ftp_decoder_proto.cpp @@ -0,0 +1,566 @@ +#include "ftp_decoder_inner.h" +#include "ftp_decoder.h" +#include "ftp_decoder_util.h" +#include "ftp_decoder_hash.h" +#include <sys/types.h> +#include <arpa/inet.h> + +/* + ------------- + |/---------\| + || 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<space><d><net-prt><d><net-addr><d><tcp-port><d> + 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; +} |
