summaryrefslogtreecommitdiff
path: root/src/ftp_decoder_proto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ftp_decoder_proto.cpp')
-rw-r--r--src/ftp_decoder_proto.cpp566
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;
+}