Packetbeat协议扩展开发教程(3)

前面介绍了Packetbeat的项目结构,今天终于要开始写代码了,想想还是有点小激动呢。(你快点吧,拖半天了)
网络传输两大协议TCP和UDP,我们的所有协议都不离这两种,HTTP、MySQL走的是TCP传输协议,DNS走的是UDP协议,在Packetbeat里面,实现一个自己的协议非常简单,继承并实现这两者对应的接口就行了,我们看一下长什么样:
打开一个现有的UDP和HTTP协议接口定义:

/~/Go/src/github.com/elastic/beats/packetbeat/protos/protos.go

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. // Functions to be exported by a protocol plugin  
  2. type ProtocolPlugin interface {  
  3.     // Called to initialize the Plugin  
  4.     Init(test_mode bool, results publisher.Client) error  
  5.    
  6.     // Called to return the configured ports  
  7.     GetPorts() []int  
  8. }  
  9.    
  10. type TcpProtocolPlugin interface {  
  11.     ProtocolPlugin  
  12.    
  13.     // Called when TCP payload data is available for parsing.  
  14.     Parse(pkt *Packet, tcptuple *common.TcpTuple,  
  15.         dir uint8, private ProtocolData) ProtocolData  
  16.    
  17.     // Called when the FIN flag is seen in the TCP stream.  
  18.     ReceivedFin(tcptuple *common.TcpTuple, dir uint8,  
  19.         private ProtocolData) ProtocolData  
  20.    
  21.     // Called when a packets are missing from the tcp  
  22.     // stream.  
  23.     GapInStream(tcptuple *common.TcpTuple, dir uint8, nbytes int,  
  24.         private ProtocolData) (priv ProtocolData, drop bool)  
  25.    
  26.     // ConnectionTimeout returns the per stream connection timeout.  
  27.     // Return <=0 to set default tcp module transaction timeout.  
  28.     ConnectionTimeout() time.Duration  
  29. }  
  30.    
  31. type UdpProtocolPlugin interface {  
  32.     ProtocolPlugin  
  33.    
  34.     // ParseUdp is invoked when UDP payload data is available for parsing.  
  35.     ParseUdp(pkt *Packet)  
  36. }  


TcpProtocolPlugin:TCP协议插件的接口定义,依次是:Parse() 解析Packet,ReceivedFin()处理TCP断开连接,GapInStream()处理空包丢包,ConnectionTimeout()超时时间;

UdpProtocolPlugin: UDP协议的接口定义,UDP协议是不需要握手和保障数据可靠性的,扔出去就结束,速度快,不保证数据可靠送达,所以只有ParseUdp一个方法需要实现,比较简单;

ProtocolPlugin:TCP和UDP都需要实现ProtocolPlugin的基础接口,其实就定义了获取端口和初始化接口。

请问:Packetbeat怎么工作的?

回答:每一个协议都有一个固定的端口用于通信,你要做的事情就是定义协议端口,然后按协议是TCP还是UDP来实现对应的接口,Packetbeat将会截获指定端口的数据包(Packet),然后如果交给你定义的方法来进行解析,TCP是Parse,UDP是ParseUdp,都在上面的接口定义好的,然后将解析出来的结构化数据封装成Json,然后扔给Elasticsearch,后续的就的如何对这些数据做一些有趣的分析和应用了。

貌似很简单嘛!

进入每个端口的数据包,我们假设是一个自来水管,拧开80端口,哗啦啦出来的全是HTTP请求的数据包,Packetbeat里面Http协议监听的是80端口啊,所有这些包统统都交给Packetbeat里面的Http协议模块来进行解析,Http协议会一个个的检查这些数据包,也就是每个数据包都会调用一次Parse接口,到这里提到了传过来一个Packet,我们看看它的数据结构长什么样?

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. type Packet struct {  
  2.     Ts      time.Time  
  3.     Tuple   common.IpPortTuple  
  4.     Payload []byte  
  5. }  


