diff options
Diffstat (limited to 'decoders/ftp/ftp_decoder_util.c')
| -rw-r--r-- | decoders/ftp/ftp_decoder_util.c | 400 |
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 |
