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

  1. // based on SMTP spec RFC 821
  2. #include "precomp.h"
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <tchar.h>
  8. #include <io.h>
  9. #include <fcntl.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <wbemutil.h>
  13. #include <ArrTempl.h>
  14. #include <ErrorObj.h>
  15. #include <strsafe.h>
  16. #define SMTP_PORT 25
  17. #define MAX_SMTP_BUFFER 0x4000 // 16K
  18. #define MAX_SUBJECT_LINE 2048 // leave room for the encoding
  19. #define MAX_USER_NAME 512
  20. #define MAX_EXTRA_HEADER 1024
  21. #define SMTP_OKAY 250 // doc'd as 'Requested mail action okay, completed'
  22. #define INTERNAL_ERROR 554 // sorta fit in with the SMTP error codes
  23. // doc'd as 'Transaction Failed' by the SMTP spec
  24. #define HH_MAX_COMPUTERNAME_LENGTH 256 // because the system defined MAX_COMPUTERNAME_LENGTH isn't
  25. #define ERROR_MESSAGE_SIZE 256
  26. // returns true if dwRet is an SMTP error
  27. bool IsSMTPError(DWORD dwRet)
  28. {
  29. return (dwRet >= 400);
  30. }
  31. // Helper functions
  32. void SkipWhite(PSTR *ppS )
  33. {
  34. PSTR pS = *ppS;
  35. while ( *pS && isspace(*pS) )
  36. ++pS;
  37. *ppS = pS;
  38. }
  39. void SkipNonWhite(PSTR *ppS )
  40. {
  41. PSTR pS = *ppS;
  42. while ( *pS && !isspace(*pS) )
  43. ++pS;
  44. *ppS = pS;
  45. }
  46. // returns numeric reply code
  47. // in general, anything >= 400 is bad.
  48. // copies & converts reply message to messageBuf
  49. int SMTPReceive(int Socket, WCHAR* messageBuf)
  50. {
  51. int nCode;
  52. char *szBuffer = new char[MAX_SMTP_BUFFER];
  53. if (szBuffer == NULL)
  54. return INTERNAL_ERROR;
  55. szBuffer[0] = '\0';
  56. int nLen = recv( Socket, szBuffer, MAX_SMTP_BUFFER, 0);
  57. if (nLen == SOCKET_ERROR)
  58. {
  59. int err = WSAGetLastError();
  60. ERRORTRACE((LOG_ESS, "SMTPReceive failed: %d\n", err));
  61. nCode = INTERNAL_ERROR;
  62. }
  63. else
  64. {
  65. szBuffer[nLen]=0;
  66. if (1 != sscanf(szBuffer, " %d", &nCode))
  67. nCode = INTERNAL_ERROR;
  68. mbstowcs(messageBuf, szBuffer, ERROR_MESSAGE_SIZE);
  69. messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
  70. }
  71. if (szBuffer)
  72. delete szBuffer;
  73. return nCode;
  74. }
  75. // returns numeric reply code
  76. // in general, anything >= 400 is bad.
  77. DWORD SMTPTransactMailCommand(SOCKET Socket, char *cCommand)
  78. {
  79. DWORD dwError;
  80. if (cCommand == NULL)
  81. return INTERNAL_ERROR;
  82. if(SOCKET_ERROR == send(Socket, cCommand, strlen(cCommand), 0))
  83. {
  84. int err = WSAGetLastError();
  85. ERRORTRACE((LOG_ESS, "SMTPTransactMailCommand failed on command: %s (%d)\n", cCommand, err));
  86. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  87. if (pErrorObj)
  88. {
  89. // so it gets truncated, it's long enough to tell what's happening.
  90. WCHAR wcsCommand[256] = L"\0";
  91. if (mbstowcs(wcsCommand, cCommand, 255) > 0)
  92. {
  93. wcsCommand[255] = L'\0';
  94. if (wcslen(wcsCommand) >= 253)
  95. StringCchCopyW(&wcsCommand[252], 4, L"...");
  96. }
  97. pErrorObj->ReportError(L"send", wcsCommand, NULL, err, true);
  98. pErrorObj->Release();
  99. }
  100. dwError = INTERNAL_ERROR;
  101. }
  102. else
  103. {
  104. WCHAR messageBuf[ERROR_MESSAGE_SIZE +1] = L"\0";
  105. dwError = SMTPReceive(Socket, messageBuf);
  106. messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
  107. if(IsSMTPError(dwError))
  108. {
  109. ERRORTRACE((LOG_ESS, "SMTP command \"%s\" returned %d (%S)\n", cCommand, dwError, messageBuf));
  110. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  111. if (pErrorObj)
  112. {
  113. // so it gets truncated, it's long enough to tell what's happening.
  114. WCHAR wcsCommand[256] = L"\0";
  115. if (mbstowcs(wcsCommand, cCommand, 255) > 0)
  116. {
  117. wcsCommand[255] = L'\0';
  118. if (wcslen(wcsCommand) >= 253)
  119. StringCchCopy(&wcsCommand[252], 4, L"...");
  120. }
  121. pErrorObj->ReportError(wcsCommand, NULL, messageBuf, dwError, false);
  122. pErrorObj->Release();
  123. }
  124. }
  125. }
  126. return dwError;
  127. }
  128. // returns 0 on success
  129. // don't bother trying to make sense of the number otherwise
  130. int SMTPConnect(char* szHost, SOCKET* pSocket)
  131. {
  132. SOCKET Socket = INVALID_SOCKET;
  133. struct sockaddr_in Address;
  134. struct hostent * HostEntry;
  135. int Error = -1;
  136. BOOL fSt = FALSE;
  137. TCHAR cComputer[HH_MAX_COMPUTERNAME_LENGTH +2];
  138. DWORD dwBuffSize= HH_MAX_COMPUTERNAME_LENGTH +1;
  139. Socket = socket(AF_INET, SOCK_STREAM, 0);
  140. if (Socket == INVALID_SOCKET)
  141. {
  142. // fprintf(stderr, "Error creating socket = %d\n", GetLastError());
  143. int err = WSAGetLastError();
  144. ERRORTRACE((LOG_ESS, "Error creating socket = %d\n", err));
  145. fSt = FALSE;
  146. goto ex;
  147. }
  148. Address.sin_family = AF_INET;
  149. Address.sin_port = 0;
  150. Address.sin_addr.s_addr = INADDR_ANY;
  151. Error =
  152. bind(
  153. Socket,
  154. (struct sockaddr *) &Address,
  155. sizeof(Address));
  156. if (Error)
  157. {
  158. int err = WSAGetLastError();
  159. ERRORTRACE((LOG_ESS, "bind failed = %d\n", err));
  160. fSt = FALSE;
  161. goto ex;
  162. }
  163. Address.sin_family = AF_INET;
  164. Address.sin_port = htons(SMTP_PORT);
  165. HostEntry = gethostbyname(szHost);
  166. if (HostEntry == NULL)
  167. {
  168. Error = WSAGetLastError();
  169. ERRORTRACE((LOG_ESS, "unable to resolve host %s error = %d\n", szHost, Error));
  170. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  171. if (pErrorObj)
  172. {
  173. WCHAR wcsHost[256] = L"\0";
  174. if (mbstowcs(wcsHost, szHost, 255) > 0)
  175. {
  176. wcsHost[255] = L'\0';
  177. if (wcslen(wcsHost) >= 253)
  178. StringCchCopy(&wcsHost[252], 4, L"...");
  179. }
  180. pErrorObj->ReportError(L"gethostbyname", wcsHost, NULL, Error, true);
  181. pErrorObj->Release();
  182. }
  183. fSt = FALSE;
  184. goto ex;
  185. }
  186. else
  187. {
  188. Address.sin_addr.s_addr = *((unsigned long *) HostEntry->h_addr);
  189. }
  190. Error =
  191. connect(
  192. Socket,
  193. (struct sockaddr *) &Address,
  194. sizeof(Address));
  195. if (Error)
  196. {
  197. int err = WSAGetLastError();
  198. ERRORTRACE((LOG_ESS, "Error connecting to %s = %d\n", szHost, err));
  199. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  200. if (pErrorObj)
  201. {
  202. WCHAR wcsHost[256] = L"\0";
  203. if (mbstowcs(wcsHost, szHost, 255) > 0)
  204. {
  205. wcsHost[255] = L'\0';
  206. if (wcslen(wcsHost) >= 253)
  207. StringCchCopy(&wcsHost[252], 4, L"...");
  208. }
  209. pErrorObj->ReportError(L"connect", wcsHost, NULL, err, true);
  210. pErrorObj->Release();
  211. }
  212. fSt = FALSE;
  213. goto ex;
  214. }
  215. WCHAR messageBuf[ERROR_MESSAGE_SIZE +1] = L"\0";
  216. Error = SMTPReceive(Socket, messageBuf);
  217. messageBuf[ERROR_MESSAGE_SIZE] = L'\0';
  218. if (!IsSMTPError(Error))
  219. {
  220. if (!GetComputerName(cComputer,&dwBuffSize))
  221. {
  222. ERRORTRACE((LOG_ESS, "GetComputerName failed! 0x%08X\n", GetLastError()));
  223. return -1;
  224. }
  225. const int BufLen = 2*HH_MAX_COMPUTERNAME_LENGTH + 20;
  226. char cMailCommand[BufLen];
  227. StringCchCopyA(cMailCommand, BufLen, "HELO ");
  228. #ifdef _UNICODE
  229. char narrowcComputer[BufLen];
  230. if (FAILED(StringCchPrintfA(narrowcComputer, BufLen, "%S", cComputer)))
  231. return -1;
  232. if (FAILED(StringCchCatA(cMailCommand, BufLen, narrowcComputer)))
  233. return -1;
  234. #else
  235. if (FAILED(StringCchCatA(cMailCommand, BufLen, cComputer)))
  236. return -1;
  237. #endif
  238. if (FAILED(StringCchCatA(cMailCommand, BufLen, "\r\n")))
  239. return -1;
  240. Error=SMTPTransactMailCommand(Socket, cMailCommand);
  241. if(IsSMTPError(Error))
  242. {
  243. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  244. if (pErrorObj)
  245. {
  246. // so it gets truncated, it's long enough to tell what's happening.
  247. WCHAR wcsCommand[256] = L"\0";
  248. if (mbstowcs(wcsCommand, cMailCommand, 255) > 0)
  249. {
  250. wcsCommand[255] = L'\0';
  251. if (wcslen(wcsCommand) >= 253)
  252. StringCchCopyW(&wcsCommand[252], 4, L"...");
  253. }
  254. pErrorObj->ReportError(wcsCommand, NULL, messageBuf, Error, false);
  255. pErrorObj->Release();
  256. }
  257. }
  258. fSt = TRUE;
  259. }
  260. else
  261. {
  262. fSt = FALSE;
  263. ERRORTRACE((LOG_ESS, "Error establishing SMTP connection (%d) (%S)\n", Error, messageBuf));
  264. ErrorObj* pErrorObj = ErrorObj::GetErrorObj();
  265. if (pErrorObj)
  266. {
  267. pErrorObj->ReportError(L"connect", NULL, messageBuf, Error, false);
  268. pErrorObj->Release();
  269. }
  270. }
  271. ex:
  272. if(!fSt)
  273. {
  274. closesocket(Socket);
  275. return Error;
  276. }
  277. else
  278. {
  279. *pSocket = Socket;
  280. return NOERROR;
  281. }
  282. }
  283. // returns SMTP reply code
  284. DWORD SMTPDisconnect(SOCKET Socket)
  285. {
  286. DWORD dwError = SMTP_OKAY;
  287. if (Socket != INVALID_SOCKET)
  288. {
  289. dwError=SMTPTransactMailCommand(Socket, "QUIT\r\n");
  290. closesocket(Socket);
  291. };
  292. return(dwError);
  293. }
  294. LPSTR Months[] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  295. LPSTR Days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  296. void GetTimeString(char *cTimeString,DWORD dwMaxSize)
  297. {
  298. SYSTEMTIME st;
  299. GetSystemTime(&st);
  300. StringCchPrintfA( cTimeString, dwMaxSize, "%s, %02d %s %4d %02d:%02d:%02d GMT",
  301. Days[ st.wDayOfWeek ], st.wDay, Months[ st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
  302. }
  303. // returns 0 upon success
  304. // inputs may be zero length, but may not be NULL.
  305. DWORD SMTPSendMailHeader(SOCKET Socket, char* szTo, char* szCc, char* szFrom, char* szSender, char* szReplyTo, char* szSubject, char* szHeaders)
  306. {
  307. // dang gone it! they went & made 'template' a keyword...
  308. // 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";
  309. 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_= - ---\"";
  310. int bufLength = strlen(templitt) +strlen(szTo) +strlen(szCc)
  311. +strlen(szFrom) +strlen(szSender) +strlen(szReplyTo)
  312. +strlen(szSubject) +strlen(szHeaders)
  313. +104; // to account for date string and for the trailing \r\n\r\n, and alittle fudge factor
  314. char* pszOutputBuffer = new char[bufLength];
  315. if (!pszOutputBuffer)
  316. return INTERNAL_ERROR;
  317. else
  318. {
  319. CDeleteMe<char> delbuf(pszOutputBuffer);
  320. char cTimeString[100];
  321. GetTimeString(cTimeString,100);
  322. StringCchPrintfA(pszOutputBuffer, bufLength, templitt, cTimeString, szTo, szCc, szFrom, szSender, szReplyTo, szSubject);
  323. if (strlen(szHeaders))
  324. {
  325. StringCchCatA(pszOutputBuffer, bufLength, "\r\n");
  326. StringCchCatA(pszOutputBuffer, bufLength, szHeaders);
  327. }
  328. StringCchCatA(pszOutputBuffer, bufLength, "\r\n\r\n");
  329. if (SOCKET_ERROR == send(Socket, pszOutputBuffer, strlen(pszOutputBuffer), 0))
  330. {
  331. int err = WSAGetLastError();
  332. ERRORTRACE((LOG_ESS, "SMTPSendMailHeader failed (%d)\n", err));
  333. return INTERNAL_ERROR;
  334. }
  335. else
  336. {
  337. return(NO_ERROR);
  338. }
  339. }
  340. }
  341. // returns 0 on success
  342. // but ignores error returns from server (server returns error if it must relay recipients.
  343. DWORD SMTPSendRecipients(SOCKET Socket, LPCSTR szRecipients)
  344. {
  345. char *tok;
  346. DWORD dwError = SMTP_OKAY;
  347. int bufLength = strlen(szRecipients) +1;
  348. char* pParseBuffer = new char[bufLength];
  349. if (!pParseBuffer)
  350. dwError = INTERNAL_ERROR;
  351. else
  352. {
  353. char szBuffer[1024];
  354. CDeleteMe<char> delbuf(pParseBuffer);
  355. StringCchCopyA(pParseBuffer, bufLength, szRecipients);
  356. tok=strtok(pParseBuffer," ;,");
  357. while (tok!=NULL)
  358. {
  359. DWORD dwErrInternal;
  360. if (FAILED(StringCchPrintfA(szBuffer, 1024, "RCPT TO:<%s>\r\n",tok)))
  361. return INTERNAL_ERROR;
  362. dwErrInternal=SMTPTransactMailCommand(Socket, szBuffer);
  363. if(IsSMTPError(dwErrInternal))
  364. {
  365. if ((dwErrInternal == 550) || (dwErrInternal == 551))
  366. ERRORTRACE((LOG_ESS, "Ignoring RCPT Error, will attempt to send mail.\n"));
  367. else
  368. {
  369. dwError = dwErrInternal;
  370. break;
  371. }
  372. }
  373. tok=strtok(NULL," ;,");
  374. }
  375. }
  376. return dwError;
  377. }
  378. // returns 0 on success, SMTP numeric reply code otherwise
  379. DWORD SMTPSendText(SOCKET Socket, char* szTo, char* szCc, char* szFrom, char* szSender, char* szReplyTo, char* szSubject,
  380. char* szHeaders, char *szText)
  381. {
  382. DWORD dwError;
  383. int nLen;
  384. int nSizeToSend;
  385. char *tmp;
  386. dwError=SMTPTransactMailCommand(Socket, "DATA\r\n");
  387. if (IsSMTPError(dwError))
  388. return dwError;
  389. dwError=SMTPSendMailHeader(Socket, szTo, szCc, szFrom, szSender, szReplyTo, szSubject, szHeaders);
  390. if (IsSMTPError(dwError))
  391. return dwError;
  392. char headerFake[] = "\r\n--- - =_HH_= - ---\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\nContent-Transfer-Encoding: 7bit\r\n\r\n";
  393. if (SOCKET_ERROR == send(Socket, headerFake, strlen(headerFake), 0))
  394. {
  395. int err = WSAGetLastError();
  396. ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
  397. return INTERNAL_ERROR;
  398. }
  399. nLen=strlen(szText);
  400. tmp=szText;
  401. while (nLen>0)
  402. {
  403. nSizeToSend=min(1000,nLen);
  404. if (SOCKET_ERROR == send(Socket, tmp, nSizeToSend, 0))
  405. {
  406. int err = WSAGetLastError();
  407. ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
  408. return INTERNAL_ERROR;
  409. }
  410. nLen-=nSizeToSend;
  411. tmp+=nSizeToSend;
  412. }
  413. char trailerFake[] = "\r\n\r\n--- - =_HH_= - -----\r\n\r\n";
  414. if (SOCKET_ERROR == send(Socket, trailerFake, strlen(trailerFake), 0))
  415. {
  416. int err = WSAGetLastError();
  417. ERRORTRACE((LOG_ESS, "SMTPSendText failed (%d)\n", err));
  418. return INTERNAL_ERROR;
  419. }
  420. dwError=SMTPTransactMailCommand(Socket, "\r\n.\r\n");
  421. return dwError;
  422. }
  423. // uses SMTP verification to determine whether
  424. // recipient actually exists (is known to this server)
  425. // returns return value from SMTPTransactMailCommand
  426. //-->> will return success code if passed a NULL buffer
  427. DWORD CheckRecipients(SOCKET Socket2me, char* szRecipients)
  428. {
  429. DWORD dwError = SMTP_OKAY;
  430. int bufLength = strlen(szRecipients) +1;
  431. char* pParseBuffer = new char[bufLength];
  432. if (!pParseBuffer)
  433. dwError = INTERNAL_ERROR;
  434. else
  435. {
  436. CDeleteMe<char> delbuf(pParseBuffer);
  437. char *tok;
  438. char szBuffer[1024];
  439. StringCchCopyA(pParseBuffer, bufLength, szRecipients);
  440. tok=strtok(pParseBuffer," ;,");
  441. while (tok!=NULL)
  442. {
  443. if (FAILED(StringCchPrintfA(szBuffer, 1024, "VRFY %s\r\n",tok)))
  444. return INTERNAL_ERROR;
  445. dwError=SMTPTransactMailCommand(Socket2me, szBuffer);
  446. if(IsSMTPError(dwError))
  447. break;
  448. tok=strtok(NULL," ;,");
  449. }
  450. }
  451. // hack to disable error returns
  452. // some servers don't handle VRFY
  453. dwError = SMTP_OKAY;
  454. return dwError;
  455. }
  456. // returns zero upon success.
  457. DWORD SMTPSend(char* szServer, char* szTo, char* szCc, char* szBcc, char* szFrom, char* szSender,
  458. char* szReplyTo, char* szSubject, char* szHeaders, char *szText)
  459. {
  460. DWORD dwError = -1;
  461. SOCKET Socket = INVALID_SOCKET;
  462. char szFromBuffer[1024];
  463. if (FAILED(StringCchPrintfA(szFromBuffer, 1024, "MAIL FROM: <%s>\r\n",szFrom)))
  464. return INTERNAL_ERROR;
  465. // each of the functions below do their own error reporting to the log
  466. if ( (0 == SMTPConnect(szServer, &Socket)) &&
  467. !IsSMTPError(CheckRecipients(Socket, szTo)) &&
  468. !IsSMTPError(CheckRecipients(Socket, szCc)) &&
  469. !IsSMTPError(CheckRecipients(Socket, szBcc)) &&
  470. !IsSMTPError(SMTPTransactMailCommand(Socket, szFromBuffer)) &&
  471. !IsSMTPError(SMTPSendRecipients(Socket, szTo)) &&
  472. !IsSMTPError(SMTPSendRecipients(Socket, szCc)) &&
  473. !IsSMTPError(SMTPSendRecipients(Socket, szBcc)) &&
  474. !IsSMTPError(SMTPSendText(Socket, szTo, szCc, szFrom, szSender, szReplyTo, szSubject, szHeaders, szText)) )
  475. {
  476. dwError = 0;
  477. }
  478. DWORD dwDebugError;
  479. dwDebugError=SMTPDisconnect(Socket);
  480. if(IsSMTPError(dwDebugError))
  481. {
  482. // If disconnect failed log a message, but don't interfere with
  483. // operation
  484. ERRORTRACE((LOG_ESS, "SMTPDisconnect returned %d\n", dwDebugError));
  485. }
  486. return dwError;
  487. }
  488. // test harness
  489. //void main()
  490. //{
  491. // WSADATA WsaData;
  492. // int Error = WSAStartup (0x101, &WsaData);
  493. //
  494. // if (Error == SOCKET_ERROR)
  495. // {
  496. // fprintf(stderr, "Error in WSAStartup = %d\n", GetLastError());
  497. // return;
  498. // }
  499. //
  500. // SMTPSend("smarthost", "[email protected]", "[email protected]", "subject", "Text");
  501. // /*
  502. // SOCKET Socket;
  503. // SMTPConnect("smarthost", &Socket);
  504. // SMTPDisconnect(Socket);
  505. // */
  506. //}