这一篇仅对smtp协议解析的思路做整体描述,所有内容均是按照自己理解所写,肯定有理解不正确的地方,请指出,谢谢。

一。Suricata解析smtp协议整体思路

Smtp协议解析模块根据客户端与邮件服务器之间的每一个smtp交互命令,设置了多个状态即当前的smtp命令和解析阶段。Smtp协议解析模块将smtp的交互过程视为一次事务,该事务记录了状态变化和解析阶段的过程,每次状态变化时,根据数据传输方向(客户到服务器,服务器到客户),根据当前方向对当前状态进行相应的处理。

例如:当客户端发送HELO命令后,事务的状态变为HELO,此时,服务器的操作就是处理HELO后反馈数据给客户端,客户端的操作就是解析服务器对HELO命令反馈的数据。

解析方式:smtp解析模块是以行为单位解析,传入处理函数的缓冲区是\r\n前的数据,不包含\r\n,长度也不包含\r\n.

Smtp解析模块对收到的数据查找\r\n,如果未找到则对数据缓存,知道找到\r\n再进行解析处理,如果数据中包括多行即多个\r\n,则每次处理一行后再根据\r\n的位置进行数据偏移处理下一行数据。

源文件:

src/app-layer-smtp.c

src/util-decode-mime.c

二。Suricata解析smtp头部数据,信件头部字段比较简单

到服务器方向的数据处理:SMTPParse->SMTPGetLine:SMTPProcessRequest

SMTPGetLine:

这个函数功能主要是在收到的数据中,查找行分隔符即\r\n,然后将mime_state->current_line指针指向这一行,并设置长度mime_state->current_line_len的长度,注意,这里的currnet_line和current_line_len不包含\r\n两个字符,如果没有找到\r\n则缓存这行数据,下一次继续同样的处理。

SMTPProcessRequest:

  1. SMTPGetLine设置好current_line和长度后,在该解析处理每行数据,主要是比较数据头部几个字节,根据smtp命令长度判断smtp命令。
  2. 如果找到smtp命令,则解析命令并提取命令后的参数(如果有)。
  3. 将命令和参数保持到smtp事务结构体中,并设置当前解析命令、解析状态,例如遇到data命令,则设置mime_state->current_command=SMTP_COMMAND_DATA,设置mime_state->parser_state |= SMTP_PARSER_STATE_COMMAND_DATA_MODE。
  4. smtp命令保存到了事务结构体的一个命令数组中,客户端收到服务器的响应时,根据这个命令数组解析其的对应的响应数据的格式。

到客户端方向的数据处理:SMTPParse->SMTPGetLine:SMTPProcessReply

SMTPGetLine的功能同上。

SMTPProcessReply:

  1. 解析服务器返回的每行数据。
  2. 主要工作就是根据事务结构体中命令数组的当前命令和索引,判断当前命令应该得到什么样的响应码,根据响应码做相应处理,设置解析状态。

三。Suricata解析smtp数据命令data的头部字段

解析data命令后的数据中的MIME头部字段:

SMTPParse-> SMTPProcessRequest-> SMTPProcessCommandDATA-> MimeDecParseLine-> ProcessMimeEntity-> ProcessMimeHeaders

SMTPProcessCommandDATA:

主要功能:对data命令后的数据解析工作,先判断命令是否是点“.”,如果是说明数据接收完毕,客户端不再发送数据,则设置解析状态,和解析标志tx->done=1。如果不是“.”,则调用MimeDecParseLine函数,完成数据行的解析。

MimeDecParseLine:

就是个包裹函数,啥都没做,又调用了函数ProcessMimeEntity,进行实际的信件体解析工作。

ProcessMimeEntity:

完成三部分工作:

a。首先判断数据长度合法性。

b。判断解析标志如果是HEADER_READY或者HEADER_STARTED,则调用函数ProcessMimeHeaders解析信件体的头部字段。第一次解析初始化mime_state的内存时会设置成HEADER_STARTED,在解析过程中如果数据是bouddary分界线则设置成HEADER_READY,因为boundary分界线的后续数据就是信件体的头部字段,准备解析头部字段。

c。不是上述标志,则调用函数ProcessMimeBody解析信件体的body数据。

ProcessMimeHeaders:

这个函数调用函数FindMimeHeader查找头部的name和value:

a。根据冒号查找头部字段name和value,如content-type等等。

b。如果找到name,如果是第一次解析name,则分配内存,保存name和value。

c。如果找到name,如果不是第一次,在说明上次的name的value的数据接收完成,调用函数StoreMimeHeader保存上一次的name和value,并为本次的name和value分配内存并存储。

d。如果数据长度为0,说明遇到\r\n,表示头部数据结束,设置解析标志HEADER_DONE,后续数据到达时根据这个标志会进入body数据解析函数。

e。如果没有找到冒号,且解析标志是HEADER_STARTED(表示当前已进入头部字段解析阶段,后续数据和头部有关,要么是name,要么是value或者value的一部分),则认为这个数据是上次的name的value的一部分(因为value如果比较长,可能会多行传输),则分配内存节点,将该部分数据作为一个数据片段存储到mime_state->hvalue链表中,头部解析完成会合并到一起并存储肉不字段链表中,这个hvalue链表是临时链表,临时存放头部字段name的value的多个数据片段。

f。如果没有找到冒号,且解析标志不是HEADER_STARTED,说明数据不是头部字段,则认为是没有头部字段的body数据,因此调用函数ProcessBodyLine解析body数据,并设置解析标志为BODY_STARTED,后续数据到达后根据这个标志进入body数据解析函数,并且设置变量mime_state->body_begin = 1,表示后续的数据就是body数据了,后续到达数据进入body数据解析函数。之所以判断HEADER_STARTED标志,是因为只有设置了该标志才表示后续部分是头部字段,一般来讲,头部字段后是body数据,这里没有发现头部信息则认为是body数据,于是进行body数据处理。

g。完成上述工作后,如果头部字段解析完成(判断标志HEADER_DONE),则进入分析头字段,例如分析content-type,contenet-transfer-code,以及值,信件体类型,编码,有没有附件等等,根据这个些设置编码标志位,在解析body数据时根据这个编码标志进行解码,设置mime_state->body_begin = 1,后续进入body数据解析函数。

头部解析至此结束。

四。Suricata解析smtp数据命令data的body数据

解析data命令后的数据中的MIME的body数据:

SMTPParse-> SMTPProcessRequest-> SMTPProcessCommandDATA-> MimeDecParseLine-> ProcessMimeEntity-> ProcessMimeBody

SMTPProcessCommandDATA:

主要功能:对data命令后的数据解析工作,先判断命令是否是点“.”,如果是说明数据接收完毕,客户端不再发送数据,则设置解析状态,和解析标志tx->done=1。如果不是“.”,则调用MimeDecParseLine函数,完成数据行的解析。

MimeDecParseLine:

就是个包裹函数,啥都没做,又调用了函数ProcessMimeEntity,进行实际的信件体解析工作。

ProcessMimeEntity:

完成三部分工作:

a。首先判断数据长度合法性。

b。判断解析标志如果是HEADER_READY或者HEADER_STARTED,则调用函数ProcessMimeHeaders解析信件体的头部字段。第一次解析初始化mime_state的内存时会设置成HEADER_STARTED,在解析过程中如果数据是bouddary分界线则设置成HEADER_READY,因为boundary分界线的后续数据就是信件体的头部字段,准备解析头部字段。

c。不是上述标志,则调用函数ProcessMimeBody解析信件体的body数据。

ProcessMimeBody:这个函数比较重要

解析body数据的总体思路是判断正在解析的行数据是否是boundary分界线,以及结尾boundary分界线,然后根据判断结果或生成信件体结构体链入其父信件或者子信件体、又或将body数据存入mime_state中,根据mime_state->stack->top指针进行操作。

a。进行一些合法性判断。

b。如果一个完整的信件体解析结束(上次解析数据时通过结尾boun

dary判断后设置的解析标志BODY_END_BOUND),则本次的数据属于上一级(当前信件体的父信件体),于是确定本次数据应该使用哪个boundary,其实应该使用其父信件体定义的boundary,父信件体的content-type中包含了这个,所以代码判断其父信件体(top->next)是否包含boundary,包含则使用这个boundary,如果不包含再查找父信件体的父信件体,直到找到boundary,如果最后没有找到boundary则说明当前信件体后续没有子信件体了,后续数据是普通的body数据,不需要根据boundary生成子信件体等操作,直接将body数据存储到当前信件体的结构体中即可。

