socket的上下层

网络协议分层本专业的人都知道,最常见的就是socket这层,TCP/IP,IP地址这些概念。
有些人可能知道,上面的应用层协议如smtp,http,ftp都是基于socket实现的,那么是怎么个基于呢?
更少的人可能知道,socket的下层还有数据链路层,这一层又是怎样的呢?

  • 这个例子展示了用socket实现smtp
#include "CSmtp.h"

#pragma warning(push)
#pragma warning(disable:4786)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSmtp::CSmtp()
{
    // Initialize variables
    m_oError = CSMTP_NO_ERROR;
    m_iXPriority = XPRIORITY_NORMAL;
    m_iSMTPSrvPort = 0;

    m_pcLocalHostName = NULL;
    m_pcMailFrom = NULL;
    m_pcNameFrom = NULL;
    m_pcSubject = NULL;
    m_pcMsgBody = NULL;
    m_pcXMailer = NULL;
    m_pcReplyTo = NULL;
    m_pcLogin = NULL;
    m_pcPassword = NULL;
    m_pcSMTPSrvName = NULL;

    if((RecvBuf = new char[BUFFER_SIZE]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }

    if((SendBuf = new char[BUFFER_SIZE]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }

    // Initialize WinSock
    WORD wVer = MAKEWORD(2,2);    
    if (WSAStartup(wVer,&wsaData) != NO_ERROR)
    {
        m_oError = CSMTP_WSA_STARTUP;
        return;
    }
    if (LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) 
    {
        m_oError = CSMTP_WSA_VER;
        WSACleanup();
        return;
    }
}

CSmtp::~CSmtp()
{
    // Clear vectors
    Recipients.clear();
    CCRecipients.clear();
    BCCRecipients.clear();
    Attachments.clear();

    // Free memory
    if (m_pcLocalHostName)
        delete[] m_pcLocalHostName;
    if (m_pcMailFrom)
        delete[] m_pcMailFrom;
    if (m_pcNameFrom)
        delete[] m_pcNameFrom;
    if (m_pcSubject)
        delete[] m_pcSubject;
    if (m_pcMsgBody)
        delete[] m_pcMsgBody;
    if (m_pcXMailer)
        delete[] m_pcXMailer;
    if (m_pcReplyTo)
        delete[] m_pcReplyTo;
    if (m_pcLogin)
        delete[] m_pcLogin;
    if (m_pcPassword)
        delete[] m_pcPassword;
    if(SendBuf)
        delete[] SendBuf;
    if(RecvBuf)
        delete[] RecvBuf;

    // Cleanup
    WSACleanup();
}

//////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////

bool CSmtp::AddAttachment(const char *path)
{
    std::string str(path);
    Attachments.insert(Attachments.end(),str);
    return true;
}

bool CSmtp::AddRecipient(const char *email, const char *name)
{
    assert(email);

    if(!email)
    {
        m_oError = CSMTP_UNDEF_RECIPENT_MAIL;
        return false;
    }

    Recipent recipent;
    recipent.Mail.insert(0,email);
    name!=NULL ? recipent.Name.insert(0,name) : recipent.Name.insert(0,"");

    Recipients.insert(Recipients.end(), recipent);

    return true;    
}

bool CSmtp::AddCCRecipient(const char *email, const char *name)
{
    assert(email);

    if(!email)
    {
        m_oError = CSMTP_UNDEF_RECIPENT_MAIL;
        return false;
    }

    Recipent recipent;
    recipent.Mail.insert(0,email);
    name!=NULL ? recipent.Name.insert(0,name) : recipent.Name.insert(0,"");

    CCRecipients.insert(CCRecipients.end(), recipent);

    return true;
}

bool CSmtp::AddBCCRecipient(const char *email, const char *name)
{
    assert(email);

    if(!email)
    {
        m_oError = CSMTP_UNDEF_RECIPENT_MAIL;
        return false;
    }

    Recipent recipent;
    recipent.Mail.insert(0,email);
    name!=NULL ? recipent.Name.insert(0,name) : recipent.Name.insert(0,"");

    BCCRecipients.insert(BCCRecipients.end(), recipent);

    return true;
}

bool CSmtp::Send()
{
    unsigned int i,rcpt_count,res,FileId;
    char *FileBuf = NULL, *FileName = NULL;
    FILE* hFile = NULL;
    unsigned long int FileSize,TotalSize,MsgPart;

    // ***** CONNECTING TO SMTP SERVER *****

    assert(m_pcSMTPSrvName);

    // connecting to remote host:
    if( (hSocket = ConnectRemoteServer(m_pcSMTPSrvName, m_iSMTPSrvPort)) == INVALID_SOCKET ) 
    {
        m_oError = CSMTP_WSA_INVALID_SOCKET;
        return false;
    }
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 220:
            break;
        default:
            m_oError = CSMTP_SERVER_NOT_READY;
            return false;
    }

    // EHLO <SP> <domain> <CRLF>
    sprintf(SendBuf,"EHLO %s\r\n",GetLocalHostName()!=NULL ? m_pcLocalHostName : "domain");
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 250:
            break;
        default:
            m_oError = CSMTP_COMMAND_EHLO;
            return false;
    }

    // AUTH <SP> LOGIN <CRLF>
    strcpy(SendBuf,"AUTH LOGIN\r\n");
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 334:
            break;
        default:
            m_oError = CSMTP_COMMAND_AUTH_LOGIN;
            return false;
    }

    // send login:
    if(!m_pcLogin)
    {
        m_oError = CSMTP_UNDEF_LOGIN;
        return false;
    }
    std::string encoded_login = base64_encode(reinterpret_cast<const unsigned char*>(m_pcLogin),strlen(m_pcLogin));
    sprintf(SendBuf,"%s\r\n",encoded_login.c_str());
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 334:
            break;
        default:
            m_oError = CSMTP_UNDEF_XYZ_RESPOMSE;
            return false;
    }

    // send password:
    if(!m_pcPassword)
    {
        m_oError = CSMTP_UNDEF_PASSWORD;
        return false;
    }
    std::string encoded_password = base64_encode(reinterpret_cast<const unsigned char*>(m_pcPassword),strlen(m_pcPassword));
    sprintf(SendBuf,"%s\r\n",encoded_password.c_str());
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 235:
            break;
        case 535:
            m_oError = CSMTP_BAD_LOGIN_PASS;
            return false;
        default:
            m_oError = CSMTP_UNDEF_XYZ_RESPOMSE;
            return false;
    }

    // ***** SENDING E-MAIL *****

    // MAIL <SP> FROM:<reverse-path> <CRLF>
    if(m_pcMailFrom == NULL)
    {
        m_oError = CSMTP_UNDEF_MAILFROM;
        return false;
    }
    sprintf(SendBuf,"MAIL FROM:<%s>\r\n",m_pcMailFrom);
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 250:
            break;
        default:
            m_oError = CSMTP_COMMAND_MAIL_FROM;
            return false;
    }

    // RCPT <SP> TO:<forward-path> <CRLF>
    rcpt_count = Recipients.size();
    for(i=0;i<Recipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(Recipients.at(i).Mail).c_str());
        if(!SendData())
            return false;
        Sleep(DELAY_IN_MS);
        if(!ReceiveData())
            return false;

        switch(SmtpXYZdigits())
        {
            case 250:
                break;
            default:
                m_oError = CSMTP_COMMAND_RCPT_TO;
                rcpt_count--;
        }
    }
    if(!rcpt_count)
        return false;
    for(i=0;i<CCRecipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(CCRecipients.at(i).Mail).c_str());
        if(!SendData())
            return false;
        Sleep(DELAY_IN_MS);
        if(!ReceiveData())
            return false;
    }
    for(i=0;i<BCCRecipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(BCCRecipients.at(i).Mail).c_str());
        if(!SendData())
            return false;
        Sleep(DELAY_IN_MS);
        if(!ReceiveData())
            return false;
    }

    // DATA <CRLF>
    strcpy(SendBuf,"DATA\r\n");
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 354:
            break;
        default:
            m_oError = CSMTP_COMMAND_DATA;
            return false;
    }

    // send header(s)
    if(!FormatHeader(SendBuf))
    {
        m_oError = CSMTP_UNDEF_MSG_HEADER;
        return false;
    }
    if(!SendData())
        return false;

    // send text message
    sprintf(SendBuf,"%s\r\n",m_pcMsgBody); // NOTICE: each line ends with <CRLF>
    if(!SendData())
        return false;

    // next goes attachments (if they are)
    if((FileBuf = new char[55]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return false;
    }
    if((FileName = new char[255]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return false;
    }
    TotalSize = 0;
    for(FileId=0;FileId<Attachments.size();FileId++)
    {
        strcpy(FileName,Attachments[FileId].c_str());

        sprintf(SendBuf,"--%s\r\n",BOUNDARY_TEXT);
        strcat(SendBuf,"Content-Type: application/x-msdownload; name=\"");
        strcat(SendBuf,&FileName[Attachments[FileId].find_last_of("\\") + 1]);
        strcat(SendBuf,"\"\r\n");
        strcat(SendBuf,"Content-Transfer-Encoding: base64\r\n");
        strcat(SendBuf,"Content-Disposition: attachment; filename=\"");
        strcat(SendBuf,&FileName[Attachments[FileId].find_last_of("\\") + 1]);
        strcat(SendBuf,"\"\r\n");
        strcat(SendBuf,"\r\n");

        if(!SendData())
            return false;

        // opening the file:
        hFile = fopen(FileName,"rb");
        if(hFile == NULL)
        {
            m_oError = CSMTP_FILE_NOT_EXIST;
            break;
        }

        // checking file size:
        FileSize = 0;
        while(!feof(hFile))
            FileSize += fread(FileBuf,sizeof(char),54,hFile);
        TotalSize += FileSize;

        // sending the file:
        if(TotalSize/1024 > MSG_SIZE_IN_MB*1024)
            m_oError = CSMTP_MSG_TOO_BIG;
        else
        {
            fseek (hFile,0,SEEK_SET);

            MsgPart = 0;
            for(i=0;i<FileSize/54+1;i++)
            {
                res = fread(FileBuf,sizeof(char),54,hFile);
                MsgPart ? strcat(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str())
                          : strcpy(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str());
                strcat(SendBuf,"\r\n");
                MsgPart += res + 2;
                if(MsgPart >= BUFFER_SIZE/2)
                { // sending part of the message
                    MsgPart = 0;
                    if(!SendData())
                    {
                        delete[] FileBuf;
                        delete[] FileName;
                        fclose(hFile);
                        return false;
                    }
                }
            }
            if(MsgPart)
            {
                if(!SendData())
                {
                    delete[] FileBuf;
                    delete[] FileName;
                    fclose(hFile);
                    return false;
                }
            }
        }
        fclose(hFile);
    }
    delete[] FileBuf;
    delete[] FileName;

    // sending last message block (if there is one or more attachments)
    if(Attachments.size())
    {
        sprintf(SendBuf,"\r\n--%s--\r\n",BOUNDARY_TEXT);
        if(!SendData())
            return false;
    }

    // <CRLF> . <CRLF>
    strcpy(SendBuf,"\r\n.\r\n");
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 250:
            break;
        default:
            m_oError = CSMTP_MSG_BODY_ERROR;
            return false;
    }

    // ***** CLOSING CONNECTION *****

    // QUIT <CRLF>
    strcpy(SendBuf,"QUIT\r\n");
    if(!SendData())
        return false;
    Sleep(DELAY_IN_MS);
    if(!ReceiveData())
        return false;

    switch(SmtpXYZdigits())
    {
        case 221:
            break;
        default:
            m_oError = CSMTP_COMMAND_QUIT;
            hSocket = NULL;
            return false;
    }

    closesocket(hSocket);
    hSocket = NULL;
    return true;
}

