Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

637 lines
18 KiB

// based on SMTP spec RFC 821
#include "precomp.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <wbemutil.h>
#include <ArrTempl.h>
#include <ErrorObj.h>
#include <strsafe.h>
#define SMTP_PORT 25
#define MAX_SMTP_BUFFER 0x4000 // 16K
#define MAX_SUBJECT_LINE 2048 // leave room for the encoding
#define MAX_USER_NAME 512
#define MAX_EXTRA_HEADER 1024
#define SMTP_OKAY 250 // doc'd as 'Requested mail action okay, completed'
#define INTERNAL_ERROR 554 // sorta fit in with the SMTP error codes
// doc'd as 'Transaction Failed' by the SMTP spec
#define HH_MAX_COMPUTERNAME_LENGTH 256 // because the system defined MAX_COMPUTERNAME_LENGTH isn't
#define ERROR_MESSAGE_SIZE 256
// returns true if dwRet is an SMTP error
bool IsSMTPError(DWORD dwRet)
{
return (dwRet >= 400);
}
// Helper functions
void SkipWhite(PSTR *ppS )
{
PSTR pS = *ppS;
while ( *pS && isspace(*pS) )
++pS;
*ppS = pS;
}
void SkipNonWhite(PSTR *ppS )
{
PSTR pS = *ppS;
while ( *pS && !isspace(*pS) )
++pS;
*ppS = pS;
}
// returns numeric reply code
// in general, anything >= 400 is bad.
// copies & converts reply message to messageBuf
int SMTPReceive(int Socket, WCHAR* messageBuf)
{
int nCode;
char *szBuffer = new char[MAX_SMTP_BUFFER];
if (szBuffer == NULL)
return INTERNAL_ERROR;
szBuffer[0] = '\0';
int nLen = recv( Socket, szBuffer, MAX_SMTP_BUFFER, 0);
if (nLen == SOCKET_ERROR)
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPReceive failed: %d\n", err));
nCode = INTERNAL_ERROR;
}
else
{
szBuffer[nLen]=0;
if (1 != sscanf(szBuffer, " %d", &nCode))
nCode = INTERNAL_ERROR;
mbstowcs(messageBuf, szBuffer, ERROR_MESSAGE_SIZE);
messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
}
if (szBuffer)
delete szBuffer;
return nCode;
}
// returns numeric reply code
// in general, anything >= 400 is bad.
DWORD SMTPTransactMailCommand(SOCKET Socket, char *cCommand)
{
DWORD dwError;
if (cCommand == NULL)
return INTERNAL_ERROR;
if(SOCKET_ERROR == send(Socket, cCommand, strlen(cCommand), 0))
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPTransactMailCommand failed on command: %s (%d)\n", cCommand, err));
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
// so it gets truncated, it's long enough to tell what's happening.
WCHAR wcsCommand[256] = L"\0";
if (mbstowcs(wcsCommand, cCommand, 255) > 0)
{
wcsCommand[255] = L'\0';
if (wcslen(wcsCommand) >= 253)
StringCchCopyW(&wcsCommand[252], 4, L"...");
}
pErrorObj->ReportError(L"send", wcsCommand, NULL, err, true);
pErrorObj->Release();
}
dwError = INTERNAL_ERROR;
}
else
{
WCHAR messageBuf[ERROR_MESSAGE_SIZE +1] = L"\0";
dwError = SMTPReceive(Socket, messageBuf);
messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
if(IsSMTPError(dwError))
{
ERRORTRACE((LOG_ESS, "SMTP command \"%s\" returned %d (%S)\n", cCommand, dwError, messageBuf));
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
// so it gets truncated, it's long enough to tell what's happening.
WCHAR wcsCommand[256] = L"\0";
if (mbstowcs(wcsCommand, cCommand, 255) > 0)
{
wcsCommand[255] = L'\0';
if (wcslen(wcsCommand) >= 253)
StringCchCopy(&wcsCommand[252], 4, L"...");
}
pErrorObj->ReportError(wcsCommand, NULL, messageBuf, dwError, false);
pErrorObj->Release();
}
}
}
return dwError;
}
// returns 0 on success
// don't bother trying to make sense of the number otherwise
int SMTPConnect(char* szHost, SOCKET* pSocket)
{
SOCKET Socket = INVALID_SOCKET;
struct sockaddr_in Address;
struct hostent * HostEntry;
int Error = -1;
BOOL fSt = FALSE;
TCHAR cComputer[HH_MAX_COMPUTERNAME_LENGTH +2];
DWORD dwBuffSize= HH_MAX_COMPUTERNAME_LENGTH +1;
Socket = socket(AF_INET, SOCK_STREAM, 0);
if (Socket == INVALID_SOCKET)
{
// fprintf(stderr, "Error creating socket = %d\n", GetLastError());
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "Error creating socket = %d\n", err));
fSt = FALSE;
goto ex;
}
Address.sin_family = AF_INET;
Address.sin_port = 0;
Address.sin_addr.s_addr = INADDR_ANY;
Error =
bind(
Socket,
(struct sockaddr *) &Address,
sizeof(Address));
if (Error)
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "bind failed = %d\n", err));
fSt = FALSE;
goto ex;
}
Address.sin_family = AF_INET;
Address.sin_port = htons(SMTP_PORT);
HostEntry = gethostbyname(szHost);
if (HostEntry == NULL)
{
Error = WSAGetLastError();
ERRORTRACE((LOG_ESS, "unable to resolve host %s error = %d\n", szHost, Error));
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
WCHAR wcsHost[256] = L"\0";
if (mbstowcs(wcsHost, szHost, 255) > 0)
{
wcsHost[255] = L'\0';
if (wcslen(wcsHost) >= 253)
StringCchCopy(&wcsHost[252], 4, L"...");
}
pErrorObj->ReportError(L"gethostbyname", wcsHost, NULL, Error, true);
pErrorObj->Release();
}
fSt = FALSE;
goto ex;
}
else
{
Address.sin_addr.s_addr = *((unsigned long *) HostEntry->h_addr);
}
Error =
connect(
Socket,
(struct sockaddr *) &Address,
sizeof(Address));
if (Error)
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "Error connecting to %s = %d\n", szHost, err));
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
WCHAR wcsHost[256] = L"\0";
if (mbstowcs(wcsHost, szHost, 255) > 0)
{
wcsHost[255] = L'\0';
if (wcslen(wcsHost) >= 253)
StringCchCopy(&wcsHost[252], 4, L"...");
}
pErrorObj->ReportError(L"connect", wcsHost, NULL, err, true);
pErrorObj->Release();
}
fSt = FALSE;
goto ex;
}
WCHAR messageBuf[ERROR_MESSAGE_SIZE +1] = L"\0";
Error = SMTPReceive(Socket, messageBuf);
messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
if (!IsSMTPError(Error))
{
if (!GetComputerName(cComputer,&dwBuffSize))
{
ERRORTRACE((LOG_ESS, "GetComputerName failed! 0x%08X\n", GetLastError()));
return -1;
}
const int BufLen = 2*HH_MAX_COMPUTERNAME_LENGTH + 20;
char cMailCommand[BufLen];
StringCchCopyA(cMailCommand, BufLen, "HELO ");
#ifdef _UNICODE
char narrowcComputer[BufLen];
if (FAILED(StringCchPrintfA(narrowcComputer, BufLen, "%S", cComputer)))
return -1;
if (FAILED(StringCchCatA(cMailCommand, BufLen, narrowcComputer)))
return -1;
#else
if (FAILED(StringCchCatA(cMailCommand, BufLen, cComputer)))
return -1;
#endif
if (FAILED(StringCchCatA(cMailCommand, BufLen, "\r\n")))
return -1;
Error=SMTPTransactMailCommand(Socket, cMailCommand);
if(IsSMTPError(Error))
{
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
// so it gets truncated, it's long enough to tell what's happening.
WCHAR wcsCommand[256] = L"\0";
if (mbstowcs(wcsCommand, cMailCommand, 255) > 0)
{
wcsCommand[255] = L'\0';
if (wcslen(wcsCommand) >= 253)
StringCchCopyW(&wcsCommand[252], 4, L"...");
}
pErrorObj->ReportError(wcsCommand, NULL, messageBuf, Error, false);
pErrorObj->Release();
}
}
fSt = TRUE;
}
else
{
fSt = FALSE;
ERRORTRACE((LOG_ESS, "Error establishing SMTP connection (%d) (%S)\n", Error, messageBuf));
ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
if (pErrorObj)
{
pErrorObj->ReportError(L"connect", NULL, messageBuf, Error, false);
pErrorObj->Release();
}
}
ex:
if(!fSt)
{
closesocket(Socket);
return Error;
}
else
{
*pSocket = Socket;
return NOERROR;
}
}
// returns SMTP reply code
DWORD SMTPDisconnect(SOCKET Socket)
{
DWORD dwError = SMTP_OKAY;
if (Socket != INVALID_SOCKET)
{
dwError=SMTPTransactMailCommand(Socket, "QUIT\r\n");
closesocket(Socket);
};
return(dwError);
}
LPSTR Months[] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
LPSTR Days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
void GetTimeString(char *cTimeString,DWORD dwMaxSize)
{
SYSTEMTIME st;
GetSystemTime(&st);
StringCchPrintfA( cTimeString, dwMaxSize, "%s, %02d %s %4d %02d:%02d:%02d GMT",
Days[ st.wDayOfWeek ], st.wDay, Months[ st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
}
// returns 0 upon success
// inputs may be zero length, but may not be NULL.
DWORD SMTPSendMailHeader(SOCKET Socket, char* szTo, char* szCc, char* szFrom, char* szSender, char* szReplyTo, char* szSubject, char* szHeaders)
{
// dang gone it! they went & made 'template' a keyword...
// const char templitt[] = "Date: %s\r\nTo: %s\r\nCc: %s\r\nFrom: %s via WMI auto-mailer\r\nReply-To: %s\r\nSubject: %s";
const char templitt[] = "Date: %s\r\nTo: %s\r\nCc: %s\r\nFrom: <%s>\r\nSender: <%s>\r\nReply-To: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"- - =_HH_= - ---\"";
int bufLength = strlen(templitt) +strlen(szTo) +strlen(szCc)
+strlen(szFrom) +strlen(szSender) +strlen(szReplyTo)
+strlen(szSubject) +strlen(szHeaders)
+104; // to account for date string and for the trailing \r\n\r\n, and alittle fudge factor
char* pszOutputBuffer = new char[bufLength];
if (!pszOutputBuffer)
return INTERNAL_ERROR;
else
{
CDeleteMe<char> delbuf(pszOutputBuffer);
char cTimeString[100];
GetTimeString(cTimeString,100);
StringCchPrintfA(pszOutputBuffer, bufLength, templitt, cTimeString, szTo, szCc, szFrom, szSender, szReplyTo, szSubject);
if (strlen(szHeaders))
{
StringCchCatA(pszOutputBuffer, bufLength, "\r\n");
StringCchCatA(pszOutputBuffer, bufLength, szHeaders);
}
StringCchCatA(pszOutputBuffer, bufLength, "\r\n\r\n");
if (SOCKET_ERROR == send(Socket, pszOutputBuffer, strlen(pszOutputBuffer), 0))
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPSendMailHeader failed (%d)\n", err));
return INTERNAL_ERROR;
}
else
{
return(NO_ERROR);
}
}
}
// returns 0 on success
// but ignores error returns from server (server returns error if it must relay recipients.
DWORD SMTPSendRecipients(SOCKET Socket, LPCSTR szRecipients)
{
char *tok;
DWORD dwError = SMTP_OKAY;
int bufLength = strlen(szRecipients) +1;
char* pParseBuffer = new char[bufLength];
if (!pParseBuffer)
dwError = INTERNAL_ERROR;
else
{
char szBuffer[1024];
CDeleteMe<char> delbuf(pParseBuffer);
StringCchCopyA(pParseBuffer, bufLength, szRecipients);
tok=strtok(pParseBuffer," ;,");
while (tok!=NULL)
{
DWORD dwErrInternal;
if (FAILED(StringCchPrintfA(szBuffer, 1024, "RCPT TO:<%s>\r\n",tok)))
return INTERNAL_ERROR;
dwErrInternal=SMTPTransactMailCommand(Socket, szBuffer);
if(IsSMTPError(dwErrInternal))
{
if ((dwErrInternal == 550) || (dwErrInternal == 551))
ERRORTRACE((LOG_ESS, "Ignoring RCPT Error, will attempt to send mail.\n"));
else
{
dwError = dwErrInternal;
break;
}
}
tok=strtok(NULL," ;,");
}
}
return dwError;
}
// returns 0 on success, SMTP numeric reply code otherwise
DWORD SMTPSendText(SOCKET Socket, char* szTo, char* szCc, char* szFrom, char* szSender, char* szReplyTo, char* szSubject,
char* szHeaders, char *szText)
{
DWORD dwError;
int nLen;
int nSizeToSend;
char *tmp;
dwError=SMTPTransactMailCommand(Socket, "DATA\r\n");
if (IsSMTPError(dwError))
return dwError;
dwError=SMTPSendMailHeader(Socket, szTo, szCc, szFrom, szSender, szReplyTo, szSubject, szHeaders);
if (IsSMTPError(dwError))
return dwError;
char headerFake[] = "\r\n--- - =_HH_= - ---\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nContent-Transfer-Encoding: 7bit\r\n\r\n";
if (SOCKET_ERROR == send(Socket, headerFake, strlen(headerFake), 0))
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
return INTERNAL_ERROR;
}
nLen=strlen(szText);
tmp=szText;
while (nLen>0)
{
nSizeToSend=min(1000,nLen);
if (SOCKET_ERROR == send(Socket, tmp, nSizeToSend, 0))
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
return INTERNAL_ERROR;
}
nLen-=nSizeToSend;
tmp+=nSizeToSend;
}
char trailerFake[] = "\r\n\r\n--- - =_HH_= - -----\r\n\r\n";
if (SOCKET_ERROR == send(Socket, trailerFake, strlen(trailerFake), 0))
{
int err = WSAGetLastError();
ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
return INTERNAL_ERROR;
}
dwError=SMTPTransactMailCommand(Socket, "\r\n.\r\n");
return dwError;
}
// uses SMTP verification to determine whether
// recipient actually exists (is known to this server)
// returns return value from SMTPTransactMailCommand
//-->> will return success code if passed a NULL buffer
DWORD CheckRecipients(SOCKET Socket2me, char* szRecipients)
{
DWORD dwError = SMTP_OKAY;
int bufLength = strlen(szRecipients) +1;
char* pParseBuffer = new char[bufLength];
if (!pParseBuffer)
dwError = INTERNAL_ERROR;
else
{
CDeleteMe<char> delbuf(pParseBuffer);
char *tok;
char szBuffer[1024];
StringCchCopyA(pParseBuffer, bufLength, szRecipients);
tok=strtok(pParseBuffer," ;,");
while (tok!=NULL)
{
if (FAILED(StringCchPrintfA(szBuffer, 1024, "VRFY %s\r\n",tok)))
return INTERNAL_ERROR;
dwError=SMTPTransactMailCommand(Socket2me, szBuffer);
if(IsSMTPError(dwError))
break;
tok=strtok(NULL," ;,");
}
}
// hack to disable error returns
// some servers don't handle VRFY
dwError = SMTP_OKAY;
return dwError;
}
// returns zero upon success.
DWORD SMTPSend(char* szServer, char* szTo, char* szCc, char* szBcc, char* szFrom, char* szSender,
char* szReplyTo, char* szSubject, char* szHeaders, char *szText)
{
DWORD dwError = -1;
SOCKET Socket = INVALID_SOCKET;
char szFromBuffer[1024];
if (FAILED(StringCchPrintfA(szFromBuffer, 1024, "MAIL FROM: <%s>\r\n",szFrom)))
return INTERNAL_ERROR;
// each of the functions below do their own error reporting to the log
if ( (0 == SMTPConnect(szServer, &Socket)) &&
!IsSMTPError(CheckRecipients(Socket, szTo)) &&
!IsSMTPError(CheckRecipients(Socket, szCc)) &&
!IsSMTPError(CheckRecipients(Socket, szBcc)) &&
!IsSMTPError(SMTPTransactMailCommand(Socket, szFromBuffer)) &&
!IsSMTPError(SMTPSendRecipients(Socket, szTo)) &&
!IsSMTPError(SMTPSendRecipients(Socket, szCc)) &&
!IsSMTPError(SMTPSendRecipients(Socket, szBcc)) &&
!IsSMTPError(SMTPSendText(Socket, szTo, szCc, szFrom, szSender, szReplyTo, szSubject, szHeaders, szText)) )
{
dwError = 0;
}
DWORD dwDebugError;
dwDebugError=SMTPDisconnect(Socket);
if(IsSMTPError(dwDebugError))
{
// If disconnect failed log a message, but don't interfere with
// operation
ERRORTRACE((LOG_ESS, "SMTPDisconnect returned %d\n", dwDebugError));
}
return dwError;
}
// test harness
//void main()
//{
// WSADATA WsaData;
// int Error = WSAStartup (0x101, &WsaData);
//
// if (Error == SOCKET_ERROR)
// {
// fprintf(stderr, "Error in WSAStartup = %d\n", GetLastError());
// return;
// }
//
// SMTPSend("smarthost", "[email protected]", "[email protected]", "subject", "Text");
// /*
// SOCKET Socket;
// SMTPConnect("smarthost", &Socket);
// SMTPDisconnect(Socket);
// */
//}