c。如果找到boundary分界线,调用函数ProcessMimeBoundary进一步处理boundary分界线,如果没有找到boundary分界线,则认为这个数据是body数据,于是设置局部变量body_found=1,函数后边的代码根据这个局部变量调用body数据处理函数处理body数据。

d。调用函数ProcessMimeBoundary处理boundary分界线的过程:

进入本函数,首先说明本次数据是一个doundary分界线,后续会判断是不是结尾doundary分界线,也会根据各种情况生成新子信件体,或者兄弟信件体,或者切换到父信件体。

(1)如果解析标志不是BODY_END_BOUND,则对当前信件体作body数据结束处理,就是一个完整的信件体结束了,为什么呢,因为每个子信件体是以boundary分界线分割的,每遇到一个boundary分界线说明遇到一个新的信件体,那么新的信件体开始了,上一个信件体也就结束了,所以如此判断。这个标志是在遇到结尾boundary分界线时设置的,只有遇到结尾boundary分界线时才会设置。

(2)如果标志是BODY_END_DOUND,说明上次的数据是结尾boundary分界线,则认为这个信件体的所有子信件体结束并且解析完成,本次数据是一个子信件体的boundary分界线(非结尾boundary分界线),那么下次的数据肯定是下个子信件体的头部字段,于是设置标志HEADER_REDAY,后续数据到达时进入头部字段解析函数。

(3)如果本次数据是结尾boundary分界线,意味着一个信件体的所有子信件体结束了并且解析完成,于是调用函数popstack将mime_state->stack-top指向其父信件体结构体指针,这个堆栈的next指针指向的是其父信件体的结构体指针,并这只解析标志BODY_END_BOUND,这个意思就是遇到结尾boundary分界线了,这个信件体的所有子信件体就解析完成了,后续数据应该存储到其父信件体了。

(4)如果本次数据是doundary分界线,如果mime_state->found_child为1,则认为后续数据是一个子信件体,因为这个变量是在解析头部字段完成后,分析content-type时,找到boundary时设置的,找到boundary说明本信件体包含一个或多个子信件体;于是此处就生成一个信件体结构体并链入其父信件体的child链表中,并将mime_state->stack->top->data指向它,后续数据会存储到这里。

(5)如果本次数据不是结尾boundary分界线,mime_state->child_found不为1,这种情况下,遇到boundary分界线,说明这个分界线后续的信件体是上个信件体的兄弟信件体,于是找出父信件体,生成新信件体,将新信件体链入父信件体的child链表中。

(6)最后判断上述逻辑对解析标志的设置,如果不是结尾boundary分界线(判断解析标志不是BODY_END_BOUND),则设置解析标志为HEADER_REDAY,因为遇到的是boundary分界线,而不是结尾doundary分界线,说明后续数据是头部字段,后续根据这个标志进入头部字段解析函数。

e。处理完分界线,即调用函数ProcessMimeBoundary处理boundary分界线后,根据局部变量body_found判断,如果为1表示本次数据为body数据,设置解析标志为BODY_STARTED,然后调用ProcessBodyLine函数处理body数据。

f。ProcessBodyLine函数处理

(1)根据头部字段设置编码标志对数据进行解码,base64,quote-prntable。

(2)如果数据未编码则不需要解码。

(3)以上两步后:

将数据存储到mime_state->data_chunk中,如果没有存储空间则调用ProcessDecodedDataChunk函数对数据进行处理,其中主要是获取每行数据从中提取url并保存,之后并调用函数指针state->DataChunkProcessorFunc,该函数指针是在函数SMTPProcessRequest中创建(MimeDecParseState)mime_state时进行的初始化,提供自定义处理功能。

之后将state->data_chunk_len置0,下次数据从头存入。

初始化的自定义函数为SMTPProcessDataChunk,实现对信件实体数据块的处理,suricate提供的这个函数功能主要完成了附件操作,即附件作为文件的存储功能。