SOCKET CSmtp::ConnectRemoteServer(const char *server,const unsigned short port)
{
    short nProtocolPort;
    LPHOSTENT lpHostEnt;
    LPSERVENT lpServEnt;
    SOCKADDR_IN sockAddr;
    SOCKET hServerSocket = INVALID_SOCKET;
    struct in_addr addr;

    // If the user input is an alpha name for the host, use gethostbyname()
    // If not, get host by addr (assume IPv4)
    if(isalpha(server[0]))
        lpHostEnt = gethostbyname(server);
    else
    {
        addr.s_addr = inet_addr(server);
    if(addr.s_addr == INADDR_NONE) 
        {
            m_oError = CSMTP_BAD_IPV4_ADDR;
            return INVALID_SOCKET;
        } 
        else
            lpHostEnt = gethostbyaddr((char *) &addr, 4, AF_INET);
    }

    if(lpHostEnt != NULL)
    {
        if((hServerSocket = socket(PF_INET, SOCK_STREAM,0)) != INVALID_SOCKET)
        {
            if(port != NULL)
                nProtocolPort = htons(port);
            else
            {
                lpServEnt = getservbyname("mail", 0);
                if (lpServEnt == NULL)
                    nProtocolPort = htons(25);
                else 
                    nProtocolPort = lpServEnt->s_port;
            }

            sockAddr.sin_family = AF_INET;
            sockAddr.sin_port = nProtocolPort;
            sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
            if(connect(hServerSocket,(PSOCKADDR)&sockAddr,sizeof(sockAddr)) == SOCKET_ERROR)
            {
                m_oError = CSMTP_WSA_CONNECT;
                hServerSocket = INVALID_SOCKET;
            }
        }
        else
        {
            m_oError = CSMTP_WSA_INVALID_SOCKET;
            return INVALID_SOCKET;
        }
    }
    else
    {
        m_oError = CSMTP_WSA_GETHOSTBY_NAME_ADDR;
        return INVALID_SOCKET;
    }

    return hServerSocket;
}

