首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >尝试通过使用c程序的SMTP与电子邮件服务器对话

尝试通过使用c程序的SMTP与电子邮件服务器对话
EN

Stack Overflow用户
提问于 2016-06-22 16:00:18
回答 1查看 1.9K关注 0票数 1

我试图创建一个小C程序,允许我通过smtp与邮件服务器对话,我创建了这个小程序:

代码语言:javascript
复制
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "25"
#define DEFAULT_ADDRESS "smtp.live.com"
int __cdecl main(int argc, char **argv) 
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
                    *ptr = NULL,
                    hints;
    char messaggio[100];
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;

    while(1){// Initialize Winsock
        iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (iResult != 0) {
            printf("WSAStartup failed with error: %d\n", iResult);
            return 1;
        }

        ZeroMemory( &hints, sizeof(hints) );
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;

        // Resolve the server address and port
        iResult = getaddrinfo(DEFAULT_ADDRESS, DEFAULT_PORT, &hints, &result);
        if ( iResult != 0 ) {
            printf("getaddrinfo failed with error: %d\n", iResult);
            WSACleanup();
            return 1;
        }

    // Attempt to connect to an address until one succeeds
        for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {

            // Create a SOCKET for connecting to server
            ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, 
                ptr->ai_protocol);
            if (ConnectSocket == INVALID_SOCKET) {
                printf("socket failed with error: %ld\n", WSAGetLastError());
                WSACleanup();
                return 1;
            }

            // Connect to server.
            iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
            if (iResult == SOCKET_ERROR) {
                closesocket(ConnectSocket);
                ConnectSocket = INVALID_SOCKET;
                continue;
            }
            break;
        }

        freeaddrinfo(result);

        if (ConnectSocket == INVALID_SOCKET) {
            printf("Unable to connect to server!\n");
            WSACleanup();
            return 1;
        }

        printf("messaggio: ");
        gets(messaggio);
        fflush(stdin);
        printf("%s\n", messaggio);
        // Send an initial buffer
        iResult = send( ConnectSocket, messaggio, (int)strlen(messaggio), 0 );
        if (iResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }

        printf("Bytes Sent: %ld\n", iResult);

        // shutdown the connection since no more data will be sent
        iResult = shutdown(ConnectSocket, SD_SEND);
        if (iResult == SOCKET_ERROR) {
            printf("shutdown failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }

        // Receive until the peer closes the connection
      //  do {

            iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
                if ( iResult > 0 ){
                printf("Bytes received: %d\n", iResult);
                printf("messaggio: %s\n", recvbuf);}
            else if ( iResult == 0 )
                printf("Connection closed\n");
            else
                printf("recv failed with error: %d\n", WSAGetLastError());
       // } while( iResult > 0 );
        // cleanu
        closesocket(ConnectSocket);
        WSACleanup();
    }
    system("PAUSE");
    return 0;
}

当我启动它时,它确实连接到smtp服务器,然后发送消息(例如Hello消息)并接收220回复代码,这意味着服务已经准备好了。当我继续输入命令时,它会继续向我发送220段代码,而当我错过一个命令时,它就会粉碎掉。

你们谁能帮帮我?

附注:我不信任代码,我只是修改了一些微软的套接字客户端文档来与smtp.live.com对话,显然它确实有效.

编辑x2:

好吧,多亏了雷米·莱博,我显然解决了这些问题,希望这是最后一个问题。

编辑代码:

代码语言:javascript
复制
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "25"
#define DEFAULT_ADDRESS "smtp.live.com"