Packet结构简单,
Ts是收到数据包的时间戳;
Tuple是一个来源IP+来源端口和目的IP+目的端口的元组;
Payload就是这个包里面的传输的有用的数据,应用层的字节数据,不包括IP和TCP/UDP头信息,是不是处理起来简单许多。
首选我们确定SMTP协议的配置,每个协议在packetbeat.yml的protocol下面都应该有一个配置节点,如下:

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. protocols:  
  2.   smtp:  
  3.     # Configure the ports where to listen for Smtp traffic. You can disable  
  4.     # the Smtp protocol by commenting out the list of ports.  
  5.     ports: [25]  


还需要在对应的config类文件:packetbeat/config/config.go,增加SMTP的结构体,目前只支持一个端口参数,继承基类ProtocolCommon就行,如下:

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. git diff config/config.go  
  2. @@ -42,6 +42,7 @@ type Protocols struct {  
  3.         Pgsql    Pgsql  
  4.         Redis    Redis  
  5.         Thrift   Thrift  
  6. +       Smtp     Smtp  
  7.  }  
  8.    
  9.  type Dns struct {  
  10. @@ -118,5 +119,9 @@ type Redis struct {  
  11.         Send_response *bool  
  12.  }  
  13.    
  14. +type Smtp struct {  
  15. +   ProtocolCommon        `yaml:",inline"`  
  16. +}  
  17. +  
  18.  // Config Singleton  
  19.  var ConfigSingleton Config  


在protos文件夹下面,新增smtp目录,并新增空白文件smtp.go,路径:packetbeat/protos/smtp/smtp.go,这里就是解析SMTP协议的地方,也是我们扩展协议的主要的工作。

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ...TODO...  


修改protos/protos.go,增加SMTP协议枚举,这里记得保证顺序一致,并且protocol名称必须和配置的节点名称一致,如这里都是smtp。

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. git diff protos/protos.go  
  2. @@ -103,6 +103,7 @@ const (  
  3.         MongodbProtocol  
  4.         DnsProtocol  
  5.         MemcacheProtocol  
  6. +       SmtpProtocol  
  7.  )  
  8.    
  9.  // Protocol names  
  10. @@ -116,6 +117,7 @@ var ProtocolNames = []string{  
  11.         "mongodb",  
  12.         "dns",  
  13.         "memcache",  
  14. +       "smtp",  
  15.  }  


继续修改packetbeat.go主文件,允许SMTP协议并加载。

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. git diff packetbeat.go  
  2. @@ -27,6 +27,7 @@ import (  
  3.         "github.com/elastic/packetbeat/protos/tcp"  
  4.         "github.com/elastic/packetbeat/protos/thrift"  
  5.         "github.com/elastic/packetbeat/protos/udp"  
  6. +       "github.com/elastic/packetbeat/protos/smtp"  
  7.         "github.com/elastic/packetbeat/sniffer"  
  8.  )  
  9.    
  10. @@ -43,6 +44,7 @@ var EnabledProtocolPlugins map[protos.Protocol]protos.ProtocolPlugin = map[proto  
  11.         protos.ThriftProtocol:   new(thrift.Thrift),  
  12.         protos.MongodbProtocol:  new(mongodb.Mongodb),  
  13.         protos.DnsProtocol:      new(dns.Dns),  
  14. +       protos.SmtpProtocol:      new(smtp.Smtp),  
  15.  }  


做完上面一系列修改之后,一个空白的SMTP协议的插件的架子就搭好了,并且插件也注册到了Packetbeat里面了,接下来我们再把packetbeat/protos/smtp/smtp.go按照TCPplugin接口的要求实现一下。

说实话TCP处理起来很难,开始之前,我们先明确几个概念,TCP协议是有状态的,并且是流式的,我们关注的是七层应用层的消息,如HTTP里面的一个HTTP请求和返回,但是TCP底层都是一系列数据包,并且不同的请求的数据包是混杂在一起的,也就是说一个数据包里面可能只是一个HTTP请求的一部分也可能包含多条HTTP请求的一部分,所以Parse()里面需要处理跨数据包的状态信息,我们要把这些数据包和具体的七层的应用层的消息关联起来。

现在我们仔细看看Parse()接口的各个参数定义是做什么用的

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. Parse(pkt *Packet, tcptuple *common.TcpTuple,  
  2.         dir uint8, private ProtocolData) ProtocolData  


pkt不用说了,是送进来的数据包,前面已经介绍了其数据结构,tcptuple是该数据包所属的TCP数据流所在的唯一标示(一个未关闭的TCP数据量包含若干数据包,直到TCP链接关闭),使用tcptuple.Hashable()获取唯一值;dir参数标示数据包在TCP数据流中的流向,和第一个TCP数据包方向一致是TcpDirectionOriginal,否则是TcpDirectionReverse;private参数可用来在TCP流中存储状态信息,可在运行时转换成具体的强类型,任意修改和传递给下一个Parse方法,简单来说就是进行中间数据的共享。

下面看段MySQL模块里面的例子

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. priv := mysqlPrivateData{}  
  2.         if private != nil {  
  3.                 var ok bool  
  4.                 priv, ok = private.(mysqlPrivateData)  
  5.                 if !ok {  
  6.                         priv = mysqlPrivateData{}  
  7.                 }  
  8.         }  
  9.    
  10.         [ ... ]  
  11.    
  12.         return priv  


上面的代码就是将private强制转换成mysqlPrivateData结构,然后再使用。我们再继续看后续怎么处理这些包的一个逻辑例子

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ok, complete := mysqlMessageParser(priv.Data[dir])  
  2.                 if !ok {  
  3.                         // drop this tcp stream. Will retry parsing with the next  
  4.                         // segment in it  
  5.                         priv.Data[dir] = nil  
  6.                         logp.Debug("mysql", "Ignore MySQL message. Drop tcp stream.")  
  7.                         return priv  
  8.                 }  
  9.    
  10.                 if complete {  
  11.                         mysql.messageComplete(tcptuple, dir, stream)  
  12.                 } else {  
  13.                         // wait for more data  
  14.                         break  
  15.                 }  


mysqlMessageParser是一个解析mysql消息的方法,细节我们忽略,我们只需要关心它的返回,ok标示成功或者失败,true则继续处理,false表示数据包不能用,那就直接忽略;第二个参数complete表示判断这一个MySQL消息是否已经完整了,如果完整了,我们就可以扔出去了,否则继续等待剩下的消息内容。

好的,我们看看SMTP协议怎么折腾吧,先看看一个邮件交互的流程图,来自 RFC5321 (https://tools.ietf.org/html/rfc5321):

 

由上图可见,发送端和邮件服务器通过一系列命令来执行邮件的发送,下面看看一个具体的命令操作流程(来源: https://zh.wikipedia.org/wiki/简单邮件传输协议 )

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. S: 220 www.example.com ESMTP Postfix  
  2. C: HELO mydomain.com  
  3. S: 250 Hello mydomain.com  
  4. C: MAIL FROM:     
  5.      
  6.    
  7. S: 250 Ok  
  8. C: RCPT TO:      
  9.       
  10.     
  11. S: 250 Ok  
  12. C: DATA  
  13. S: 354 End data with   
  14.     
  15.         
  16. C: Subject: test message  
  17. C: From:""sender@mydomain.com>  
  18. C: To:""friend@example.com>  
  19. C:  
  20. C: Hello,  
  21. C: This is a test.  
  22. C: Goodbye.  
  23. C: .  
  24. S: 250 Ok: queued as 12345  
  25. C: quit  
  26. S: 221 Bye  


上面的过程可以看到就几个命令就能将邮件发送出去,但是其实SMTP协议比较复杂,还包括身份认证、附件、多媒体编码等等,我们今天精简一下,我们目前只关心谁给谁发了邮件,发送内容先不管,这样相比完整的SMTP协议( RFC5321 ),我们只需要关注以下几个命令:

MAIL:开始一份邮件 mail from: xxx@xx.com

RCPT: 标识单个的邮件接收人;常在mail命令后面 可有多个rcpt to: xx@xx.com

QUIT:结束SMTP会话,不一定发送了邮件,注意

RESET:重置会话,当前传输被取消

最终希望通过Packetbeat将这些数据解析并处理成我们想要的如下JSON数据,即大功告成:

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. {  
  2. "timestamp":"2016-1-15 12:00:00",  
  3. "from":"medcl@example.co",  
  4. "to":["lcdem@example.co"]  
  5. }  


我们还需要一个测试数据,这里有一个下载各种协议测试数据包的地方,由wireshark站点提供: https://wiki.wireshark.org/SampleCaptures/

Ctrl+F找到SMTP的 下载地址(https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=smtp.pcap)

用wireshark打开我们刚刚下载的smtp.pcap文件,然后再输入过滤条件:tcp.port == 25,只看25端口的数据,如下图:

 

上图可以看到25端口的跑的数据有很多,不过我们只关心我们需要的那几个命令就好了。

打开/~/go/src/github.com/elastic/beats/packetbeat/protos/smtp/smtp.go定义smtpPrivateData,里面的Data是一个数组,分别是TCP两个方向的数据,SmtpMessage是解析出来的邮件信息

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. type smtpPrivateData struct{  
  2.     Data [2]*SmtpStream  
  3. }  
  4.    
  5. type SmtpStream struct {  
  6.     tcptuple *common.TcpTuple  
  7.    
  8.     data []byte  
  9.    
  10.     parseOffset int  
  11.     isClient    bool  
  12.     message *SmtpMessage  
  13. }  
  14.    
  15. type SmtpMessage struct {  
  16.     Ts   time.Time  
  17.     From string  
  18.     To []string  
  19. }  


然后参照MySQL协议,定义相应的方法,最终如下:

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package smtp  
  2.    
  3. import (  
  4.     "github.com/elastic/beats/libbeat/common"  
  5.     "github.com/elastic/beats/libbeat/logp"  
  6.     "github.com/elastic/beats/libbeat/publisher"  
  7.     "github.com/elastic/beats/packetbeat/config"  
  8.     "github.com/elastic/beats/packetbeat/protos"  
  9.     "github.com/elastic/beats/packetbeat/protos/tcp"  
  10.     "bytes"  
  11.     "time"  
  12.     "strings"  
  13. )  
  14.    
  15. type smtpPrivateData struct{  
  16.     Data [2]*SmtpStream  
  17. }  
  18.    
  19. type SmtpStream struct {  
  20.     tcptuple *common.TcpTuple  
  21.    
  22.     data []byte  
  23.    
  24.     parseOffset int  
  25.     isClient    bool  
  26.    
  27.     message *SmtpMessage  
  28. }  
  29.    
  30. type SmtpMessage struct {  
  31.     start int  
  32.     end   int  
  33.    
  34.     Ts   time.Time  
  35.     From string  
  36.     To []string  
  37.     IgnoreMessage bool  
  38. }  
  39.    
  40. type Smtp struct {  
  41.     SendRequest         bool  
  42.     SendResponse        bool  
  43.     transactionTimeout time.Duration  
  44.     Ports         []int  
  45.     results publisher.Client  
  46. }  
  47.    
  48. func (smtp *Smtp) initDefaults() {  
  49.     smtp.SendRequest = false  
  50.     smtp.SendResponse = false  
  51.     smtp.transactionTimeout = protos.DefaultTransactionExpiration  
  52. }  
  53.    
  54. func (smtp *Smtp) setFromConfig(config config.Smtp) error {  
  55.     smtp.Ports = config.Ports  
  56.     if config.SendRequest != nil {  
  57.         smtp.SendRequest = *config.SendRequest  
  58.     }  
  59.     if config.SendResponse != nil {  
  60.         smtp.SendResponse = *config.SendResponse  
  61.     }  
  62.    
  63.     if config.TransactionTimeout != nil && *config.TransactionTimeout > 0 {  
  64.         smtp.transactionTimeout = time.Duration(*config.TransactionTimeout) * time.Second  
  65.     }  
  66.    
  67.     return nil  
  68. }  
  69.    
  70. func (smtp *Smtp) GetPorts() []int {  
  71.     return smtp.Ports  
  72. }  
  73.    
  74. func (smtp *Smtp) Init(test_mode bool, results publisher.Client) error {  
  75.     smtp.initDefaults()  
  76.    
  77.     if !test_mode {  
  78.         err := smtp.setFromConfig(config.ConfigSingleton.Protocols.Smtp)  
  79.         if err != nil {  
  80.             return err  
  81.         }  
  82.     }  
  83.     smtp.results = results  
  84.    
  85.     return nil  
  86. }  
  87.    
  88. func readLine(data []byte, offset int) (bool, string, int) {  
  89.     q := bytes.Index(data[offset:], []byte("\r\n"))  
  90.     if q == -1 {  
  91.         return false, "", 0  
  92.     }  
  93.     return true, string(data[offset : offset+q]), offset + q + 2  
  94. }  
  95.    
  96. func (smtp *Smtp) Parse(pkt *protos.Packet, tcptuple *common.TcpTuple, dir uint8, private protos.ProtocolData, ) protos.ProtocolData {  
  97.    
  98.     defer logp.Recover("ParseSmtp exception")  
  99.    
  100.     priv := smtpPrivateData{}  
  101.     if private != nil {  
  102.         var ok bool  
  103.         priv, ok = private.(smtpPrivateData)  
  104.         if !ok {  
  105.             priv = smtpPrivateData{}  
  106.         }  
  107.     }  
  108.    
  109.     if priv.Data[dir] == nil {  
  110.         priv.Data[dir] = &SmtpStream{  
  111.             tcptuple: tcptuple,  
  112.             data:     pkt.Payload,  
  113.             message:  &SmtpMessage{Ts: pkt.Ts},  
  114.         }  
  115.     } else {  
  116.         // concatenate bytes  
  117.         priv.Data[dir].data = append(priv.Data[dir].data, pkt.Payload...)  
  118.         if len(priv.Data[dir].data) > tcp.TCP_MAX_DATA_IN_STREAM {  
  119.             logp.Debug("smtp", "Stream data too large, dropping TCP stream")  
  120.             priv.Data[dir] = nil  
  121.             return priv  
  122.         }  
  123.     }  
  124.    
  125.     stream := priv.Data[dir]  
  126.     for len(stream.data) > 0 {  
  127.         if stream.message == nil {  
  128.             stream.message = &SmtpMessage{Ts: pkt.Ts}  
  129.         }  
  130.    
  131.         ok, complete := stmpMessageParser(priv.Data[dir])  
  132.         if !ok {  
  133.             // drop this tcp stream. Will retry parsing with the next  
  134.             // segment in it  
  135.             priv.Data[dir] = nil  
  136.             logp.Debug("smtp", "Ignore SMTP message. Drop tcp stream. Try parsing with the next segment")  
  137.             return priv  
  138.         }  
  139.    
  140.         if complete {  
  141.             smtp.messageComplete(tcptuple, dir, stream)  
  142.         } else {  
  143.             logp.Debug("smtp","still wait message...")  
  144.             // wait for more data  
  145.             break  
  146.         }  
  147.     }  
  148.    
  149.     return priv  
  150. }  
  151.    
  152. func (smtp *Smtp) ConnectionTimeout() time.Duration {  
  153.     return smtp.transactionTimeout  
  154. }  
  155.    
  156. func stmpMessageParser(s *SmtpStream) (bool, bool) {  
  157.    
  158.     var value string=""  
  159.    
  160.     for s.parseOffset < len(s.data) {  
  161.    
  162.    
  163.         logp.Info("smtp", "Parse message: %s", string(s.data[s.parseOffset]))  
  164.    
  165.    
  166.         if strings.HasPrefix(string(s.data[s.parseOffset]),"MAIL" ) {  
  167.    
  168.             logp.Debug("smtp", "Hit MAIL command: %s", string(s.data[s.parseOffset]))  
  169.    
  170.             found, line, off := readLine(s.data, s.parseOffset)  
  171.             if !found {  
  172.                 return true, false  
  173.             }  
  174.    
  175.             value = line[1:]  
  176.             logp.Debug("smtp", "value  %s", value)  
  177.    
  178.             s.parseOffset = off  
  179.         } else {  
  180.             logp.Debug("smtp", "Unexpected message starting with %s", s.data[s.parseOffset:])  
  181.             return false, false  
  182.         }  
  183.     }  
  184.    
  185.     return true, false  
  186. }  
  187.    
  188. func handleSmtp(stmp *Smtp, m *SmtpMessage, tcptuple *common.TcpTuple,  
  189. dir uint8, raw_msg []byte) {  
  190.     logp.Info("smtp","handle smtp message...")  
  191.    
  192.     //TODO  
  193.    
  194. }  
  195.    
  196. // Called when the parser has identified a full message.  
  197. func (smtp *Smtp) messageComplete(tcptuple *common.TcpTuple, dir uint8, stream *SmtpStream) {  
  198.    
  199.     logp.Info("smtp","message completed...")  
  200.    
  201.     // all ok, ship it  
  202.     msg := stream.data[stream.message.start:stream.message.end]  
  203.    
  204.     if !stream.message.IgnoreMessage {  
  205.         handleSmtp(smtp, stream.message, tcptuple, dir, msg)  
  206.     }  
  207.    
  208.     // and reset message  
  209.     stream.PrepareForNewMessage()  
  210. }  
  211.    
  212. func (stream *SmtpStream) PrepareForNewMessage() {  
  213.     logp.Info("smtp","prepare for new message...")  
  214.    
  215.     stream.data = stream.data[stream.parseOffset:]  
  216.     stream.parseOffset = 0  
  217.     stream.isClient = false  
  218.     stream.message = nil  
  219. }  
  220.    
  221.    
  222.    
  223. func (smtp *Smtp) GapInStream(tcptuple *common.TcpTuple, dir uint8,  
  224. nbytes int, private protos.ProtocolData) (priv protos.ProtocolData, drop bool) {  
  225.    
  226.     defer logp.Recover("GapInStream(smtp) exception")  
  227.    
  228.     if private == nil {  
  229.         return private, false  
  230.     }  
  231.    
  232.     return private, true  
  233. }  
  234.    
  235. func (smtp *Smtp) ReceivedFin(tcptuple *common.TcpTuple, dir uint8,  
  236. private protos.ProtocolData) protos.ProtocolData {  
  237.    
  238.     logp.Info("smtp","stream closed...")  
  239.    
  240.     // TODO: check if we have data pending and either drop it to free  
  241.     // memory or send it up the stack.  
  242.     return private  
  243. }  


现在切换到命令行,编译一下

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. cd ~/go/src/github.com/elastic/beats/packetbeat  
  2. make  


编译成功,一个滚烫的packetbeat可执行文件就躺在当前目录下了,运行一下先,参数-I 指定pcap文件(还记得前面下载的那个测试文件吧)

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ./packetbeat -d "smtp" -c etc/packetbeat.yml -I ~/Downloads/smtp.pcap  -e -N  


运行查看控制台输出结果:

 

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. ➜  packetbeat git:(smtpbeat) ✗ ./packetbeat -d "smtp" -c etc/packetbeat.yml -I ~/Downloads/smtp.pcap  -e -N   
  2. 2016/01/15 10:12:19.058535 publish.go:191: INFO Dry run mode. All output types except the file based one are disabled.  
  3. 2016/01/15 10:12:19.058570 geolite.go:24: INFO GeoIP disabled: No paths were set under output.geoip.paths  
  4. 2016/01/15 10:12:19.058592 publish.go:262: INFO Publisher name: medcls-MacBook.local  
  5. 2016/01/15 10:12:19.058724 beat.go:145: INFO Init Beat: packetbeat; Version: 1.0.0  
  6. 2016/01/15 10:12:19.059758 beat.go:171: INFO packetbeat sucessfully setup. Start running.  
  7. 2016/01/15 10:12:20.155335 smtp.go:163: DBG  Parse message: 2  
  8. 2016/01/15 10:12:20.155416 smtp.go:180: DBG  Unexpected message starting with 250-xc90.websitewelcome.com Hello GP [122.162.143.157]  
  9. 250-SIZE 52428800  
  10. 250-PIPELINING  
  11. 250-AUTH PLAIN LOGIN  
  12. 250-STARTTLS  
  13. 250 HELP  
  14. 2016/01/15 10:12:22.310974 smtp.go:163: DBG  Parse message: F  
  15. 2016/01/15 10:12:22.311025 smtp.go:180: DBG  Unexpected message starting with From: "Gurpartap Singh"     
  16.      
  17.    
  18. To:      
  19.       
  20.     
  21. Subject: SMTP  
  22. Date: Mon, 5 Oct 2009 11:36:07 +0530  
  23. Message-ID: <000301ca4581$ef9e57f0$cedb07d0$@in>  
  24. MIME-Version: 1.0  
  25. ...  


成功了,邮件内容都在控制台输出了,但这还不是我们要的最终结果,我需要里面的关键信息,我们继续修改smtp.go这个文件。

posted on 2017-03-27 10:32  让编程成为一种习惯  阅读(413)  评论(0编辑  收藏  举报