int CSmtp::SmtpXYZdigits()
{
    assert(RecvBuf);
    if(RecvBuf == NULL)
        return 0;
    return (RecvBuf[0]-'0')*100 + (RecvBuf[1]-'0')*10 + RecvBuf[2]-'0';
}

bool CSmtp::FormatHeader(char* header)
{
    int i,s = 0;
    TCHAR szDate[500];
    TCHAR sztTime[500];
    char *to = NULL;
    char *cc = NULL;
    char *bcc = NULL;

    // check for at least one recipient
    if(Recipients.size())
    {
        for (unsigned int i=s=0;i<Recipients.size();i++)
            s += Recipients[i].Mail.size() + Recipients[i].Name.size() + 3;
        if (s == 0) 
            s = 1;
        if((to = new char[s]) == NULL)
        {
            m_oError = CSMTP_LACK_OF_MEMORY;
            return false;
        }

        to[0] = '\0';
        for (i=0;i<Recipients.size();i++)
        {
            i > 0 ? strcat(to,","):strcpy(to,"");
            strcat(to,Recipients[i].Name.c_str());
            strcat(to,"<");
            strcat(to,Recipients[i].Mail.c_str());
            strcat(to,">");
        }
    }
    else
    {
        m_oError = CSMTP_UNDEF_RECIPENTS;
        return false;
    }

    if(CCRecipients.size())
    {
        for (i=s=0;i<CCRecipients.size();i++)
            s += CCRecipients[i].Mail.size() + CCRecipients[i].Name.size() + 3;
        if (s == 0)
            s = 1;
        if((cc = new char[s]) == NULL)
        {
            m_oError = CSMTP_LACK_OF_MEMORY;
            delete[] to;
            return false;
        }

        cc[0] = '\0';
        for (i=0;i<CCRecipients.size();i++)
        {
            i > 0 ? strcat(cc,","):strcpy(cc,"");
            strcat(cc,CCRecipients[i].Name.c_str());
            strcat(cc,"<");
            strcat(cc,CCRecipients[i].Mail.c_str());
            strcat(cc,">");
        }
    }

    if(BCCRecipients.size())
    {
        for (i=s=0;i<BCCRecipients.size();i++)
            s += BCCRecipients[i].Mail.size() + BCCRecipients[i].Name.size() + 3;
        if(s == 0)
            s=1;
        if((bcc = new char[s]) == NULL)
        {
            m_oError = CSMTP_LACK_OF_MEMORY;
            delete[] to;
            delete[] cc;
            return false;
        }

        bcc[0] = '\0';
        for (i=0;i<BCCRecipients.size();i++)
        {
            i > 0 ? strcat(bcc,","):strcpy(bcc,"");
            strcat(bcc,BCCRecipients[i].Name.c_str());
            strcat(bcc,"<");
            strcat(bcc,BCCRecipients[i].Mail.c_str());
            strcat(bcc,">");
        }
    }

    // Date: <SP> <dd> <SP> <mon> <SP> <yy> <SP> <hh> ":" <mm> ":" <ss> <SP> <zone> <CRLF>
    SYSTEMTIME st={0};
    ::GetSystemTime(&st);
    ::GetDateFormat(MAKELCID(0x0409,SORT_DEFAULT),0,&st,"ddd\',\' dd MMM yyyy",szDate,sizeof(szDate));
    ::GetTimeFormat(MAKELCID(0x0409,SORT_DEFAULT),TIME_FORCE24HOURFORMAT,&st,"HH\':\'mm\':\'ss",sztTime,sizeof(sztTime));
    sprintf(header,"Date: %s %s\r\n", szDate, sztTime); 

    // From: <SP> <sender>  <SP> "<" <sender-email> ">" <CRLF>
    if(m_pcMailFrom == NULL)
    {
        m_oError = CSMTP_UNDEF_MAILFROM;
    delete[] to;
    delete[] cc;
    delete[] bcc;
        return false;
    }
    strcat(header,"From: ");    
    if(m_pcNameFrom)
        strcat(header, m_pcNameFrom);
    strcat(header," <");
    strcat(header,m_pcMailFrom);
    strcat(header, ">\r\n");

    // X-Mailer: <SP> <xmailer-app> <CRLF>
    if (m_pcXMailer != NULL)
    {
        strcat(header,"X-Mailer: ");
        strcat(header, m_pcXMailer);
        strcat(header, "\r\n");
    }

    // Reply-To: <SP> <reverse-path> <CRLF>
    if(m_pcReplyTo != NULL)
    {
        strcat(header, "Reply-To: ");
        strcat(header, m_pcReplyTo);
        strcat(header, "\r\n");
    }

    // X-Priority: <SP> <number> <CRLF>
    switch(m_iXPriority)
    {
        case XPRIORITY_HIGH:
            strcat(header,"X-Priority: 2 (High)\r\n");
            break;
        case XPRIORITY_NORMAL:
            strcat(header,"X-Priority: 3 (Normal)\r\n");
            break;
        case XPRIORITY_LOW:
            strcat(header,"X-Priority: 4 (Low)\r\n");
            break;
        default:
            strcat(header,"X-Priority: 3 (Normal)\r\n");
    }

    // To: <SP> <remote-user-mail> <CRLF>
    strcat(header,"To: ");
    strcat(header, to);
    strcat(header, "\r\n");

    // Cc: <SP> <remote-user-mail> <CRLF>
    if(CCRecipients.size())
    {
        strcat(header,"Cc: ");
        strcat(header, cc);
        strcat(header, "\r\n");
    }

    if(BCCRecipients.size())
    {
        strcat(header,"Bcc: ");
        strcat(header, bcc);
        strcat(header, "\r\n");
    }

    // Subject: <SP> <subject-text> <CRLF>
    if(m_pcSubject == NULL) 
    {
        m_oError = CSMTP_UNDEF_SUBJECT;
        strcat(header, "Subject:  ");
    }
    else
    {
      strcat(header, "Subject: ");
      strcat(header, m_pcSubject);
    }
    strcat(header, "\r\n");

    // MIME-Version: <SP> 1.0 <CRLF>
    strcat(header,"MIME-Version: 1.0\r\n");
    if(!Attachments.size())
    { // no attachments
        strcat(header,"Content-type: text/plain; charset=US-ASCII\r\n");
        strcat(header,"Content-Transfer-Encoding: 7bit\r\n");
        strcat(SendBuf,"\r\n");
    }
    else
    { // there is one or more attachments
        strcat(header,"Content-Type: multipart/mixed; boundary=\"");
        strcat(header,BOUNDARY_TEXT);
        strcat(header,"\"\r\n");
        strcat(header,"\r\n");
        // first goes text message
        strcat(SendBuf,"--");
        strcat(SendBuf,BOUNDARY_TEXT);
        strcat(SendBuf,"\r\n");
        strcat(SendBuf,"Content-type: text/plain; charset=US-ASCII\r\n");
        strcat(SendBuf,"Content-Transfer-Encoding: 7bit\r\n");
        strcat(SendBuf,"\r\n");
    }

    // clean up
    delete[] to;
    delete[] cc;
    delete[] bcc;

    // done    
    return true;    
}