int __cdecl main(int argc, char **argv){
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
    *ptr = NULL,
    hints;
    char messaggio[100];
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;
    int success = 1;

    iResult = WSAStartup(MAKEWORD(2,2), &wsaData); // Initialize Winsock
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    iResult = getaddrinfo(DEFAULT_ADDRESS, DEFAULT_PORT, &hints, &result); // Resolve the server address and port
    if ( iResult != 0 ) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {  // Attempt to connect to an address until one succeeds

        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);  //Connect to server.
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }

        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    while(success){

        printf("messaggio: ");
        scanf(" %[^\n]", messaggio);
        printf("%s\n", messaggio);

        iResult = send( ConnectSocket, strcat(messaggio,"\r\n"), (int)strlen(messaggio)+2, 0 );  //Send an initial buffer
        if (iResult < 0 || iResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            system("PAUSE");
            return 1;
        }
        printf("Bytes Sent: %ld\n", iResult);

        do{  //riceve tutto il possibile dal server
            iResult = recv(ConnectSocket, recvbuf, recvbuflen-1, 0);
            recvbuf[iResult]='\0';
            if ( iResult > 0 ){
                printf("Bytes received: %d\n", iResult);
                printf("messaggio: %s\n", recvbuf);
            }
            else{
                printf("recv failed with error: %d\n", WSAGetLastError());
                success=0;
            }
        }while(recvbuf[iResult-3]=='\r'&& recvbuf[iResult-2]=='\n');

        printf("stop receive\n");
    }
    iResult = shutdown(ConnectSocket, SD_SEND);
    closesocket(ConnectSocket);
    WSACleanup();
    system("PAUSE");
    return 0;
}

现在它确实接受了我的命令并进行了响应,我执行了helomail from: <mail>to: <mail>,在这里它说:"530 ( auth问题的代码)必须首先发出一个STARTTLS命令。

有办法解决这个问题吗?我必须对我的发送命令进行编码,有什么方法可以用winsock来完成吗?

我几乎明白问题的意义了,还是这不是问题?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-06-23 03:19:36

您的代码逻辑对于SMTP客户端是不完整的。

TCP是一个字节流。不能保证您能够在一个send()调用中发送整个命令,也不能保证您能够在一个recv()调用中接收整个响应。你必须不断地发送,直到你没有什么可以发送,你必须继续阅读,直到没有更多的阅读。在SMTP的情况下,这意味着当您遇到终止每一行的\r\n时。

一旦您的基础套接字I/O工作,您仍然没有实现实际的SMTP协议。您只是向服务器抛出任意数据,并读取它返回的任意数据。SMTP有您需要考虑的规则。例如,SMTP服务器在接受命令之前发送问候语。你不是在读那种问候。此外,SMTP响应可能有1行或更多行,因此您必须分别解析每一行(格式在RFC 5321中概述),以发现需要读取多少行。继续阅读,直到你遇到回复的最后一行。在完全读取之前,不要发送下一个命令(除非您想要实现SMTP流水线,我不会在这里讨论)。

至于“必须首先发出STARTTLS命令”错误,这是相当不言自明的。您通过不安全的连接发送了SMTP命令,但服务器希望通过安全连接发送该命令(例如,当发送包含纯文本身份验证凭据的AUTH命令时)。STARTTLS命令用于激活新的SSL/TLS会话,以保护当前不安全的连接。

您可以使用像OpenSSL这样的库或像微软的SChannel这样的API在套接字上实现实际的SSL/TLS。这不属于这个问题的范围。如果您想要帮助实现SSL/TLS,就有很多关于这个主题的书籍和教程。

所有这些都说了,尝试更像这样的东西:

代码语言:javascript
复制
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")
#pragma command(lib, "mswsock.lib")
//#pragma command(lib, "advapi32.lib")

#define DEFAULT_BUFLEN 512

#define DEFAULT_PORT "25"
#define DEFAULT_ADDRESS "smtp.live.com"

char *recvbuf = NULL;
int recvbuflen = 0;
int recvbufused = 0;

int sendData(SOCKET s, char *buffer, int buflen)
{
    int iResult;

    while (buflen > 0)
    {
        // TODO: if the connection is secure, encrypt the data being sent...

        iResult = send(s, buffer, buflen, 0);
        if (iResult == SOCKET_ERROR)
        {
            printf("send failed with error: %d\n", WSAGetLastError());
            return -1;
        }

        buffer += iResult;
        buflen -= iResult;
    }

    return 0;
}

int sendLine(SOCKET s, char *line)
{
    int iResult = sendData(s, line, strlen(line));
    if (iResult == 0)
        iResult = sendData(s, "\r\n", 2);
    return iResult;
}

