小弟现在做一个sniffer,要求能把数据全部还原,但是处理mail时(封装在一个SMTP类里面处理),有个很棘手的问题,所有的数据和包头都是连成一片的,也就是说boundary(就是NEXTPART那个标示)有可能在两个数据包之间,所以目前我只有把两个包组合起来用(程序对效率还有一定的要求,老大不准把所有数据截获完再来处理),但是这样也只减少了1/2概率,而且这样做程序目前都还有问题,关键是解码一个字符都不能多或者少(比如base64解码最明显),而分的段又太多了,难免保证不出意外,有时一个包可能有3个boundary。
那位高手有好点的建议没,小弟谢谢了先。
我也想过干脆把User和Password还原出来,我也去POP3算了,但是又不知道前面User和Password是用的什么算法,请高手指点。
不知道邮件服务器一般是怎么解决解码的问题的?

解决方案 »

  1.   

    做成状态机,一点都不会错。根据收到的文本行改变状态。不满一行缓存留待下次。
    参考rfc。
      

  2.   

    为了大家分析方便,付上源代码
    bool    SMTP::DealWithSmtp(char *pdata,int len ,unsigned long ackno)
    {
        char *datapos=NULL;
    char *nextdata=NULL;
    CString infotemp,strtemp;
        
    static int bufnum=0;
    //char *findbound="find the bound!";
        maildata.Write(pdata, len); 
    if(m_first)
    {
    m_firstsourceip=GetSourceip();
    m_first    =false;
        m_helloOver=true;

    }
    if(*pdata=='Q'&& *(pdata+1)=='U'&& *(pdata+2)=='I' && *(pdata+3)=='T')
    {
            m_first    =true;
    m_mailbegin=false;
    m_infoOver=false;
    // m_creflag  =false;
    m_firstackno=0;
    return true;
    }
    if(m_helloOver)
    if(m_firstsourceip==GetSourceip())
        datapos =GetMailInfo(pdata,len); if(m_firstsourceip==GetSourceip())
       if(m_firstackno==ackno)
     if(m_infoOver)
     {
          bufnum++;
          if(bufnum%2)
      {                  strncpy(m_twobuf,pdata,len);//用twobuf来控制
      prelen=len;
      }
          else
      {
         strncpy(m_twobuf+prelen,pdata,len);
          len+=prelen;
        if(nextdata=Findboundary(m_twobuf,len))        //找寻是否存在BOUND,如果有返回找到后的位置,这里开始和下面其实是一样的了

              

    WriteToFile(m_twobuf,m_findbound);
    datapos=GetboundInfo(nextdata,m_leavelen); //如果存在一个BOUND则取它的信息和
                                        //并返回位置datapos
              if(m_findbound<0)                //处理特殊情况
                         m_findbound=0;
        
     FindNewFile();   
                     GetBodyLen(datapos,m_leavelen);          //根据取的BOUND后的位置取主体的长度
       WriteToFile(datapos,m_bodylen);
         m_leavelen-=m_bodylen;                   //得到剩余长度,bodylen置空,以便下次使用
         m_bodylen=0;                             
       while(m_leavelen)                        //如果还有剩余
       {
                         if(nextdata=Findboundary(datapos,m_leavelen))//如果剩余文件中还有BOUND,重复取得
     {
      datapos=GetboundInfo(nextdata,m_leavelen);
      
          GetBodyLen(datapos,m_leavelen);
          FindNewFile();
      WriteToFile(datapos,m_bodylen);
          m_leavelen-=m_bodylen;
      datapos+=(m_bodylen+4);
      m_bodylen=0;
     }
         else                             //没有BOUND了,直接写入
     {
                           WriteToFile(datapos,m_leavelen);
           m_leavelen=0;
          break;
     }    }
         
    }
        else
    {
    WriteToFile(m_twobuf,len);

    }
           for(int i=0;i<3000;i++)   
             m_twobuf[i]=0;
      }
        
     }
    if(m_CanGetInfo)
    {
          
        for(int j=0;j<9;j++)
    {
    if(m_fielddata[j])
    {
      strtemp.Format("%s",m_fielddata[j]);
          infotemp+=strtemp;
      infotemp+="\r\n";
    }
    } mailusedata.Write(infotemp, infotemp.GetLength());
    m_helloOver=false;
        m_firstackno=ackno;
    m_infoOver=true;
                m_CanGetInfo=false;
        m_textbound=Gettextbound();
    if((m_infolen+4) < len)
    {
         if(nextdata=Findboundary(datapos,len-m_infolen))
    {
       
       datapos=GetboundInfo(nextdata,m_leavelen);
       m_filebound=Getfilebound();
       while(m_leavelen)
       {
                          if(nextdata=Findboundary(datapos,m_leavelen)) //nextdata为bound参数的位置,如果跳出则m_leavelen减
      {
      
          datapos=GetboundInfo(nextdata,m_leavelen);   //datapos是主体数据的位置,如果跳出则m_leavelen减
              FindNewFile();
      GetBodyLen(datapos,m_leavelen); 
      WriteToFile(datapos,m_bodylen);

      m_leavelen-=m_bodylen;
          datapos+=(m_bodylen+4);
          m_bodylen=0;
      }
      else
      {
                            WriteToFile( datapos , m_leavelen); 
                             m_leavelen=0;
          break;
      }
       }
                       
    }
           else
       {
                     WriteToFile( datapos , len-m_infolen); 
      
       }
    }
              // maildata.Write(pdata, len);
    }

         return  true;
    }
    所有的SMTP处理也就在这个DealWith里面,上面一部分和下面差不多,只是条件不一样。
    这个程序目前还有丢失字符的问题,高手请指出来。
      

  3.   

    有个屁道理,数据当然截获完了才能处理,又不是流媒体,
    如果是一个zip文件呢,拿了半个你去处理看看.
    截取就负责截取,截获的数据放在内存或磁盘缓冲区里,开其他线程处理,或者
    干脆在停止sniffer后做数据分析,象iris之类的都是这么做的.
      

  4.   

    老兄没理解到我的意思,截获的数据全部都已经在一个LIST中,问题是要不要再把LIST的中的SMTP数据复制一个到BUF(有可能很大)来专门接收SMTP信息然后处理。
    还有,用多线程边接收边处理,只要得到了头信息也应该是可行的啊,比如我得到了文件名信息,后来的同一个ACKNO就把他写入一个文件就行了。
      

  5.   

    你应该需要从list中取得需要的数据再处理的,在解码的时候可能用到位运算,这样可能好一些。
      

  6.   

    不是实时处理就更简单了,一行一行取不就行了?
    实时的话,就应该做成状态机。smtp协议控制块也是有不同状态的。
      

  7.   

    这样的话,你原来parse的代码是针对一整块内存的,现在需要改成针对一个buf list的.
    我看这样做的开销,比你复制到一个大buf里再处理要大.
      

  8.   

    程序有两个线程,一个是负责接收的(接受的数据全部在一个LIST表中),一个是负责处理的,我这个是处理线程中的一部分
      

  9.   

    我晕,本来那个BUF LIST就是必须的(那是接收线程里面的BUF),目前是不要在BUF LIST基础上再加一大的BUF,说白了就是要基本上实时的来处理,但是 
    22  
              ------=_NextPart_000_0063_01C
    23       10.150.1.10----->202.108.44.204        大小为:1452
    SeqNo:    670762205----->AckNo:    -189272848
      2FE7D.E4DFE240     //这个就是boundary的直
    这样的情况怎么办?
      

  10.   

    不是,pdata是BUFLIST的一个BUF而已
      

  11.   

    你说的怎么颠三倒四的?
    一开始说"程序对效率还有一定的要求,老大不准把所有数据截获完再来处理",
    然后又倒过来了"截获的数据全部都已经在一个LIST中,问题是要不要再把LIST的中的SMTP数据复制一个到BUF(有可能很大)来专门接收SMTP信息然后处理"
    你把数据全放在buf list里就是为了不在截获的同时做parse,就是为了不实时的做parse,现在又说"说白了就是要基本上实时的来处理"
    我就搞不懂了,你到底要什么??
      

  12.   

    static char unhandled[MAXLINELEN]
    while(more_pkts_in_list)
    {
     get a packet;
     while(packet.有回车)
     {
      把回车前的内容拼凑到unhandled中,得到一整行
      handleline();//处理之
     }
     把最后一个回车后的保存到unhandled中
    }对handleline来说,就是一行一行处理了。
      

  13.   

    基本流程是这样,接收线程接收一个就加入LIST一个,处理线程是死循环,有一个才处理一个,处理完了就释放掉,没有就休眠,我处理一个就写一个,比如这个包是index.zip的我就写到index.zip中,那点有问题?接收完了再处理?我怎么知道它什么时候完?
    buflist接收到一个buf才AddTail()加入到list中,不说了,现在方法已经定了--边接收边处理,我们最后要做的是个防火墙,不可能接收完了才处理,这样机器早就挂了。
      

  14.   

    好么,一开始说做sniifer,现在又变成firewall了.
    sniffer用作截获分析网络数据,分析某些数据可能需要比较多的时间,可以非实时,
    防火墙当然必须是实时的,拿防火墙做sniffer的事情,这种设计在效率上可行吗??
      

  15.   

    不知道什么时候完?可能吗?邮件正文以\r\n.\r\n结束.你在buf list里发现"\r\n.\r\n"就意味着邮件结束,当然,查"\r\n.\r\n"不能用一般的查找函数,因为可能是跨buf的,我前面都说了,把针对buf的函数改写成自己的针对buf list的函数,接口不变.
      

  16.   

    我想 robinford(罗宾)说的realtime是这个意思:每收到一个包就马上处理,一旦某mail所有包都处理后,就可以察看;而不是有一点看一点。并不太难啊...
    防火墙?要做邮件过滤吗?这个sniffer可不行
      

  17.   

    其实都不用作解码,写到一个.eml文件,ShellExecute就行
      

  18.   

    我晕死,我说的是最后要成为一个firewall,现在的功能不是相当一个sniffer 是什么?何况谁规定的sniffer 一定要截获完数据才处理?ahao(天·狼·星星) 你不要咬住这些问题不放好不好?什么"/r/n"就是结束?那是一部分结束的标志(有可能是头结束,有可能是尾结束,还有其他情况),结束是QUIT,表示邮件发送完了,是,邮件是可以知道,但是其他比如FTP,HTTP就很困难了。如果改成对buflist处理(也就是不实时处理),肯定是要简单很多,因为数据全部连接起来了,就不存在跨越两个数据包这样的问题了,处理也是直线形的,不容易发生数据丢失和各种意外,但是要拷贝buflist一个邮件里面的所有数据,有可能有很大的开销(比如可能有10M的邮件怎么办),所以综上,还是只有接一个处理一个,虽然比较麻烦,但是也是没的办法的事。
      

  19.   

    最终的目标不同,设计原则有很大不同,懂吗?你能保证现在的设计以后能过渡过去?
    我说了"/r/n"了吗?你看看清楚,我说的是"邮件正文以\r\n.\r\n结束",这是一个例子.
    QUIT是什么?smtp会话结束才是QUIT,这时候可能已经发了100封信了.
    还有,你懂不懂什么是list?说了半天,你认为buf list是物理空间连续的buffer?
      

  20.   

    kenpk163=A3=AC=C4=FA=BA=C3=A3=A1=09=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=D6=C2
    =C0=F1=A3=A1
     =09=09=09=09=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1=A1robin
      

  21.   

    47   顺序号:257765906   确认号:973667268    10.150.1.10----->202.108.44.205   大小为:6  QUIT
    153   顺序号:260929923   确认号:8603353    10.150.1.10----->202.108.44.205   大小为:6  QUIT
      

  22.   

    我不想和你说了,你自己睁大眼睛看清楚了来说话。
    还有我什么时候说了buflist是物理上连续的,难道我不知道把他们一个个取出来再写到一个BigBuf里面?
    没你想的这么笨。
    我是菜鸟,我不和ahao(天·狼·星星)这样只能够咬文嚼字挑毛病的大虾比。