diff options
Diffstat (limited to 'decoders/mail/mail.c')
| -rw-r--r-- | decoders/mail/mail.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/decoders/mail/mail.c b/decoders/mail/mail.c new file mode 100644 index 0000000..7516548 --- /dev/null +++ b/decoders/mail/mail.c @@ -0,0 +1,514 @@ +#include <stdio.h> +#include <limits.h> +#include <ctype.h> +#include <assert.h> +#include <sys/time.h> + +#include "stellar/packet.h" +#include "stellar/utils.h" +#include "stellar/mq.h" +#include "stellar/session.h" +#include "stellar/mail.h" + +#include "mail_internal.h" +#include "mail_parser.h" + +#define PORT_SMTP 25 +#define PORT_POP3 110 +#define PORT_IMAP 143 + +static int is_current_packet_c2s(struct session *sess) +{ + const struct layer *first_pkt_layer, *curr_pkt_layer; + + const struct packet *first_pkt, *curr_pkt; + + first_pkt = session_get_first_packet(sess, FLOW_TYPE_C2S); + curr_pkt = session_get_current_packet(sess); + + int i = 0; + int layer_count = packet_get_layer_count(first_pkt); + + for (i = 0; i < 2 /* l3/l4 */; i++) { + first_pkt_layer = packet_get_layer_by_idx(first_pkt, layer_count - 1 -i); + curr_pkt_layer = packet_get_layer_by_idx(curr_pkt, layer_count - 1 -i); + + if (first_pkt_layer->proto != curr_pkt_layer->proto) { + return 0; + } + switch (first_pkt_layer->proto) { + case LAYER_PROTO_TCP: + if (0 != memcmp(first_pkt_layer->hdr.tcp, curr_pkt_layer->hdr.tcp, sizeof(struct tcphdr))) { + return 0; + } + break; + case LAYER_PROTO_UDP: + if (0 != memcmp(first_pkt_layer->hdr.udp, curr_pkt_layer->hdr.udp, sizeof(struct udphdr))) { + return 0; + } + break; + case LAYER_PROTO_IPV4: + if (0 != memcmp(first_pkt_layer->hdr.ip4, curr_pkt_layer->hdr.ip4, sizeof(struct ip))) { + return 0; + } + break; + case LAYER_PROTO_IPV6: + if (0 != memcmp(first_pkt_layer->hdr.ip6, curr_pkt_layer->hdr.ip6, sizeof(struct ip6_hdr))) { + return 0; + } + break; + default: + return 0; + } + + } + + return 1; +} + +static int mail_subscribe_command(struct mail_env *mail_env, mail_command_callback_func *cb, void *arg) +{ + int topic; + struct mq_schema *schema; + + schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref); + if (schema == NULL) { + return -1; + } + + topic = mq_schema_get_topic_id(schema, MAIL_COMMAND_TOPIC_NAME); + if (topic < 0) { + return -1; + } + + return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg); +} + +static int mail_subscribe_header(struct mail_env *mail_env, mail_header_callback_func *cb, void *arg) +{ + int topic; + struct mq_schema *schema; + + schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref); + if (schema == NULL) { + return -1; + } + + topic = mq_schema_get_topic_id(schema, MAIL_HEADER_TOPIC_NAME); + if (topic < 0) { + return -1; + } + + return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg); +} + +static int mail_subscribe_body(struct mail_env *mail_env, mail_body_callback_func *cb, void *arg) +{ + int topic; + struct mq_schema *schema; + + schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref); + if (schema == NULL) { + return -1; + } + + topic = mq_schema_get_topic_id(schema, MAIL_BODY_TOPIC_NAME); + if (topic < 0) { + return -1; + } + + return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg); +} + +static int mail_subscribe_attachment(struct mail_env *mail_env, mail_attachment_callback_func *cb, void *arg) +{ + int topic; + struct mq_schema *schema; + + schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref); + if (schema == NULL) { + return -1; + } + + topic = mq_schema_get_topic_id(schema, MAIL_ATTACHMENT_TOPIC_NAME); + if (topic < 0) { + return -1; + } + + return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg); +} + +static int mail_subscribe_eml(struct mail_env *mail_env, mail_eml_callback_func *cb, void *arg) +{ + int topic; + struct mq_schema *schema; + + schema = module_manager_get_mq_schema(mail_env->mod_mgr_ref); + if (schema == NULL) { + return -1; + } + + topic = mq_schema_get_topic_id(schema, MAIL_EML_TOPIC_NAME); + if (topic < 0) { + return -1; + } + + return mq_schema_subscribe(schema, topic, (on_msg_cb_func *)(void *)cb, arg); +} + +static void mail_dispatch(int topic, void *msg, on_msg_cb_func *msg_cb, void *arg, void *dispatch_arg) +{ + struct mail_message *mail_msg; + struct mail_env *mail_env; + + mail_msg = (struct mail_message *)msg; + mail_env = (struct mail_env *)dispatch_arg; + + if (topic == mail_env->command_topic_id) { + mail_command_callback_func *command_cb = (mail_command_callback_func *)(void *)msg_cb; + command_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq, + mail_msg->command, mail_msg->command_param, mail_msg->command_param_len, + mail_msg->command_line, mail_msg->command_line_len, + arg); + return; + } + + if (topic == mail_env->header_topic_id) { + mail_header_callback_func *header_cb = (mail_header_callback_func *)(void *)msg_cb; + header_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq, &mail_msg->header, arg); + return; + } + + if (topic == mail_env->body_topic_id) { + mail_body_callback_func *body_cb = (mail_body_callback_func *)(void *)msg_cb; + body_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq, + mail_msg->body, mail_msg->body_len, + mail_msg->body_offset, mail_msg->is_body_finished, + arg); + return; + } + + if (topic == mail_env->attachment_topic_id) { + mail_attachment_callback_func *attachment_cb = (mail_attachment_callback_func *)(void *)msg_cb; + attachment_cb(mail_msg->sess_ref, mail_msg->mail_protocol, mail_msg->mail_seq, + mail_msg->attachment_name, mail_msg->attachment_name_len, + mail_msg->attachment, mail_msg->attachment_len, + mail_msg->attachment_offset, mail_msg->is_attachment_finished, + arg); + return; + } + + if (topic == mail_env->eml_topic_id) { + mail_eml_callback_func *eml_cb = (mail_eml_callback_func *)(void *)msg_cb; + eml_cb(mail_msg->sess_ref, mail_msg->mail_protocol, + mail_msg->eml, mail_msg->eml_len, + mail_msg->eml_offset, mail_msg->is_eml_finished, + arg); + return; + } +} + +int mail_subscribe(struct mail_env *mail_env, + mail_command_callback_func command_cb, + mail_header_callback_func *header_cb, + mail_body_callback_func *body_cb, + mail_attachment_callback_func *attachment_cb, + mail_eml_callback_func *eml_cb, + void *arg) +{ + int ret; + + ret = mail_subscribe_command(mail_env, command_cb, arg); + if (ret < 0) { + return ret; + } + + ret = mail_subscribe_header(mail_env, header_cb, arg); + if (ret < 0) { + return ret; + } + + ret = mail_subscribe_body(mail_env, body_cb, arg); + if (ret < 0) { + return ret; + } + + ret = mail_subscribe_attachment(mail_env, attachment_cb, arg); + if (ret < 0) { + return ret; + } + + ret = mail_subscribe_eml(mail_env, eml_cb, arg); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void mail_session_ctx_free(struct mail_session_ctx *session_ctx) +{ + switch (session_ctx->protocol) { + case MAIL_PROTOCOL_SMTP: + if (session_ctx->smtp_parser) { + smtp_parser_free(session_ctx->smtp_parser); + } + break; + case MAIL_PROTOCOL_POP3: + break; + case MAIL_PROTOCOL_IMAP: + break; + default: + break; + } + free(session_ctx); +} + +static struct mail_session_ctx *mail_session_ctx_new(void) +{ + struct mail_session_ctx *session_ctx; + session_ctx = (struct mail_session_ctx *)calloc(1, sizeof(struct mail_session_ctx)); + return session_ctx; +} + +static int mail_info_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s) +{ + int ret; + + ret = smtp_parser_get_command(session_ctx->smtp_parser); + if (ret == 0) { + mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len); + } + + struct mail_info *mail; + mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser); + if (mail) { + mail_info_get_header(); + mail_info_get_body(); + mail_info_get_attachment(); + } +} + +static int mail_imap_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s) +{ + int ret; + + ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s); + if (ret != 0) { + return; + } + + ret = smtp_parser_get_command(session_ctx->smtp_parser); + if (ret == 0) { + mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len); + } + + struct mail_info *mail; + mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser); + if (mail) { + mail_info_get_header(); + mail_info_get_body(); + mail_info_get_attachment(); + } +} + +static int mail_pop3_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s) +{ + int ret; + + ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s); + if (ret != 0) { + return; + } + + ret = smtp_parser_get_command(session_ctx->smtp_parser); + if (ret == 0) { + mail_publish_command(mail_env, sess, mail_protocol, mail_seq, cmd, cmd_param, cmd_param_len, cmd_line, cmd_line_len); + } + + struct mail_info *mail; + mail = smtp_parser_get_mailinfo(session_ctx->smtp_parser); + if (mail) { + mail_info_get_header(); + mail_info_get_body(); + mail_info_get_attachment(); + } +} + +static int mail_smtp_process(struct mail_env *mail_env, struct mail_session_ctx *session_ctx, const char *payload, size_t payload_len, int is_c2s) +{ + int ret; + + if (session_ctx->smtp_parser == NULL) { + session_ctx->smtp_parser = smtp_parser_new(mail_env); + } + + ret = smtp_parser_process(session_ctx->smtp_parser, payload, payload_len , is_c2s); + if (ret != 0) { + return -1; + } + + return 0; +} + +static void mail_on_tcp_payload(struct session *sess, enum session_state state, const char *payload, uint32_t payload_len, void *arg) +{ + (void)state; + int ret; + int is_packet_c2s; + struct mail_session_ctx *session_ctx; + struct mail_env *mail_env = (struct mail_env *)arg; + + if (payload == NULL || payload_len == 0) { + return; + } + + session_ctx = (struct mail_session_ctx *)session_get_exdata(sess, mail_env->exdata_id); + if (session_ctx == NULL) { + session_ctx = mail_session_ctx_new(); + session_ctx->mail_env_ref = mail_env; + session_ctx->sess_ref = sess; + session_ctx->protocol = MAIL_PROTOCOL_MAX; + session_ctx->is_mail_protocol = 1; + session_set_exdata(sess, mail_env->exdata_id, session_ctx); + + if (smtp_identify(payload, payload_len, is_packet_c2s)) { + session_ctx->protocol = MAIL_PROTOCOL_SMTP; + } + //if (pop3_identify((char *)payload, payload_len, is_packet_c2s)) { + // session_ctx->protocol = MAIL_PROTOCOL_POP3; + //} + + //if (imap_identify((char *)payload, payload_len, is_packet_c2s)) { + // session_ctx->protocol = MAIL_PROTOCOL_IMAP; + //} + } + + is_packet_c2s = is_current_packet_c2s(sess); + + switch (session_ctx->protocol) { + case MAIL_PROTOCOL_SMTP: + mail_smtp_process(mail_env, session_ctx, payload, payload_len, is_packet_c2s); + break; + case MAIL_PROTOCOL_POP3: + //ret = pop3_parser_process(session_ctx->smtp, payload, payload_len , is_packet_c2s); + //if (ret != 0) { + // return; + //} + break; + case MAIL_PROTOCOL_IMAP: + //ret = imap_parser_process(session_ctx->smtp, payload, payload_len , is_packet_c2s); + //if (ret != 0) { + // return; + //} + break; + default: + break; + } +} + +void mail_on_exdata_free(int idx, void *ex_ptr, void *arg) +{ + (void)(idx); + (void)(arg); + struct mail_session_ctx *session_ctx = (struct mail_session_ctx *)ex_ptr; + + if (session_ctx) { + mail_session_ctx_free(session_ctx); + } +} + +static void mail_on_message_free(void *msg, void *arg) +{ + (void)(arg); + struct mail_message *mail_msg; + + mail_msg = (struct mail_message *)msg; + if (mail_msg) { + free(mail_msg); + } +} + +void mail_exit(struct module_manager *mod_mgr, struct module *mod) +{ + (void)(mod_mgr); + struct mail_env *mail_env; + struct mq_schema *schema; + + if (mod) { + mail_env = (struct mail_env *)module_get_ctx(mod); + if (mail_env) { + schema = module_manager_get_mq_schema(mod_mgr); + mq_schema_destroy_topic(schema, mail_env->command_topic_id); + mq_schema_destroy_topic(schema, mail_env->header_topic_id); + mq_schema_destroy_topic(schema, mail_env->body_topic_id); + mq_schema_destroy_topic(schema, mail_env->attachment_topic_id); + mq_schema_destroy_topic(schema, mail_env->eml_topic_id); + free(mail_env); + } + + module_free(mod); + } +} + +struct module* mail_init(struct module_manager *mod_mgr) +{ + int ret; + struct module *mod; + struct mq_schema *schema; + struct session_manager *sess_mgr; + struct mail_env *mail_env; + + mail_env = (struct mail_env *)calloc(1, sizeof(struct mail_env)); + mail_env->mod_mgr_ref = mod_mgr; + mod = module_new(MAIL_MODULE_NAME, mail_env); + sess_mgr = module_to_session_manager(module_manager_get_module(mod_mgr, SESSION_MANAGER_MODULE_NAME)); + schema = module_manager_get_mq_schema(mod_mgr); + + if (sess_mgr == NULL || schema == NULL) { + goto exit; + } + + ret = session_manager_subscribe_tcp_stream(sess_mgr, mail_on_tcp_payload, mail_env); + if (ret < 0) { + goto exit; + } + + mail_env->exdata_id = session_manager_new_session_exdata_index(sess_mgr, MAIL_EXDATA_NAME, mail_on_exdata_free, NULL); + if (mail_env->exdata_id < 0) { + goto exit; + } + + mail_env->command_topic_id = mq_schema_create_topic(schema, MAIL_COMMAND_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL); + if (mail_env->command_topic_id < 0) { + goto exit; + } + mail_env->header_topic_id = mq_schema_create_topic(schema, MAIL_HEADER_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL); + if (mail_env->header_topic_id < 0) { + goto exit; + } + mail_env->body_topic_id = mq_schema_create_topic(schema, MAIL_BODY_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL); + if (mail_env->body_topic_id < 0) { + goto exit; + } + mail_env->attachment_topic_id = mq_schema_create_topic(schema, MAIL_ATTACHMENT_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL); + if (mail_env->attachment_topic_id < 0) { + goto exit; + } + mail_env->eml_topic_id = mq_schema_create_topic(schema, MAIL_EML_TOPIC_NAME, mail_dispatch, mail_env, mail_on_message_free, NULL); + if (mail_env->eml_topic_id < 0) { + goto exit; + } + + return mod; +exit: + mail_exit(mod_mgr, mod); + return NULL; +} + +struct mail_env *module_to_mail_env(struct module *mod) +{ + assert(mod); + assert(strcmp(module_get_name(mod), MAIL_MODULE_NAME) == 0); + return module_get_ctx(mod); +} |