int readLine(SOCKET s, char **line)
{
    char buf[DEFAULT_BUFLEN];
    int iResult;
    int offset = 0, len, total;
    char *p;

    *line = NULL;

    do
    {
        // check if the line terminator is already in the buffer
        p = memchr(recvbuf+offset, '\n', recvbufused-offset);
        if (p != NULL)
        {
            ++p;
            total = (p - recvbuf);

            // check if the line terminator is actually "\r\n"
            len = total - 1;
            if ((len > 0) && (recvbuf[len-1] == '\r'))
                --len;

            // extract the line and remove it from the buffer
            *line = malloc(len+1);
            if (*line == NULL)
            {
                printf("Unable to allocate memory! Bytes: %d\n", len+1);
                return -1;
            }

            memcpy(*line, recvbuf, len);
            (*line)[len] = 0;

            memmove(recvbuf, recvbuf+total, recvbufused-total);
            recvbufused -= total;

            printf("%s\n", *line);
            return len;
        }

        // line terminator is not in the buffer, keep reading

        // do not search the buffer that was already searched
        offset = recvbufused;

        // read more data from the socket
        iResult = recv(s, buf, sizeof(buf), 0);
        if (iResult <= 0)
        {
            if (iResult == 0)
                printf("server closed the connection\n");
            else
                printf("recv failed with error: %d\n", WSAGetLastError());

            break;
        }

        // TODO: if the connection is secure, decrypt the data that was received...

        // if the buffer is too small for the new data, grow it
        if ((recvbufused+iResult) > recvbuflen)
        {
            len = (recvbufused+iResult) + DEFAULT_BUFLEN;
            p = realloc(recvbuf, len);
            if (p == NULL)
            {
                printf("Unable to allocate memory! Bytes: %d\n", len);
                break;
            }

            recvbuf = p;
            recvbuflen = len;
        }

        // now add the data to the buffer
        memcpy(recvbuf+recvbufused, buf, iResult);
        recvbufused += iResult;
    }
    while (1);

    return -1;
}

int readResponse(SOCKET s)
{
    char *line, term[5];
    int len, code = 0;

    // read the first line
    len = readLine(s, &line);
    if (len == -1)
        return -1;

    // check if the response has multiple lines
    if ((len > 3) && (line[3] == '-'))
    {
        // keep reading until the last line is received
        memcpy(term, line, 3);
        term[3] = ' ';
        term[4] = '\0';

        do
        {
            free(line);
            len = readLine(s, &line);
            if (len == -1)
                return -1;
        }
        while ((len > 3) && (strncmp(line, term, 4) != 0));
    }

    // now extract the response code and return it

    if (len > 3) line[3] = '\0';
    code = strtol(line, NULL, 10);
    free(line);

    if (code == 0)
        return -1;

    return code;
}

int __cdecl main(int argc, char **argv)
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo hints, *result, *ptr;
    char messaggio[100];
    int iResult;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0)
    {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(DEFAULT_ADDRESS, DEFAULT_PORT, &hints, &result);
    if (iResult != 0)
    {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to an address until one succeeds
    for(ptr=result; ptr != NULL; ptr=ptr->ai_next)
    {
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET)
        {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            break;
        }

        //Connect to server.
        iResult = connect(ConnectSocket, ptr->ai_addr, ptr->ai_addrlen);
        if (iResult == 0)
            break;

        closesocket(ConnectSocket);
        ConnectSocket = INVALID_SOCKET;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET)
    {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    printf("Connected to server\n");

    // read the greeting
    iResult = readResponse(ConnectSocket);
    if (iResult == 220)
    {
        do
        {
            // ask the user for a command
            printf("messaggio: ");
            if (!fgets(messaggio, sizeof(messaggio), stdin))
                break;

            // remove the line terminator if present
            iResult = strlen(messaggio);
            if ((iResult > 0) && (messaggio[iResult-1] == '\n'))
                messaggio[iResult-1] = '\0';
    
            // send the command
            iResult = sendLine(ConnectSocket, messaggio);
            if (iResult == -1)
                break;

            // read the response
            iResult = readResponse(ConnectSocket);
            if (iResult == -1)
                break;

            // need to stop now?
            if (strcmp(messaggio, "QUIT") == 0)
                break;

            // need to turn on SSL/TLS now?
            if ((strcmp(messaggio, "STARTTLS") == 0) && (iResult == 220))
            {
                // TODO: activate SSL/TLS, stop if failed...
            }
        }
        while(1);
    }

    shutdown(ConnectSocket, SD_SEND);
    do
    {
        iResult = recv(ConnectSocket, messaggio, sizeof(messaggio), 0);
    }
    while (iResult > 0);

    closesocket(ConnectSocket);
    WSACleanup();

    free(recvbuf);

    system("PAUSE");
    return 0;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37973097

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档