suricata smtp协议解析源码注释一相关推荐

  1. Modbus通信协议+Modbus串口调试工具+Java版协议解析源码

    网络与串口二合一调试助手TCPCOM: https://download.csdn.net/download/liuyuan_java/87454762 Modbus调试工具,模拟串口调试工具 htt ...

  2. WebBench压力测试工具(详细源码注释+分析)

    WebBench压力测试工具(详细源码注释+分析) 本文适合人群:对WebBench实现感兴趣的人 WebBench原理: Linux下使用的服务器压力测试工具,利用fork建立多个子进程,每个子进程 ...

  3. Flink 源码解析 —— 源码编译运行

    更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...

  4. 红黑树原理浅谈(附Linux内核源码注释)

    引言:红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组.它是在1972年由鲁道夫·贝尔发明的,他称之为"对称二 ...

  5. 基于stm32、0.96寸OLED实现的贪吃蛇小游戏(详细源码注释)

    简介:本实验基于stm32最小系统.0.96寸OLED(68*128)和摇杆实现一个经典的贪吃蛇小游戏.项目源码地址:点击下载. 硬件设计: 普通摇杆,0.96寸OLED 单色屏幕(SPI协议通讯), ...

  6. 基于stm32、0.96寸OLED实现的俄罗斯方块小游戏(详细源码注释)

    概述:本实验基于stm32最小系统.0.96寸OLED(68*128)和摇杆实现一个经典的俄罗斯方块小游戏.项目源码地址:点击下载. 硬件要求: 普通摇杆,两个电位器和一个开关组成,左右摇动控制一个电 ...

  7. Dubbo协议模块源码剖析

    Dubbo协议模块源码剖析 目录 概 述 RPC协议报文编码与实现详解 RPC 传输实现: 拆包与粘包解决办法: Dubbo 报文格式 分析: 小结: 参考资料和推荐阅读 LD is tigger f ...

  8. 【机器学习】word2vec学习笔记(三):word2vec源码注释

    1. word2vec地址 官网地址:https://code.google.com/archive/p/word2vec/ GitHub地址:https://github.com/tmikolov/ ...

  9. php文件直链源码,蓝奏网盘文件夹直链解析源码

    蓝奏网盘文件夹直链解析源码 @晶晶易.版本 2 .支持库 spec .程序集 程序集1 .子程序 _启动子程序, 整数型, , 本子程序在程序启动后最先执行 .局部变量 返回文本, 文本型 .局部变量 ...

最新文章

  1. IP Precedence DSCP、TOS
  2. rtsp连接断开_live555学习之RTSP连接建立以及请求消息处理过程
  3. html得到画布的颜色的值,从画布上获取像素颜色
  4. jquery 删除字符串最后一个字符的方法
  5. TreeMap实现排序
  6. 数学老师出的语文试卷,哈哈哈
  7. 程序员说的demo是什么意思_“黄龄说的什么意思”???
  8. 当程序员写不出代码了,该怎么办?
  9. vscode还用装git_在windows下搭建编程环境git+vscode安装配置教程
  10. JSP 获取Request 经常使用參数
  11. Apizza--特别好用的 Http请求模拟工具 PHP写接口然后测试详细教程
  12. 2021几款好用的思维导图软件推荐!
  13. WPS 关闭热点推送通知
  14. 电脑蓝屏,问题:你的电脑未正确启动,按“重启”以重启你的电脑,有时这样可以解决问题,你还可以按“高级选项”,尝试使用其他选项修复你的电脑
  15. 【python】详解pandas库的pd.merge_ordered与pd.merge_asof
  16. 使用Mailgun WordPress插件增加订户
  17. 手机java系统说明什么_Android的手机系统是开源的,开源到底是什么意思?
  18. linux安装globalsign证书,GlobalSign 普通 OV 代码签名证书提取教程
  19. mysql进阶教程pdf_Mysql基础到进阶精品视频教程附讲义文档 91课
  20. 如何防止恶意攻击短信验证码接口

热门文章

  1. cmake-gui使用教程
  2. redolog 、undolog 和binlog
  3. 计算机考研调剂专业课,一波七折的计算机考研初试调剂经验教训贴,别放弃,太阳还在...
  4. OPT机器视觉12月高峰论坛一览表
  5. GetKeyState
  6. UML图之五——时序图
  7. Wifi无法自动连接的问题
  8. Backtrack5安装
  9. b站选择HTML5播放器,b站用什么播放器看片好?选择硬解还是软解?看本地视频首选哪一个?...
  10. Yii中CGridView单元格组件和数据提供者的使用