gpt4 book ai didi

c - 尝试使用 C 程序通过 SMTP 与电子邮件服务器通信

转载 作者:太空宇宙 更新时间:2023-11-04 04:29:34 24 4
gpt4 key购买 nike

我正在尝试创建一个允许我通过 smtp 与邮件服务器通信的小 C 程序,我创建了这个小程序:

#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:

好的,感谢 Remy Lebeau,我显然设法解决了这些问题,希望这是最后一个。

编辑代码:

#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;
}

现在它确实接受了我的命令并且它确实响应了,我做 helo , mail from: <mail>to: <mail>此时它说:“530(身份验证问题代码)必须先发出 STARTTLS 命令。

有办法解决吗?我必须对我发送的命令进行编码,有没有办法用 winsock 做到这一点?

我是不是几乎明白了问题的要点,还是这不是问题所在?

最佳答案

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

TCP 是一个字节流。无法保证您能够在单个 send() 调用中发送整个命令,或者您能够在单个 recv()< 中接收到整个响应 调用。你必须继续发送,直到你没有什么可发送的,你必须继续阅读,直到没有什么可读的。对于 SMTP,这意味着当您遇到终止每行的 \r\n 时。

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

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

您可以使用像 OpenSSL 这样的库,或类似 Microsoft 的 API SChannel , 在套接字之上实现实际的 SSL/TLS。这超出了这个问题的范围。如果您在实现 SSL/TLS 方面需要帮助,有很多关于该主题的书籍和教程。

综上所述,请尝试更像这样的方法:

#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;
}

关于c - 尝试使用 C 程序通过 SMTP 与电子邮件服务器通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37973097/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com