#include #include #include #include #include "mail_decoder_util.h" #include "mail_decoder_codec.h" #include "mail_decoder_email.h" #include "mail_decoder_smtp.h" const char* g_smtp_command[] = { "unknown", "ehlo", "helo", "auth", "starttls", "mail", "send", "soml", "saml", "rcpt", "data", "vrfy", "expn", "noop", "rset", "quit", }; static enum SMTP_COMMAND smtp_str2command(const char *payload, size_t payload_len) { int i; if (payload == NULL || payload_len == 0) { return SMTP_COMMAND_UNKNOWN; } for(i = 1; i < SMTP_COMMAND_MAX; i++) { if(0 == SAFE_STRNCASECMP(payload, payload_len, g_smtp_command[i], strlen(g_smtp_command[i]))) { break; } } if (i == SMTP_COMMAND_MAX) { return SMTP_COMMAND_UNKNOWN; } return (enum SMTP_COMMAND)i; } static int is_smtp_cmd(const char *data, int datalen) { if (SMTP_COMMAND_UNKNOWN == smtp_str2command(data, datalen)) { return 0; } return 1; } static int is_smtp_res(const char *data, size_t datalen) { if (datalen >= strlen(SMTP_STR_220) && 0 == strncmp(data, SMTP_STR_220, strlen(SMTP_STR_220))) { return 1; } return 0; } static int is_wanted_line(struct smtp_parser *parser, char *a_smtp_line, int linelen, int apply_count, int *authmatchlen) { enum EML_LINE_TYPE eml_line_type; struct email_parser *email = parser->eml_parser; if((email->EmlAnalyseState == MAIL_STAT_INIT) || (apply_count < RSETQUIT_CMD_LEN)) { if(strncmp_one_word_mesa("rset", "RSET", 4, a_smtp_line, linelen)) //�������� return SMTP_CMD_RSET; if(strncmp_one_word_mesa("quit", "QUIT", 4, a_smtp_line, linelen)) //�˳����� return SMTP_CMD_QUIT; } if((email->EmlAnalyseState == MAIL_STAT_INIT) || (apply_count < MAIL_FROM_LEN)) { if(strcmp_two_word(a_smtp_line,linelen, "MAIL", 4, "FROM", 4)) return SMTP_CMD_MAIL_FROM; } if(email->EmlAnalyseState == MAIL_STAT_INIT) { if(strcmp_two_word(a_smtp_line, linelen, "RCPT", 4, "TO", 2)) return SMTP_CMD_RCPT_TO; if(strncmp_one_word_mesa("ehlo", "EHLO", 4, a_smtp_line, linelen)) { if(email->MailInfoState==MAIL_NO_USER) email->MailInfoState=MAIL_HAVE_USER; return SMTP_CMD_EHLO; } if(strncmp_one_word_mesa("helo", "HELO", 4, a_smtp_line, linelen)) { if(email->MailInfoState==MAIL_NO_USER) email->MailInfoState=MAIL_AFTER_PASS; return SMTP_CMD_EHLO; } if(strncmp_one_word_mesa("data", "DATA", 4, a_smtp_line,linelen)) return SMTP_CMD_DATA; if(strncmp_one_word_mesa("starttls", "STARTTLS", 8, a_smtp_line,linelen)) return SMTP_CMD_STARTTLS; if(strcmp_two_word(a_smtp_line, linelen, "send", 4, "from", 4)) return SMTP_CMD_MAIL_FROM; if(strcmp_two_word(a_smtp_line,linelen, "soml", 4, "from", 4)) return SMTP_CMD_MAIL_FROM; if(strcmp_two_word(a_smtp_line,linelen, "saml", 4, "from", 4)) return SMTP_CMD_MAIL_FROM; if((*authmatchlen=strncmp_two_word_mesa("auth", "AUTH", 4, "xoauth2", "XOAUTH2", 7, a_smtp_line, linelen))!=0) return SMTP_CMD_XOAUTH2; } //if((apply_count < BDAT_CMD_LEN) && (strncmp_one_word_mesa("bdat", "BDAT", 4, a_smtp_line,linelen))) // return SMTP_CMD_BDAT; if((email->EmlAnalyseState == MAIL_STAT_BODY) && ((linelen==3 && !strncmp(a_smtp_line, ".\r\n",3)) || (linelen==2 && !strncmp(a_smtp_line, ".\n",2)))) return EML_DATA_END; if(email->is_drop) return SMTP_CMD_DROP; eml_line_type = email_parser_parse_line(a_smtp_line, linelen, email->EmlAnalyseState); if(eml_line_type != EML_UNKNOWN) return eml_line_type; //add by lqy 060526 if((email->EmlAnalyseState == MAIL_STAT_INIT)) return SMTP_CMD_USER_PASS; //maybe AUTH XXXX, EHLO, HELO; ������������Ϊ����������ʼ�ͷ���� return SMTP_CMD_UNKNOWN; } static int process_auth_plain(struct smtp_parser *parser, char * a_smtp_line,int datalen) { int i, ret; int user_flag=0, passward_flag=0; char* point1=NULL, *point2=NULL, *point_tmp=NULL; char* begin=NULL; char decode[SMTP_PLAIN_MAXLEN]={0}; struct email_parser *email = parser->eml_parser; begin = a_smtp_line; while(*begin == ' ') { begin++; datalen--; } while(datalen > 0 && (*(a_smtp_line + datalen -1)=='\n' || *(a_smtp_line + datalen -1)=='\r')) datalen -= 1; //now @decode is the BASE64 of user&pass---"AHpsdzM1NDI2MTc5MgB6bHdAMzU=" ret = Base64_DecodeBlock((unsigned char *)begin, datalen, (unsigned char *)decode, SMTP_PLAIN_MAXLEN); if(ret < 0) { char buf[128]={0}; memcpy(buf, begin, datalen>=128?127:datalen); return -1; } decode[ret] = '\0'; for(i=0;iMailInfoState = MAIL_AFTER_PASS | MAIL_BEFORE_PASS; datalen = point2-point1-1; email->plug_mailinfo.pMailInfo->username->buflen = 0; mail_save_mail_elem(point1, datalen, email->plug_mailinfo.pMailInfo->username); datalen = strlen(point2); email->plug_mailinfo.pMailInfo->password->buflen = 0; mail_save_mail_elem(point2, datalen, email->plug_mailinfo.pMailInfo->password); } else { return -1; } return SMTP_PLAIN_FLAG; } static int process_user_pass(struct smtp_parser *parser, struct session *sess, char *a_smtp_line, int datalen) { size_t mail_seq; struct email_parser *email = parser->eml_parser; if(strncmp_one_word_mesa("auth login", "AUTH LOGIN", 10, a_smtp_line, datalen)) { email->MailInfoState=MAIL_BEFORE_USER; return 0; } if(strncmp_one_word_mesa("auth plain", "AUTH PLAIN", SMTP_STR_PLAIN_LEN, a_smtp_line, datalen)) { if(email->MailInfoState!=MAIL_AUTH_PLAIN && datalen<=SMTP_STR_PLAIN_LEN+2) // sizeof(r\n)=2 { email->MailInfoState=MAIL_AUTH_PLAIN; return 0; } return process_auth_plain(parser, a_smtp_line+SMTP_STR_PLAIN_LEN+1,datalen-SMTP_STR_PLAIN_LEN-1); } if(email->MailInfoState==MAIL_AUTH_PLAIN) { return process_auth_plain(parser, a_smtp_line,datalen); } if(email->MailInfoState==MAIL_AFTER_PASS) return 0; if((email->MailInfoState==MAIL_BEFORE_USER)||(email->MailInfoState==MAIL_BEFORE_PASS)) { if(datalen >= MAX_MAIL_USER_LEN) return -1; char cOutBuf[MAX_MAIL_USER_LEN]; int linelen; while(datalen > 0 && (*(a_smtp_line + datalen -1)=='\n' || *(a_smtp_line + datalen -1)=='\r')) datalen -= 1; linelen = Base64_DecodeBlock((unsigned char *)a_smtp_line, datalen, (unsigned char *)cOutBuf, MAX_MAIL_USER_LEN); if(linelen < 0) { char buf[128]={0}; memcpy(buf, a_smtp_line, datalen>=128?127:datalen); return -1; } if(linelen > MAX_USER_PASS_LEN) { email->MailInfoState=MAIL_AFTER_PASS; return 0; } if(email->MailInfoState == MAIL_BEFORE_USER) { email->MailInfoState=MAIL_BEFORE_PASS; email->plug_mailinfo.pMailInfo->username->buflen = 0; mail_save_mail_elem(cOutBuf, linelen, email->plug_mailinfo.pMailInfo->username); return 1; } else if(email->MailInfoState == MAIL_BEFORE_PASS) { email->MailInfoState = MAIL_AFTER_PASS; email->plug_mailinfo.pMailInfo->password->buflen = 0; mail_save_mail_elem(cOutBuf, linelen, email->plug_mailinfo.pMailInfo->password); return 1; } } if((email->MailInfoState & MAIL_BEFORE_PASS) && email->plug_mailinfo.pMailInfo->username->buf != NULL) { mail_command_update(parser->current_command, MAIL_CMD_USERNAME, email->plug_mailinfo.pMailInfo->username->buf, email->plug_mailinfo.pMailInfo->username->buflen, email->plug_mailinfo.pMailInfo->username->buf, email->plug_mailinfo.pMailInfo->username->buflen); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); } if((email->MailInfoState & MAIL_AFTER_PASS) && email->plug_mailinfo.pMailInfo->password->buf != NULL) { mail_command_update(parser->current_command, MAIL_CMD_PASSWORD, email->plug_mailinfo.pMailInfo->password->buf, email->plug_mailinfo.pMailInfo->password->buflen, email->plug_mailinfo.pMailInfo->password->buf, email->plug_mailinfo.pMailInfo->password->buflen); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); } return 0; } int process_oauth2(struct smtp_parser *parser, struct session *sess, char *cmddata, int datalen) { char *p, decode_buf[2048], username[128]; int decoded_len, namelen=0; size_t mail_seq; struct email_parser *email = parser->eml_parser; if((decoded_len=Base64_DecodeBlock((unsigned char*)cmddata, datalen, (unsigned char*)decode_buf, 2048)) < 0) { return 0; } if(!strncmp_one_word_mesa("user=", "USER=", 5, decode_buf, decoded_len)) { return 0; } p=decode_buf; p += 5; decoded_len-=5; while(decoded_len>0 && namelen<128 && *p!=0x1) { username[namelen++] = *p; p++; decoded_len--; } email->plug_mailinfo.pMailInfo->username->buflen = 0; mail_save_mail_elem(username, namelen, parser->eml_parser->plug_mailinfo.pMailInfo->username); mail_command_update(parser->current_command, MAIL_CMD_USERNAME, email->plug_mailinfo.pMailInfo->username->buf, email->plug_mailinfo.pMailInfo->username->buflen, email->plug_mailinfo.pMailInfo->username->buf, email->plug_mailinfo.pMailInfo->username->buflen); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); return 0; } int process_ehlo(struct smtp_parser *parser, struct session *sess, const char *line, int len) { const char *line_start; const char *arg_start; size_t line_len; size_t arg_len; size_t mail_seq; while(len > 0 && (*(line + len -1)=='\n' || *(line + len -1)=='\r')) { len -= 1; } line_start = line; line_len = len; len -= strlen("ehlo "); line += strlen("echo "); if(len <= 0) { return -1; } arg_start = line; arg_len = len; mail_command_update(parser->current_command, MAIL_CMD_EHLO, arg_start, arg_len, line_start, line_len); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); return 0; } int process_mail_from(struct smtp_parser *parser, struct session *sess, char *line, int len) { int ret; const char *line_start; const char *arg_start; size_t line_len; size_t arg_len; size_t mail_seq; while(len > 0 && (*(line + len -1)=='\n' || *(line + len -1)=='\r')) { len -= 1; } line_start = line; line_len = len; len -= strlen("mail from:"); line += strlen("mail from:"); if(len <= 0) { return -1; } parser->eml_parser->plug_mailinfo.pMailInfo->sendaddr->buflen = 0; ret = mail_get_mailaddr(parser->eml_parser->plug_mailinfo.pMailInfo->sendaddr, line, len); if(ret < 0) { return -1; } arg_start = parser->eml_parser->plug_mailinfo.pMailInfo->sendaddr->buf; arg_len = parser->eml_parser->plug_mailinfo.pMailInfo->sendaddr->buflen; mail_command_update(parser->current_command, MAIL_CMD_MAIL_FROM, arg_start, arg_len, line_start, line_len); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); return 0; } int process_rcpt_to(struct smtp_parser *parser, struct session *sess, char *line, int len) { int ret; const char *line_start; const char *arg_start; size_t line_len; size_t arg_len; size_t mail_seq; while(len > 0 && (*(line + len -1)=='\n' || *(line + len -1)=='\r')) { len -= 1; } line_start = line; line_len = len; len -= strlen("rcpt to:"); line += strlen("rcpt to:"); if(len <= 0) { return -1; } parser->eml_parser->plug_mailinfo.pMailInfo->recvaddr->buflen = 0; // clear recvaddr ret = mail_get_mailaddr(parser->eml_parser->plug_mailinfo.pMailInfo->recvaddr, line, len); if(ret < 0) { return -1; } arg_start = parser->eml_parser->plug_mailinfo.pMailInfo->recvaddr->buf; arg_len = parser->eml_parser->plug_mailinfo.pMailInfo->recvaddr->buflen; mail_command_update(parser->current_command, MAIL_CMD_RCPT_TO, arg_start, arg_len, line_start, line_len); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); return 0; } int process_starttls(struct smtp_parser *parser, struct session *sess, char *line, int len) { const char *line_start; size_t line_len; size_t mail_seq; while(len > 0 && (*(line + len -1)=='\n' || *(line + len -1)=='\r')) { len -= 1; } line_start = line; line_len = len; mail_command_update(parser->current_command, MAIL_CMD_STARTTLS, NULL, 0, line_start, line_len); mail_seq = email_parser_get_seq(parser->eml_parser); mail_publish_command(parser->mq, parser->command_topic_id, sess, MAIL_PROTOCOL_SMTP, mail_seq, parser->current_command); return 0; } int smtp_identify(const char *payload, size_t payload_len, int is_c2s) { if (payload == NULL || payload_len < 4) { return 0; } if (is_c2s) { if (is_smtp_cmd(payload, payload_len)) { return 1; } } else { if (is_smtp_res(payload, payload_len)) { return 1; } } return 0; } int smtp_parser_entry(struct smtp_parser *parser, struct session *sess, const char *payload, size_t payload_len, int is_c2s) { int ret; int needfolding=0; int line_status = MAIL_FOLD_LINE_START; char *output_line = NULL; int outputlen=0, matchlen; struct email_parser *email = parser->eml_parser; if (!is_c2s) { return 0; } while(1) { if(email->EmlAnalyseState == MAIL_STAT_HEADER || (email->EmlAnalyseState == MAIL_STAT_BODY && email->mimeinfo->text_begin==0)) needfolding=1; else needfolding=0; line_status = email_parser_get_tcpdata(email, payload, payload_len, needfolding, &output_line, &outputlen); if(line_status == MAIL_INPUT_END) { break; } if(line_status == MAIL_GETLINE_ERR) { return -1; } email->pDataLineBuf.len = 0; line_status = is_wanted_line(parser, output_line, outputlen, payload_len, &matchlen); switch(line_status) { case SMTP_CMD_EHLO: process_ehlo(parser, sess, output_line, outputlen); break; case SMTP_CMD_USER_PASS: if(email->pending_state) { email->pending_state = 0; } process_user_pass(parser, sess, output_line,outputlen); break; case SMTP_CMD_XOAUTH2: process_oauth2(parser, sess, output_line+matchlen, outputlen-matchlen); break; case SMTP_CMD_MAIL_FROM: if(email->pending_state) { email->pending_state = 0; } email->MailInfoState=MAIL_GET_FROM; process_mail_from(parser, sess, output_line, outputlen); break; case SMTP_CMD_RCPT_TO: if(email->pending_state) { email->pending_state = 0; } email->MailInfoState = MAIL_GET_TO; process_rcpt_to(parser, sess, output_line, outputlen); break; case SMTP_CMD_DATA: if(email->pending_state) { email->pending_state = 0; } email->EmlAnalyseState = MAIL_STAT_HEADER; break; case SMTP_CMD_BDAT: case SMTP_CMD_UNKNOWN: case SMTP_CMD_DROP: break; case SMTP_CMD_STARTTLS: process_starttls(parser, sess, output_line, strlen("starttls")); return -1; case SMTP_CMD_RSET: case SMTP_CMD_QUIT: case EML_DATA_END: if(email->pending_state == 0) { ret = email_parser_entry(parser->eml_parser, sess, output_line, outputlen, EML_DATA_END); if (ret != 0) { return -1; } } break; default: if(email->pending_state) { email->pending_state = 0; } ret = email_parser_entry(parser->eml_parser, sess, output_line, outputlen, line_status); if (ret != 0) { return -1; } break; } } email->iDataPointer = 0; return 0; } void smtp_parser_free(struct smtp_parser *parser) { if (parser->current_command) { mail_command_free(parser->current_command); } email_parser_free(parser->eml_parser); free(parser); } struct smtp_parser *smtp_parser_new(struct mq_runtime *mq, struct mail_topics *topics, int is_c2s) { struct smtp_parser *parser = (struct smtp_parser *)calloc(1, sizeof(struct smtp_parser)); parser->mq = mq; parser->command_topic_id = topics->topic_ids[MAIL_TOPIC_COMMAND]; parser->current_command = mail_command_new(); parser->eml_parser = email_parser_new(mq, topics, MAIL_PROTOCOL_SMTP, is_c2s); return parser; }