资料出处:http://my.csdn.net/cclongying1989/code/detail/13010
#include "nids.h" //包含Libnids头文件 #include <stdio.h> #include <string.h> //使用bzero() #include "iostream" #include "fstream" #include "time.h" #include "Base64.h" //用来Base64加密解密 using namespace std; #pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" ) // 设置入口地址 char ascii_string[10000]; //用于存放ASCII明文邮件内容的全局变量 char serverip[16]; //存放服务器IP bool sendmail = false; //是否有邮件发送 //读取并分析获取的ASCII字符串内容 char* char_to_ascii(char ch) { //初始化 char* string; ascii_string[0] = 0; string = ascii_string; if(isgraph(ch)) //判断该字符是否是除空格之外可打印的字符(ASCII码33-126之间的字符) *string++ = ch; else if(ch ==' ') //空格或其它字符 *string++ = ch; else if(ch == 'n' || ch == 'r') //如果是回车换行 *string++ = ch; else *string++ = '.'; *string = 0; return ascii_string; } //分析SMTP协议的回调函数 void smtp_protocol_callback(struct tcp_stream* smtp_connection, void** arg) { int i; char content[65535]; char content_urgent[65535]; char content_temp[65535]; char content_gb[65535]; struct tuple4 ip_and_port = smtp_connection->addr; //打开文件,准备随时写入,保证在return之前关闭文件 FILE *fp; fp = fopen("maillog.txt", "a+"); //可读写打开文件 fseek(fp, 0L, 2); //跳到文件结尾 switch(smtp_connection->nids_state) { case NIDS_JUST_EST: if(smtp_connection->addr.dest == 25) //SMTP客户端和SMTP服务器端建立连接 { strcpy(serverip, inet_ntoa(*((struct in_addr *) &(ip_and_port.daddr)))); //记录邮件服务器的IP smtp_connection->client.collect++; //SMTP客户端接收数据 smtp_connection->server.collect++; //SMTP服务器接收数据 smtp_connection->server.collect_urg++; //SMTP服务器接收紧急数据 smtp_connection->client.collect_urg++; //SMTP客户端接收紧急数据 fprintf(fp, "操作:t发送邮件.n"); } fclose(fp); return; case NIDS_CLOSE: //SMTP客户端与SMTP服务器连接正常关闭 time_t datetime; //使用time.h中的结构体获取时间 struct tm *dt; time(&datetime); dt = localtime(&datetime); if(sendmail) { fprintf(fp, "发送邮件成功!n服务器IP:t%sn", serverip); sendmail = false; } else { fprintf(fp, "没有邮件可发送!n服务器IP:t%sn", serverip); } fprintf(fp, "操作时间:t%d.%d.%d %d:%d:%dn", (1900 + dt->tm_year), (1 + dt->tm_mon), dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec); fprintf(fp, "--------------------------n"); fclose(fp); return; case NIDS_RESET: //SMTP客户端与SMTP服务器连接被RST关闭 time_t datetimer; //使用time.h中的结构体获取时间 struct tm *dtr; time(&datetimer); dtr = localtime(&datetimer); fprintf(fp, "发送邮件失败!n服务器IP:t%sn", serverip); fprintf(fp, "操作时间:t%d.%d.%d %d:%d:%dn", (1900 + dtr->tm_year), (1 + dtr->tm_mon), dtr->tm_mday, dtr->tm_hour, dtr->tm_min, dtr->tm_sec); fprintf(fp, "--------------------------n"); fclose(fp); sendmail = false; //被RESET时也要把该变量置为false return; case NIDS_DATA: //SMTP接收到新的数据 { char status_code[4]; struct half_stream* hlf; if(smtp_connection->server.count_new_urg) //SMTP服务器接收到新的紧急数据 { fclose(fp); return; } if(smtp_connection->client.count_new_urg) //SMTP客户端接收到新的紧急数据 { fclose(fp); return; } if(smtp_connection->client.count_new) //SMTP客户端接收到新的数据 { /* hlf = &smtp_connection->client; memcpy(content, hlf->data, hlf->count_new); content[hlf->count_new] = ''; if(strstr(strncpy(status_code, content, 3), "221")) //连接终止 fprintf(fp, "连接终止"); if(strstr(strncpy(status_code, content, 3), "250")) //操作成功 fprintf(fp, "操作成功"); if(strstr(strncpy(status_code, content, 3), "220")) //服务器就绪 fprintf(fp, "服务器就绪"); if(strstr(strncpy(status_code, content, 3), "354")) //开始邮件输入 fprintf(fp, "开始邮件输入"); if(strstr(strncpy(status_code, content, 3), "334")) //服务器响应验证 fprintf(fp, "服务器响应验证"); if(strstr(strncpy(status_code, content, 3), "235")) //认证成功可以发邮件 fprintf(fp, "认证成功可以发送"); for(i = 0; i < hlf->count_new; i++) { fprintf(fp, "%s", char_to_ascii(content[i])); } fprintf(fp, "n"); */ fclose(fp); return; } if(smtp_connection->server.count_new) //SMTP服务器接收到新的数据 { hlf = &smtp_connection->server; memcpy(content, hlf->data, hlf->count_new); content[hlf->count_new] = ''; /* if(strstr(content, "EHLO")) fprintf(fp, "HELLO命令n"); if(strstr(content, "QUIT")) fprintf(fp, "退出连接n"); if(strstr(content, "DATA")) fprintf(fp, "开始传输数据n"); if(strstr(content, "AUTH")) fprintf(fp, "请求认证n"); if(strstr(content, "LOGIN")) fprintf(fp, "认证机制为LOGINn"); if(strstr(content, "n.")) fprintf(fp, "数据传输结束n"); */ /* if(strstr(content, "MAIL FROM")) { fprintf(fp, "发送方:"); //"MAIL FROM: "占11字符,i从11开始 for(i = 11; i < hlf->count_new; i++) { fprintf(fp, "%s", char_to_ascii(content[i])); } } if(strstr(content, "RCPT TO")) { fprintf(fp, "接收方:"); //"RCPT TO: "占9字符,i从9开始 for(i = 9; i < hlf->count_new; i++) { fprintf(fp, "%s", char_to_ascii(content[i])); } }*/ if(strstr(content, "DATA")) //判断是否有可发邮件 { sendmail = true; //在连接关闭的时候要把该变量置为false } //在DATA中提取需要信息 if(hlf->count_new > 0) { //提取From char *pfrom; pfrom = strstr(content, "From: "); if(pfrom) //如果找到了From: { /*是否是汉字GB2312编码,如果是则分离出=?gb2312?B?到?=之间的字符 *发件人是这样的格式"=?gb2312?B?0cfD9w==?= <[email protected]>" *不仅要提取出汉字,还要把后边的邮件地址输出 */ strcpy(content_temp, pfrom); fprintf(fp, "发件人:t"); if(content_temp[6] == 61) //如果"From: "后是"=" { //strcpy(content_temp, pGB2312); char gbstring[1000]; i = 17; //只捕获=?gb2312?B?后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 while(!(content_temp[i] == 63 && content_temp[i+1] == 61)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1000];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } } else { fprintf(fp, "解码失败!"); }//end if //接下来输出<>之间的地址,ASSCII码"<"为60,">"为62 j = i; j+= 2; //跳到"<"位置 while(!(content_temp[j-1] == 62)) { fprintf(fp, "%s", char_to_ascii(content_temp[j])); j++; } } else { //strcpy(content_temp, pfrom); i = 6; //"From: "后边开始输出 while(!(content_temp[i-1]==62 && content_temp[i]==13 && content_temp[i+1]==10)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } }//end if pGB2312 fprintf(fp, "n"); }//end if pfrom pfrom=NULL; free(pfrom); //释放指针 //提取To char *pto; pto = strstr(content, "To: "); if(pto) { strcpy(content_temp, pto); fprintf(fp, "收件人:t"); if(content_temp[4] == 61) //如果"To: "后是"=" { char gbstring[1000]; i = 15; //只捕获=?gb2312?B?后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 while(!(content_temp[i] == 63 && content_temp[i+1] == 61)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1000];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } } else { fprintf(fp, "解码失败!"); }//end if //接下来输出<>之间的地址,ASSCII码"<"为60,">"为62,","为44 j = i; j+= 2; //跳到"<"位置 //while(!(content_temp[j-1] == 62)) while(!(content_temp[j-1]==62 && content_temp[j]==13 && content_temp[j+1]==10)) { fprintf(fp, "%s", char_to_ascii(content_temp[j])); j++; } } else { i = 4; //"To: "后边开始输出 while(!(content_temp[i-1]==62 && content_temp[i]==13 && content_temp[i+1]==10)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } }//end if pGB2312 fprintf(fp, "n"); }//end if pto pto=NULL; free(pto); //提取Cc char *pCc; pCc = strstr(content, "Cc: "); if(pCc) { strcpy(content_temp, pCc); fprintf(fp, "抄送人:t"); if(content_temp[4] == 61) //如果"Cc: "后是"=" { char gbstring[1000]; i = 15; //只捕获=?gb2312?B?后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 while(!(content_temp[i] == 63 && content_temp[i+1] == 61)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1000];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } } else { fprintf(fp, "解码失败!"); }//end if //接下来输出<>之间的地址,ASSCII码"<"为60,">"为62 j = i; j+= 2; //跳到"<"位置 while(!(content_temp[j-1]==62 && content_temp[j]==13 && content_temp[j+1]==10)) { fprintf(fp, "%s", char_to_ascii(content_temp[j])); j++; } } else { i = 4; //"Cc: "后边开始输出 while(!(content_temp[i-1]==62 && content_temp[i]==13 && content_temp[i+1]==10)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } }//end if pGB2312 fprintf(fp, "n"); }//end if pCc pCc=NULL; free(pCc); //提取Subject char *psub; psub = strstr(content, "Subject: "); if(psub) { strcpy(content_temp, psub); //是否是汉字GB2312编码,如果是则分离出=?gb2312?B?到?=之间的字符 fprintf(fp, "主题:t"); if(content_temp[9] == 61) //如果"Subject: "后是"=" { char gbstring[1000]; i = 20; //只捕获=?gb2312?B?后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 while(!(content_temp[i] == 63 && content_temp[i+1] == 61)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1000];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } } else { fprintf(fp, "解码失败!"); }//end if } else { i = 9; //输出"Subject: "后的字符 while(!(content_temp[i] == 10 || content_temp[i] == 13)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } }//end if pGB2312 fprintf(fp, "n"); }//end if psub psub=NULL; free(psub); //提取Date char *pdate; pdate = strstr(content, "Date: "); if(pdate) { strcpy(content_temp, pdate); i = 6; //截取"Date: "后的字符 //fprintf(fp, "日期:t"); while(!(content_temp[i] == 13 && content_temp[i+1] == 10)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } fprintf(fp, "n"); } pdate=NULL; free(pdate); //提取内容 char *pcontent; pcontent = strstr(content, "Content-Type: text/plain;"); if(pcontent && strstr(content, "Return-path:")) { strcpy(content_gb, pcontent); char *pbase; pbase = strstr(content_gb, "base64"); //fprintf(fp, "邮件内容:n"); if(pbase) { fprintf(fp, "邮件内容:n"); strcpy(content_temp, pbase); //当内容全部在一个包中时 if(strstr(content_temp, "=")) { //内容有可能为空,判定条件是"base64rn"后如果是"rn-" /*//测试 for(int m=8; m<20; m++) { printf("%s", char_to_ascii(content_temp[m])); } //测试*/ if(content_temp[10]==13 && content_temp[11]==10) { fprintf(fp, "t内容为空."); } else { char gbstring[1600]; i = 10; //只捕获base64rnrn后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 //以该标志结束"rnrn------=","-"的Ascii为45 while(!(content_temp[i+2]==13 && content_temp[i+3]==10 && content_temp[i+4] == 45)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1600];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } /*//测试 printf("长度%d字节.n", len); //测试*/ } else { fprintf(fp, "解码失败!"); }//end if*/ } } else { fprintf(fp, "暂时无法显示."); } }//end if(pGB2312) fprintf(fp, "n"); pbase = NULL; free(pbase); }//end if(pcontent) pcontent = NULL; free(pcontent); //提取附件名字 if(strstr(content, "Content-Disposition: attachment")) //Content-Disposition: attachment { char *pattach; pattach = strstr(content, "filename="); if(pattach) { //是否是汉字GB2312编码,如果是则分离出=?gb2312?B?到?=之间的字符 char *pGB2312; pGB2312 = strstr(content, "=?gb2312?B?"); if(pGB2312) //含有汉字编码后的字符 { strcpy(content_temp, pGB2312); char gbstring[1000]; i = 11; //只捕获=?gb2312?B?后边的字符 int j = 0; //用来记录循环次数,即截取的字符串长度 fprintf(fp, "附件:t"); while(!(content_temp[i] == 63 && content_temp[i+1] == 61)) { gbstring[j] = content_temp[i]; i++; j++; } gbstring[j] = ''; unsigned long len; string InBase = gbstring; unsigned char OutBase[1000];//存放解密后字符串 if(CBase64::Decode(InBase, OutBase, &len)) { for(int k=0; k<len; k++) { fprintf(fp, "%c", OutBase[k]); } } else { fprintf(fp, "解密失败!"); }//end if } else //否则直接输出英文字符串 { strcpy(content_temp, pattach); i = 9; //输出filename=后的字符 fprintf(fp, "附件:t"); while(!(content_temp[i] == 10 || content_temp[i] ==13)) { fprintf(fp, "%s", char_to_ascii(content_temp[i])); i++; } }//end if pGB2312 fprintf(fp, "n"); pGB2312 = NULL; free(pGB2312); }//end if pattach pattach = NULL; free(pattach); }//end if attachment } fclose(fp); return; } } default: fclose(fp); break; }//end switch return; } //////////////主函数//////////////////// void main() { nids_params.device = "2"; //手选网络适配器,因为默认的是虚拟网卡 if(!nids_init()) //Libnids初始化 { // printf("出现错误:%sn", nids_errbuf); exit(1); } nids_register_tcp(smtp_protocol_callback); //注册分析SMTP协议的回调函数 nids_run(); //进入循环捕获数据包的状态 }