我试图创建一个小C程序,允许我通过smtp与邮件服务器对话,我创建了这个小程序:
#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:
好吧,多亏了雷米·莱博,我显然解决了这些问题,希望这是最后一个问题。
编辑代码:
#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 ( auth问题的代码)必须首先发出一个STARTTLS命令。
有办法解决这个问题吗?我必须对我的发送命令进行编码,有什么方法可以用winsock来完成吗?
我几乎明白问题的意义了,还是这不是问题?
发布于 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,就有很多关于这个主题的书籍和教程。
所有这些都说了,尝试更像这样的东西:
#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;
}https://stackoverflow.com/questions/37973097
复制相似问题