summaryrefslogtreecommitdiff
path: root/decoders/ftp/ftp_decoder_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'decoders/ftp/ftp_decoder_util.c')
-rw-r--r--decoders/ftp/ftp_decoder_util.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/decoders/ftp/ftp_decoder_util.c b/decoders/ftp/ftp_decoder_util.c
new file mode 100644
index 0000000..b0e1d3c
--- /dev/null
+++ b/decoders/ftp/ftp_decoder_util.c
@@ -0,0 +1,400 @@
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ftp_decoder_inner.h"
+#include "ftp_decoder_util.h"
+#include "ftp_decoder_hash.h"
+#include "stellar/session.h"
+#include "stellar/packet.h"
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <libgen.h>
+
+ void fstring_dup(fstring *dst, const fstring *src)
+ {
+ dst->iov_base = (char *)calloc(1, src->iov_len + 1);
+ assert(dst->iov_base);
+ memcpy((void *)dst->iov_base, src->iov_base, src->iov_len);
+ dst->iov_len = src->iov_len;
+ }
+ void fstring_safe_dup(fstring *dst, const fstring *src)
+ {
+ if (dst->iov_base)
+ {
+ free(dst->iov_base); // free it if exist
+ }
+ fstring_dup(dst, src);
+ }
+
+ void ftp_cmd_line_parse(const char *payload, size_t len, struct ftp_interact_line *cmd_line, int delim)
+ {
+ cmd_line->cmd_line.iov_base = (char *)payload;
+ cmd_line->cmd_line.iov_len = len;
+
+ const char *p = (char *)memchr(payload, delim, len);
+ if (NULL == p)
+ {
+ cmd_line->cmd_refer.iov_base = (char *)payload;
+ cmd_line->cmd_refer.iov_len = len;
+ cmd_line->arg_refer.iov_base = NULL;
+ cmd_line->arg_refer.iov_len = 0;
+ }
+ else
+ {
+ cmd_line->cmd_refer.iov_base = (char *)payload;
+ cmd_line->cmd_refer.iov_len = (p - payload);
+ cmd_line->arg_refer.iov_base = (char *)p + 1;
+ cmd_line->arg_refer.iov_len = len - (p + 1 - payload);
+ }
+ }
+
+ size_t ftp_strip_crlf(const char *payload, size_t len)
+ {
+ size_t new_len = 0;
+ for (size_t i = 0; i < len; i++, new_len++)
+ {
+ if ((payload[i] == '\r' || payload[i] == '\n'))
+ {
+ break;
+ }
+ }
+ return new_len;
+ }
+
+ int ftp_cmd_readline(struct ftp_interact_line *cmd_line, const char *payload, size_t len)
+ {
+ memset(cmd_line, 0, sizeof(struct ftp_interact_line));
+ size_t strip_crlf_len = ftp_strip_crlf(payload, len);
+ if (strip_crlf_len == 0)
+ {
+ return -1;
+ }
+ ftp_cmd_line_parse(payload, strip_crlf_len, cmd_line, ' ');
+ return 0;
+ }
+
+ void ftp_strtolower(char *str, size_t len)
+ {
+ for (size_t i = 0; i < len; i++)
+ {
+ str[i] = tolower(str[i]);
+ }
+ }
+
+ int ftp_mkdir_p(const char *path, mode_t mode)
+ {
+ struct stat st;
+ errno = 0;
+
+ /* Try to make the directory */
+ if (mkdir(path, mode) == 0)
+ return 0;
+
+ /* If it fails for any reason but EEXIST, fail */
+ if (errno != EEXIST)
+ return -1;
+
+ /* Check if the existing path is a directory */
+ if (stat(path, &st) != 0)
+ return -1;
+
+ /* If not, fail with ENOTDIR */
+ if (!S_ISDIR(st.st_mode))
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ errno = 0;
+ return 0;
+ }
+
+ struct ftp_dtp *ftp_dtp_deep_clone(const struct ftp_dtp *src)
+ {
+ if (NULL == src)
+ {
+ return NULL;
+ }
+ struct ftp_dtp *new_dtp = (struct ftp_dtp *)calloc(1, sizeof(struct ftp_dtp));
+ memcpy(new_dtp, src, sizeof(struct ftp_dtp));
+ if (src->uri)
+ {
+ new_dtp->uri = strdup(src->uri);
+ }
+ else
+ {
+ new_dtp->uri = NULL;
+ }
+ return new_dtp;
+ }
+
+ /*
+ from ctrl link to data link.
+ */
+ struct ftp_decoder_exdata *ftp_exdata_deep_clone(const struct ftp_decoder_exdata *src)
+ {
+ struct ftp_decoder_exdata *new_ext = (struct ftp_decoder_exdata *)calloc(1, sizeof(struct ftp_decoder_exdata));
+ memcpy(new_ext, src, sizeof(struct ftp_decoder_exdata));
+ fstring_dup(&new_ext->ftp_login_pri.username, &src->ftp_login_pri.username);
+ fstring_dup(&new_ext->ftp_login_pri.password, &src->ftp_login_pri.password);
+ new_ext->dtp = NULL; // update in retr/stor commands
+ new_ext->reference = 1;
+ return new_ext;
+ }
+
+ static void ftp_set_tcp_addr(const struct tcphdr *tcph, struct ftp_session_addr *addr, enum flow_type fdir)
+ {
+ if (FLOW_TYPE_C2S == fdir)
+ {
+ addr->sport = 0; // tcph->th_sport;
+ addr->dport = tcph->th_dport;
+ }
+ else
+ {
+ addr->sport = 0; // tcph->th_dport;
+ addr->dport = tcph->th_sport;
+ }
+ }
+ static void ftp_set_ipv4_addr(const struct ip *ip4h, struct ftp_session_addr *addr, enum flow_type fdir)
+ {
+ addr->af_inet = AF_INET;
+ if (FLOW_TYPE_C2S == fdir)
+ {
+ addr->saddr4 = ip4h->ip_src.s_addr;
+ addr->daddr4 = ip4h->ip_dst.s_addr;
+ }
+ else
+ {
+ addr->saddr4 = ip4h->ip_dst.s_addr;
+ addr->daddr4 = ip4h->ip_src.s_addr;
+ }
+ }
+ static void ftp_set_ipv6_addr(const struct ip6_hdr *ip6h, struct ftp_session_addr *addr, enum flow_type fdir)
+ {
+ addr->af_inet = AF_INET6;
+ if (FLOW_TYPE_C2S == fdir)
+ {
+ memcpy(&addr->saddr6, &ip6h->ip6_src, sizeof(struct in6_addr));
+ memcpy(&addr->daddr6, &ip6h->ip6_dst, sizeof(struct in6_addr));
+ }
+ else
+ {
+ memcpy(&addr->saddr6, &ip6h->ip6_dst, sizeof(struct in6_addr));
+ memcpy(&addr->daddr6, &ip6h->ip6_src, sizeof(struct in6_addr));
+ }
+ }
+
+ void ftp_session_get_addr(const struct session *sess, struct ftp_session_addr *addr)
+ {
+ if (sess == NULL || addr == NULL)
+ {
+ return;
+ }
+ enum flow_type fdir = session_get_flow_type(sess);
+ const struct packet *raw_pkt = session_get_first_packet(sess, fdir);
+ if (NULL == raw_pkt)
+ {
+ addr->af_inet = 0;
+ return;
+ }
+
+ int count = packet_get_layer_count(raw_pkt);
+ for (int i = count - 1; i >= 0; i--)
+ {
+ const struct layer *layer = packet_get_layer_by_idx(raw_pkt, i);
+ if (layer->proto == LAYER_PROTO_TCP)
+ {
+ ftp_set_tcp_addr(layer->hdr.tcp, addr, fdir);
+ }
+ else if (layer->proto == LAYER_PROTO_IPV4)
+ {
+ ftp_set_ipv4_addr(layer->hdr.ip4, addr, fdir);
+ break;
+ }
+ else if (layer->proto == LAYER_PROTO_IPV6)
+ {
+ ftp_set_ipv6_addr(layer->hdr.ip6, addr, fdir);
+ break;
+ }
+ }
+ }
+
+ int ftp_ctrl_identify_by_payload(const char *payload, size_t len, enum flow_type curdir)
+ {
+ if (NULL == payload || len < FTP_IDENTIRY_MIN_LEN)
+ {
+ return 0;
+ }
+ if (curdir == FLOW_TYPE_C2S)
+ {
+ if ((strncasecmp(payload, "USER", 4) == 0) ||
+ (strncasecmp(payload, "OPTS", 4) == 0) ||
+ (strncasecmp(payload, "SYST", 4) == 0) ||
+ (strncasecmp(payload, "STAT", 4) == 0))
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ if (memcmp(payload, "220", 3) == 0)
+ {
+ int tmplen = MIN(len, FTP_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;
+ }
+#if 0
+ /* return value need free after used */
+ const char *ftp_path_update(const char *current_path, const char *new_cwd_path)
+ {
+ size_t current_path_len = current_path ? strlen(current_path) : 0;
+ size_t new_path_len = new_cwd_path ? strlen(new_cwd_path) : 0;
+
+ if (NULL == new_cwd_path || new_path_len == 0)
+ {
+ return strdup(current_path);
+ }
+
+ if (NULL == current_path || current_path_len == 0)
+ {
+ if (new_cwd_path)
+ {
+ if ('/' == new_cwd_path[0])
+ {
+ return strdup(new_cwd_path);
+ }
+ else
+ {
+ size_t tmp_len = strlen(new_cwd_path) + 2;
+ char *new_path = (char *)calloc(1, tmp_len);
+ snprintf(new_path, tmp_len, "/%s", new_cwd_path);
+ return new_path;
+ }
+ }
+ return NULL;
+ }
+
+ char *new_path = (char *)calloc(1, current_path_len + new_path_len + 1);
+ if (current_path[0] == '/')
+ {
+ snprintf(new_path, PATH_MAX, "%s/%s", current_path, new_cwd_path);
+ }
+ else
+ {
+ snprintf(new_path, PATH_MAX, "/%s/%s", current_path, new_cwd_path);
+ }
+ return new_path;
+ }
+#endif
+
+ static void ftp_normalize_path(const char *input, char *output, size_t size)
+ {
+ if (!input || !output || size == 0)
+ {
+ if (output)
+ {
+ output[0] = '\0';
+ }
+ return;
+ }
+
+ char *stack[PATH_MAX]; // Use pointers for components, no deep copying.
+ int stack_index = 0;
+
+ // Temporary buffer for tokenization
+ char temp_path[PATH_MAX];
+ strncpy(temp_path, input, sizeof(temp_path) - 1);
+ temp_path[sizeof(temp_path) - 1] = '\0';
+
+ // Tokenize the path and process components
+ char *token = strtok(temp_path, "/");
+ while (token)
+ {
+ if (strcmp(token, "..") == 0)
+ {
+ if (stack_index > 0)
+ stack_index--; // Pop from stack
+ }
+ else if (strcmp(token, ".") != 0 && strlen(token) > 0)
+ {
+ stack[stack_index++] = token; // Push valid component
+ }
+ token = strtok(NULL, "/");
+ }
+
+ // Build the normalized path
+ size_t len = 0;
+ output[0] = '\0';
+ for (int i = 0; i < stack_index; i++)
+ {
+ len += snprintf(output + len, size - len, "/%s", stack[i]);
+ if (len >= size)
+ break; // Avoid buffer overflow
+ }
+
+ // Ensure at least "/" is returned
+ if (stack_index == 0)
+ strncpy(output, "/", size - 1);
+
+ output[size - 1] = '\0'; // Null-terminate the output
+ }
+
+ /*
+ * According to the cwd, the server address, the file name of stor/retr command,
+ * concatenated them into a complete absolute uri path.
+ */
+ void ftp_join_absolute_path(const char *dir, const char *file, char *result, size_t size)
+ {
+ if (!dir || !file || !result || size == 0)
+ {
+ if (result)
+ {
+ result[0] = '\0';
+ }
+ return;
+ }
+ char combined_path[PATH_MAX] = {};
+ // If file is absolute path, use it directly
+ if (file[0] == '/')
+ {
+ strncpy(combined_path, file, sizeof(combined_path) - 1);
+ combined_path[sizeof(combined_path) - 1] = '\0';
+ }
+ else
+ {
+ if (dir[0] == '\0')
+ {
+ snprintf(combined_path, sizeof(combined_path), "/%s", file);
+ }
+ else
+ {
+ // Otherwise, join dir and file
+ if (dir[strlen(dir) - 1] == '/')
+ {
+ snprintf(combined_path, sizeof(combined_path), "%s%s", dir, file);
+ }
+ else
+ {
+ snprintf(combined_path, sizeof(combined_path), "%s/%s", dir, file);
+ }
+ }
+ }
+ // Normalize the combined path
+ ftp_normalize_path(combined_path, result, size);
+ }
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file