bool CSmtp::ReceiveData()
{
    assert(RecvBuf);    
    int res;

    if(RecvBuf == NULL)
        return false;

    if( (res = recv(hSocket,RecvBuf,BUFFER_SIZE,0)) == SOCKET_ERROR )
    {
        m_oError = CSMTP_WSA_RECV;
        return false;
    }
    if(res == 0)
    {
        m_oError = CSMTP_CONNECTION_CLOSED;
        return false;
    }
    RecvBuf[res] = '\0';

    return true;
}

bool CSmtp::SendData()
{
    assert(SendBuf);    
    int idx = 0,res,nLeft = strlen(SendBuf);
    while(nLeft > 0)
    {
        if( res = send(hSocket,&SendBuf[idx],nLeft,0) == SOCKET_ERROR)
        {
            m_oError = CSMTP_WSA_SEND;
            return false;
        }
        if(!res)
            break;
        nLeft -= res;
        idx += res;
    }
    return true;
}

CSmtpError CSmtp::GetLastError()
{
    return m_oError;
}

const char* const CSmtp::GetLocalHostName() 
{
    if(m_pcLocalHostName)
        delete[] m_pcLocalHostName;
    if((m_pcLocalHostName = new char[255]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return NULL;
    }
    if(gethostname((char FAR*)m_pcLocalHostName,255) == SOCKET_ERROR)
        m_oError = CSMTP_WSA_HOSTNAME;
    return m_pcLocalHostName;
}

unsigned const int CSmtp::GetBCCRecipientCount()
{
    return BCCRecipients.size();
}

unsigned const int CSmtp::GetCCRecipientCount() 
{
    return CCRecipients.size();
}

const char* const CSmtp::GetMessageBody() 
{
    return m_pcMsgBody;
}

unsigned const int CSmtp::GetRecipientCount()
{
    return Recipients.size();
}

const char* const CSmtp::GetReplyTo()  
{
    return m_pcReplyTo;
}

const char* const CSmtp::GetMailFrom() 
{
    return m_pcMailFrom;
}

const char* const CSmtp::GetSenderName() 
{
    return m_pcNameFrom;
}

const char* const CSmtp::GetSubject() 
{
    return m_pcSubject;
}

const char* const CSmtp::GetXMailer() 
{
    return m_pcXMailer;
}

CSmptXPriority CSmtp::GetXPriority()
{
    return m_iXPriority;
}

void CSmtp::SetXPriority(CSmptXPriority priority)
{
    m_iXPriority = priority;
}

void CSmtp::SetMessageBody(const char *body)
{
    assert(body);
    int s = strlen(body);
    if (m_pcMsgBody)
        delete[] m_pcMsgBody;
    if((m_pcMsgBody = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcMsgBody, body);    
}

void CSmtp::SetReplyTo(const char *replyto)
{
    assert(replyto);
    int s = strlen(replyto);
    if (m_pcReplyTo)
        delete[] m_pcReplyTo;
    if((m_pcReplyTo = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcReplyTo, replyto);
}

void CSmtp::SetSenderMail(const char *email)
{
    assert(email);
    int s = strlen(email);
    if (m_pcMailFrom)
        delete[] m_pcMailFrom;
    if((m_pcMailFrom = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcMailFrom, email);        
}

void CSmtp::SetSenderName(const char *name)
{
    assert(name);
    int s = strlen(name);
    if (m_pcNameFrom)
        delete[] m_pcNameFrom;
    if((m_pcNameFrom = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcNameFrom, name);
}

void CSmtp::SetSubject(const char *subject)
{
    assert(subject);
    int s = strlen(subject);
    if (m_pcSubject)
        delete[] m_pcSubject;
    m_pcSubject = new char[s+1];
    strcpy(m_pcSubject, subject);
}

void CSmtp::SetXMailer(const char *xmailer)
{
    assert(xmailer);
    int s = strlen(xmailer);
    if (m_pcXMailer)
        delete[] m_pcXMailer;
    if((m_pcXMailer = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcXMailer, xmailer);
}

void CSmtp::SetLogin(const char *login)
{
    assert(login);
    int s = strlen(login);
    if (m_pcLogin)
        delete[] m_pcLogin;
    if((m_pcLogin = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcLogin, login);
}

void CSmtp::SetPassword(const char *password)
{
    assert(password);
    int s = strlen(password);
    if (m_pcPassword)
        delete[] m_pcPassword;
    if((m_pcPassword = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcPassword, password);
}

void CSmtp::SetSMTPServer(const char* SrvName,const unsigned short SrvPort)
{
    assert(SrvName);
    int s = strlen(SrvName);
    if(m_pcSMTPSrvName)
        delete[] m_pcSMTPSrvName;
    if((m_pcSMTPSrvName = new char[s+1]) == NULL)
    {
        m_oError = CSMTP_LACK_OF_MEMORY;
        return;
    }
    strcpy(m_pcSMTPSrvName, SrvName);
}

char* GetErrorText(CSmtpError ErrorId)
{
    switch(ErrorId)
    {
        case CSMTP_NO_ERROR:
            return "";
        case CSMTP_WSA_STARTUP:
            return "Unable to initialise winsock2.";
        case CSMTP_WSA_VER:
            return "Wrong version of the winsock2.";
        case CSMTP_WSA_SEND:
            return "Function send() failed.";
        case CSMTP_WSA_RECV:
            return "Function recv() failed.";
        case CSMTP_WSA_CONNECT:
            return "Function connect failed.";
        case CSMTP_WSA_GETHOSTBY_NAME_ADDR:
            return "Functions gethostbyname() or gethostbyaddr() failed.";
        case CSMTP_WSA_INVALID_SOCKET:
            return "Invalid winsock2 socket.";
        case CSMTP_WSA_HOSTNAME:
            return "Function hostname() failed.";
        case CSMTP_BAD_IPV4_ADDR:
            return "Improper IPv4 address.";
        case CSMTP_UNDEF_MSG_HEADER:
            return "Undefined message header.";
        case CSMTP_UNDEF_MAILFROM:
            return "Undefined from is the mail.";
        case CSMTP_UNDEF_SUBJECT:
            return "Undefined message subject.";
        case CSMTP_UNDEF_RECIPENTS:
            return "Undefined at least one reciepent.";
        case CSMTP_UNDEF_RECIPENT_MAIL:
            return "Undefined recipent mail.";
        case CSMTP_UNDEF_LOGIN:
            return "Undefined user login.";
        case CSMTP_UNDEF_PASSWORD:
            return "Undefined user password.";
        case CSMTP_COMMAND_MAIL_FROM:
            return "Server returned error after sending MAIL FROM.";
        case CSMTP_COMMAND_EHLO:
            return "Server returned error after sending EHLO.";
        case CSMTP_COMMAND_AUTH_LOGIN:
            return "Server returned error after sending AUTH LOGIN.";
        case CSMTP_COMMAND_DATA:
            return "Server returned error after sending DATA.";
        case CSMTP_COMMAND_QUIT:
            return "Server returned error after sending QUIT.";
        case CSMTP_COMMAND_RCPT_TO:
            return "Server returned error after sending RCPT TO.";
        case CSMTP_MSG_BODY_ERROR:
            return "Error in message body";
        case CSMTP_CONNECTION_CLOSED:
            return "Server has closed the connection.";
        case CSMTP_SERVER_NOT_READY:
            return "Server is not ready.";
        case CSMTP_FILE_NOT_EXIST:
            return "File not exist.";
        case CSMTP_MSG_TOO_BIG:
            return "Message is too big.";
        case CSMTP_BAD_LOGIN_PASS:
            return "Bad login or password.";
        case CSMTP_UNDEF_XYZ_RESPOMSE:
            return "Undefined xyz SMTP response.";
        case CSMTP_LACK_OF_MEMORY:
            return "Lack of memory.";
        default:
            return "Undefined error id.";
    }
}
  • 这个例子展示了在socket下层的链路层发送EAP报文实现8021x认证,没有IP这些概念下是如何通信的。
void SetMd5Buf(NewThreadTag *tht,PPASSWORDFRM pBuf, const u_char ID, const u_char *chap)
{   //计算MD5
    u_char TmpBuf[1 + 64 + 16]; 
    MD5_CTX md5T; 
    u_char digest[16]; 
    size_t PasswdLen = strlen(tht->password);
    TmpBuf[0] = ID;//   memcpy(TmpBuf + 0x00, ID, 1); 
    memcpy(TmpBuf + 0x01, tht->password, PasswdLen); 
    memcpy(TmpBuf + 0x01 + PasswdLen, chap, 16); 
    md5T.MD5Update(TmpBuf, 17 + (unsigned int)PasswdLen); 
    md5T.MD5Final(digest); 
    memcpy(pBuf->Md5Pwd, digest, 16); 
}

void PrintPacket(NewThreadTag *tht, bool issend, const u_char* buf, int size)
{
    CStringA strlog(tht->nic);
    strlog += issend ? "发送" : "接收";
    strlog.AppendFormat(" 长度:%d 内容:", size);
    for(int i = 0; i < size; i++)
        strlog.AppendFormat("%02x ",buf[i]);
    Logger::Debug(strlog);
}

void InitBuf(NewThreadTag *tht,u_char *buf)
{   //初始化各个帧
    u_char prototype[3] = {0x88, 0x8e, 0x01};
    u_char mb[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
    if(tht->requestissingle && (tht->DestMAC[0] != 0 || tht->DestMAC[1] != 0 || tht->DestMAC[2] != 0 || tht->DestMAC[3] != 0 || tht->DestMAC[4] != 0 || tht->DestMAC[5] != 0)) 
        memcpy(buf, tht->DestMAC, 6);
    else 
        memcpy(buf,mb,6);
    memcpy(buf+6,tht->mac,6);   //set source MAC
    memcpy(buf+12,prototype,3); //set protocol type and its version
}

bool SendLogin(NewThreadTag *tht)
{   
    bool bRet = false;
    //发送EAPOL-START帧
    u_char buf[100] = { 0 };
    PLOGINFRM pbuf = (PLOGINFRM) buf;
    u_char mb[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};
    InitBuf(tht,buf);//Set Dest MAC, Source MAC, Protocol Type and Version
    if(tht->issingle) memset(buf, 0xff, 6);//广播发送EAPOL-START(默认为多播发送)
    else memcpy(buf, mb, 6);
    pbuf->PktType = 0x01;        //EAPOL-START

    if(!mpcap_sendpacket(tht->adhandle, buf, 19))
    {       
        PrintPacket(tht,true,buf,19);
        bRet = true;
    } 
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendLogin::EAPOL-START】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendLogin::EAPOL-START】==>失败");
    return bRet;
}

bool SendLogout(NewThreadTag *tht)
{   
    bool bRet = false;
    //发送断线请求帧
    u_char buf[100] = { 0 };
    PLOGOUTFRM pbuf = (PLOGOUTFRM) buf;
    InitBuf(tht,buf);//set Dest MAC, Source MAC, Protocol Type and Version
    pbuf->PktType = 0x02;//EAPOL-LOGOUT
    if(!mpcap_sendpacket(tht->adhandle, buf, 19))
    {
        PrintPacket(tht,true,buf,19);
        bRet = true;
    }   
    if(bRet)
        g_1xdlg.AddDebugInfo(L"发送【SendLogout::EAPOL-LOGOUT】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"发送【SendLogout::EAPOL-LOGOUT】==>失败");
    return bRet;
}

bool SendVersion(NewThreadTag *tht,const u_char Id)
{   
    bool bRet = false;
    //发送版本确认帧
    u_char buf[100] = {
    0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00, 
    0x00, 0x31, 0x02, 0x01 ,0x00, 0x31, 0x02, 0x01,
    0x16, 0x2e,  0x25,0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x16, 0x5f, 
    0x59, 0x55, 0x5e, 0xbb, 0x5c, 0x54, 0x58, 0x5a,
    0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d,
    0x6d, 0x6d, 0x6d
    }; 
    InitBuf(tht,buf);
    if(!mpcap_sendpacket(tht->adhandle, buf, 67))
    {
        PrintPacket(tht,true,buf,67);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendVersion】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendVersion】==>失败");    
    return bRet;
}

bool SendUsername(NewThreadTag *tht,const u_char Id)
{   
    bool bRet = false;
    //发送用户名帧
    u_char buf[300] = { 0 };
    PUSERNAMEFRM pbuf = (PUSERNAMEFRM) buf;
    char username[100] = {0};
    if(tht->privatelabel == 1)  
    {
        sprintf_s(username,sizeof(username),"%s%s",tht->username,tht->labelsuffix);
    }
    else
    {
        sprintf_s(username,sizeof(username),"%s",tht->username);
    }
    InitBuf(tht,buf);
    pbuf->Hdr.Len1 = htons((u_short)(strlen(username) + 0x05));
    pbuf->Hdr.Code = 0x02;
    pbuf->Hdr.Id = Id;
    pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
    pbuf->Hdr.EapType = 0x01;
    memcpy(pbuf->Username, username, strlen(username));
    if(!mpcap_sendpacket(tht->adhandle, buf, (int)strlen(tht->username)+40))
    {
        PrintPacket(tht,true,buf,strlen(tht->username)+40);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendUsername】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendUsername】==>失败");   
    return bRet;
}

// 发送密码帧
bool SendNak(NewThreadTag *tht,const u_char Id)
{   
    bool bRet = false;
    u_char buf[300] = { 0 };
    PPASSWORDFRM pbuf = (PPASSWORDFRM) buf;
    InitBuf(tht,buf);
    pbuf->Hdr.Len1 = htons(6);
    pbuf->Hdr.Code = 0x02;
    pbuf->Hdr.Id = Id;
    pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
    pbuf->Hdr.EapType = 0x03;
    pbuf->Unknown[0] = 0x04;
    if(!mpcap_sendpacket(tht->adhandle, buf, 72))
    {
        PrintPacket(tht,true,buf,72);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendNak】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendNak】==>失败");    
    return bRet;
}


bool SendNACOldVersion(NewThreadTag *tht,const u_char Id, const u_char *Chap)
{   
    bool bRet = false;
    u_char buf[300] = { 0 };
    PPASSWORDFRM pbuf = (PPASSWORDFRM) buf;
    InitBuf(tht,buf);
    strcpy_s((char*)(pbuf->Username), sizeof(pbuf->Username), tht->mid);
    pbuf->Hdr.Len1 = htons(strlen((char*)(pbuf->Username)) + 0x17);
    pbuf->Hdr.Code = 0x02;
    pbuf->Hdr.Id = Id;
    pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
    pbuf->Hdr.EapType = EAP_NAC;
    pbuf->Unknown[0] = 0x10;
    SetMd5Buf(tht,pbuf, Id, Chap);//计算MD5
    if(!mpcap_sendpacket(tht->adhandle, buf, strlen((char*)(pbuf->Username))+41))
    {
        PrintPacket(tht,true,buf, strlen((char*)(pbuf->Username))+41);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendNACOldVersion】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendNACOldVersion】==>失败");  
    return bRet;
}

//  发送密码帧 
bool SendNAC(NewThreadTag *tht,const u_char Id, u_char *Chap, u_char8 codetype)
{
    bool bRet = false;
    if(codetype == EAP_NAC)
        return SendNACOldVersion(tht, Id, Chap);
    u_char buf[300] = { 0 };
    PNACPASSWORDFRM pbuf = (PNACPASSWORDFRM) buf;
    DWORD pwdlen = (DWORD)strlen(tht->password);
    DWORD midlen = (DWORD)strlen(tht->mid);
    DWORD serverkeylen = (DWORD)strlen(tht->serverkey);
    InitBuf(tht,buf);
    //getmid((char*)(pbuf->Username),sizeof(pbuf->Username));   
    pbuf->tag = 0;  
    pbuf->reserver[0] = 1; //2017.08.21 增加tlv内容
    pbuf->mode = tht->dialmode;
    pbuf->pwdtype = 0;
    pbuf->pwdlen = (u_char8)strlen(tht->password);
    memcpy(pbuf->pwdcontent,tht->password,strlen(tht->password));
    MD5_CTX md5T; 
    u_char digest[16]; 
    md5T.MD5Update(Chap, 16); 
    md5T.MD5Final(digest); 
    rc4 _rc4(digest,16);
    _rc4.rc4_encode(pbuf->pwdcontent,(int)strlen(tht->password));
    u_char8* pmidlen = pbuf->pwdcontent+pwdlen;
    *pmidlen = (u_char8)midlen;
    char* pmid = (char*)(pmidlen+1);
    strcpy_s(pmid,midlen+1,tht->mid); //strcpy_s 会把null也复制进去
    char* pserverkeyType = pmid+midlen;
    *pserverkeyType = 0x03;  //skylar_server_key
    u_char8* pserverkeyLen = (u_char8*)pserverkeyType+1;
    *pserverkeyLen = (u_char8)serverkeylen;
    char* pserverkeyValue = (char*)(pserverkeyLen+1);
    strcpy_s(pserverkeyValue,serverkeylen+1,tht->serverkey);
    pbuf->Hdr.Len1 = htons((u_short)(pwdlen+midlen + 15 + 2 + serverkeylen));
    pbuf->Hdr.Code = 0x02;
    pbuf->Hdr.Id = Id;
    pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
    pbuf->Hdr.EapType = codetype;//tht->wx->m_cfg.prop_servicecontrol.prop_codetype;
    if(!mpcap_sendpacket(tht->adhandle, buf, 18 + pwdlen + midlen + 15 + 2 + serverkeylen))
    {
        PrintPacket(tht,true,buf,  18 + pwdlen + midlen + 15 + 2 + serverkeylen);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendNAC】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendNAC】==>失败");    
    return bRet;
}

// 就是要依靠这个函数进行查找
bool SendKeeponline(NewThreadTag *tht,const u_char Id)
{   
    bool bRet = false;
    //发送保持在线帧
    u_char buf[300] = { 0 };
    PKEEPONLINEFRM pbuf = (PKEEPONLINEFRM) buf;
    InitBuf(tht,buf);
    pbuf->Hdr.Len1 = htons((u_short)(strlen(tht->username) + 0x05));
    pbuf->Hdr.Code = 0x02;
    pbuf->Hdr.Id = Id;
    pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
    pbuf->Hdr.EapType = 0x14;
    pbuf->Unknown[0] = 0x00;
    pbuf->Unknown[1] = 0x15;
    pbuf->Unknown[2] = 0x04;
    memcpy(pbuf->Ip, tht->ip, 4);
    memcpy(pbuf->Username, tht->username, strlen(tht->username));
    if(!mpcap_sendpacket(tht->adhandle, buf, (int)strlen(tht->username)+40))
    {
        PrintPacket(tht,true,buf,strlen(tht->username)+40);
        bRet = true;
    }
    if(bRet)
        g_1xdlg.AddDebugInfo(L"【SendKeeponline】==>成功");
    else
        g_1xdlg.AddDebugInfo(L"【SendKeeponline】==>失败"); 
    return bRet;
}