Advertisement
Ghostriax-Atrocity

SMTP.cpp

Apr 11th, 2015
261
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.00 KB | None | 0 0
  1. #include "stdafx.h"
  2. #include "SMTP.h"
  3.  
  4. bool SMTP::send()
  5. {
  6. std::string from = "MAIL FROM: <" + fromAddress.a + ">\r\n";
  7. sock->Send(from);
  8. if(!is_successful())
  9. {
  10. currentError = SENDER_NOT_ACCEPTED;
  11. return false;
  12. }
  13. currentStatus = SENDER_ACCEPTED;
  14.  
  15. // Send addresses
  16. if(!send_recipents(toAddress) || !send_recipents(ccAddress) || !send_recipents(bccAddress))
  17. {
  18. currentError = RECIPENTS_NOT_ACCEPTED;
  19. return false;
  20. }
  21. currentStatus = RECIPENTS_ACCEPTED;
  22.  
  23. // Start delivery
  24. sock->Send("DATA\r\n");
  25. if(!is_successful("354"))
  26. {
  27. currentError = DATA_NOT_ACCEPTED;
  28. // Fixme: More codes? 421 is from Mercury/Greywall
  29. if(0 == strcmp(get_reply_code(), "421"))
  30. {
  31. currentError = GREYLISTED;
  32. }
  33. return false;
  34. }
  35. currentStatus = DATA_ACCEPTED;
  36.  
  37. std::string envelopeHeader;
  38. build_envelope_header(envelopeHeader);
  39. sock->Send(envelopeHeader);
  40. sock->Send("\r\n");
  41.  
  42. std::string body;
  43. build_message_body(body);
  44. sock->Send(body);
  45. sock->Send("\r\n.\r\n");
  46.  
  47. // 451 (Or just 4XX?) == Greylisting
  48. // "451 Temporary local problem, please try again!"
  49. // TODO: Decide whether content blacklisting or Greylisting? am I even informed in case of blacklisting
  50. bool b = is_successful();
  51. if(b)
  52. currentStatus = DELIVERY_SUCCESSFUL;
  53. return b;
  54. }
  55.  
  56. void SMTP::add_attachment(std::string data, std::string name, std::string type)
  57. {
  58. add_attachment((const unsigned char*)data.c_str(), data.length(), name, type);
  59. }
  60.  
  61. void SMTP::add_attachment(const unsigned char* data, size_t len, std::string name, std::string type)
  62. {
  63. Attachment a;
  64.  
  65. append_base64(a.data, data, len, false);
  66. a.name = name;
  67. a.type = type;
  68.  
  69. attachments.push_back(a);
  70. }
  71.  
  72. std::string SMTP::get_boundary(std::string seed)
  73. {
  74. std::string boundary = "";
  75.  
  76. while(boundary.length() < 24)
  77. {
  78. int n = (seed.at(boundary.length() % seed.length()) ^ 'a') % (boundary.length() % 2 ? 10 : 100);
  79. add_int_to_string(boundary, n);
  80. }
  81. return boundary;
  82. }
  83.  
  84. void SMTP::build_message_body(std::string& body)
  85. {
  86. if(0 == attachments.size() && 0 == htmlMessage.length())
  87. {
  88. body = message;
  89. return;
  90. }
  91.  
  92. std::string mainBoundary = get_boundary(message);
  93. std::string htmlBoundary = get_boundary(htmlMessage);
  94.  
  95. body = "This is a multi-part message in MIME format.\r\n\r\n";
  96. body += "--" + mainBoundary + "\r\n";
  97. if(attachments.size())
  98. {
  99. body += "Content-Type: multipart/alternative;\r\n"
  100. " boundary=\"" + htmlBoundary + "\"\r\n\r\n";
  101. body += "--" + htmlBoundary + "\r\n";
  102. }
  103. // Increasing order of preference
  104. body += std::string("Content-Type: text/plain\r\n") +
  105. std::string("Content-Transfer-Encoding: 7bit\r\n");
  106. body += "\r\n";
  107. body += message;
  108. body += "\r\n";
  109. if(htmlMessage.size())
  110. {
  111. body += "--" + (attachments.size() ? htmlBoundary : mainBoundary) + std::string("\r\n");
  112. body += std::string("Content-Type: text/html\r\n") +
  113. std::string("Content-Transfer-Encoding: 7bit\r\n\r\n");
  114.  
  115. body += htmlMessage + std::string("\r\n");
  116. body += "--" + (attachments.size() ? htmlBoundary : mainBoundary) + std::string("--\r\n");
  117. }
  118.  
  119.  
  120. for(std::vector<Attachment>::iterator i = attachments.begin(); i != attachments.end(); i++)
  121. {
  122. body += "--" + mainBoundary + "\r\n";
  123. body += "Content-Type: " + i->type + ";\r\n";
  124. body += " name=\"" + i->name + "\"\r\n";
  125. body += "Content-Transfer-Encoding: base64\r\n"
  126. "Content-Disposition: attachment;\r\n";
  127. body += " filename=\"" + i->name + "\"\r\n";
  128.  
  129. body += "\r\n";
  130. body += i->data;
  131. body += "\r\n";
  132. }
  133.  
  134. body += "--" + mainBoundary + std::string("--");
  135. }
  136.  
  137. void SMTP::build_envelope_header(std::string& header)
  138. {
  139. //FIXME: Message ID
  140.  
  141. std::string date;
  142. get_date(date);
  143. header += std::string("Date: ") + date + std::string("\r\n");
  144.  
  145. std::string envelopeFrom = "From: ";
  146. format_address(fromAddress, envelopeFrom);
  147. envelopeFrom += "\r\n";
  148. header += envelopeFrom;
  149.  
  150. add_envelope_recipent("To: ", toAddress, header);
  151. add_envelope_recipent("CC: ", ccAddress, header);
  152.  
  153. std::string envelopSubject = "Subject: " + subject + "\r\n";
  154. header += envelopSubject;
  155.  
  156. // If there are attachments always use this header
  157. if(attachments.size() > 0)
  158. {
  159. header += "MIME-Version: 1.0\r\n";
  160. header += "Content-Type: multipart/mixed;\r\n"
  161. " boundary=\"" + get_boundary(message) + "\"\r\n";
  162. }
  163. else if(htmlMessage.size())
  164. {
  165. header += "MIME-Version: 1.0\r\n";
  166. header += "Content-Type: multipart/alternative;\r\n"
  167. " boundary=\"" + get_boundary(message) + "\"\r\n";
  168. }
  169.  
  170. if(returnAddress.a.length())
  171. {
  172. header += "Return-Path: ";
  173. format_address(returnAddress, header);
  174. header += "\r\n";
  175. }
  176.  
  177. }
  178.  
  179. void SMTP::format_address(Address& a, std::string& res)
  180. {
  181. if(a.friendlyName.length())
  182. res += a.friendlyName + std::string(" <") + a.a + std::string(">");
  183. else
  184. res += a.a;
  185. }
  186.  
  187. bool SMTP::get_supported_functions()
  188. {
  189. bool lastFunction = false;
  190. bool successReply = false;
  191.  
  192. while(!lastFunction)
  193. {
  194. recv_reply();
  195.  
  196. // If the server sent all functions in one go this will probably contain them all
  197. std::vector<std::string> temp;
  198. string_explode(buffer, "\r\n", temp);
  199.  
  200. for(std::vector<std::string>::iterator i = temp.begin(); i != temp.end(); i++)
  201. {
  202. // Set status code
  203. successReply = ('2' == i->at(0));
  204.  
  205. // Skip status code (3 chars) and " " or "-"
  206. std::string function = i->substr(4);
  207. supportedFunctions.push_back(function);
  208.  
  209. // Is last feature?
  210. lastFunction = (' ' == i->at(3));
  211. }
  212. }
  213.  
  214. return successReply;
  215. }
  216.  
  217. void SMTP::get_date(std::string& res)
  218. {
  219. const char* szWeekDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  220. const char* szMonths[] = {NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  221.  
  222. SYSTEMTIME systemTime = {0};
  223. GetLocalTime(&systemTime);
  224.  
  225. // Date in format "Weekday, Day Month Year "
  226. res += std::string(szWeekDays[systemTime.wDayOfWeek]) + ", ";
  227. add_int_to_string(res, systemTime.wDay);
  228. res += " ";
  229. res += std::string(szMonths[systemTime.wMonth]) + " ";
  230. add_int_to_string(res, systemTime.wYear);
  231. res += " ";
  232.  
  233. // "Hour:Minute:Second"
  234. add_int_to_string(res, systemTime.wHour);
  235. res += ":";
  236. add_int_to_string(res, systemTime.wMinute);
  237. res += ":";
  238. add_int_to_string(res, systemTime.wSecond);
  239.  
  240. TIME_ZONE_INFORMATION timezoneinfo;
  241. GetTimeZoneInformation(&timezoneinfo);
  242.  
  243. int h = (-timezoneinfo.Bias / 60), m = (timezoneinfo.Bias % 60);
  244. if(h > 0)
  245. m = -m;
  246. char dateOffset[6] = {0};
  247. sprintf_s(dateOffset, 6, "%+03i%02u", h, m);
  248. res += dateOffset;
  249. }
  250.  
  251. bool SMTP::send_recipents(std::vector<Address>& addresses)
  252. {
  253. for(std::vector<Address>::iterator i = addresses.begin(); i != addresses.end(); i++)
  254. {
  255. std::string address = std::string("RCPT TO: <") + i->a + std::string(">\r\n");
  256.  
  257. sock->Send(address);
  258. if(!is_successful())
  259. return false;
  260. }
  261.  
  262. return TRUE;
  263. }
  264.  
  265. void SMTP::add_envelope_recipent( std::string recipentType, std::vector<Address>& addresses, std::string& res)
  266. {
  267. if(!addresses.size())
  268. return;
  269.  
  270. std::string envelopeRecipents = recipentType;
  271.  
  272. for(std::vector<Address>::iterator i = addresses.begin(); i != addresses.end(); i++)
  273. {
  274. format_address(*i, envelopeRecipents);
  275. envelopeRecipents += std::string(",");
  276. }
  277.  
  278. envelopeRecipents = envelopeRecipents.substr(0, envelopeRecipents.length() - 1);
  279. envelopeRecipents += "\r\n";
  280.  
  281. res += envelopeRecipents;
  282. }
  283.  
  284. VOID SMTP::recv_reply()
  285. {
  286. #if 0
  287. memset(buffer, 0, sizeof(buffer));
  288. sock->RecvLine(buffer, length, sizeof(buffer));
  289. #else
  290. length = sizeof(buffer);
  291. memset(buffer, 0, sizeof(buffer));
  292. sock->Recv(buffer, length);
  293. #endif
  294. }
  295.  
  296. VOID SMTP::send_NOOP()
  297. {
  298. sock->Send("NOOP\r\n", strlen("NOOP\r\n"));
  299. recv_reply();
  300. }
  301.  
  302. VOID SMTP::add_address( Address a, AddressType type )
  303. {
  304. switch(type)
  305. {
  306. case RETURN:
  307. returnAddress = a;
  308. break;
  309. case FROM:
  310. fromAddress = a;
  311. break;
  312. case BCC:
  313. bccAddress.push_back(a);
  314. break;
  315. case CC:
  316. ccAddress.push_back(a);
  317. break;
  318. case TO:
  319. default:
  320. toAddress.push_back(a);
  321. break;
  322. }
  323. }
  324.  
  325. bool SMTP::is_successful()
  326. {
  327. recv_reply();
  328. if(*get_reply_code() != '2')
  329. return false;
  330. return true;
  331. }
  332.  
  333. bool SMTP::is_successful(char* successStatusCode)
  334. {
  335. recv_reply();
  336. return 0 == strcmp(get_reply_code(), successStatusCode);
  337. }
  338.  
  339. bool SMTP::supports_feature(std::string feature)
  340. {
  341. for(std::vector<std::string>::iterator i = supportedFunctions.begin(); i != supportedFunctions.end(); i++)
  342. {
  343. if(i->find(feature) != std::string::npos)
  344. return true;
  345. }
  346. return false;
  347. }
  348.  
  349. // Crypto & Auth
  350. bool SMTP::switch_to_TLS()
  351. {
  352. if(!is_TLS_possible())
  353. return false;
  354.  
  355. sock->Send("STARTTLS\r\n");
  356. if(!is_successful("220"))
  357. {
  358. currentError = SSL_ERROR_SERVER_NOT_READY;
  359. puts(buffer);
  360. return false;
  361. }
  362.  
  363. // Save all mandatory information about the connection
  364. SOCKET s = sock->GetSocket();
  365. // Delete the old NormalSocket
  366. //FIXME: delete sock;
  367.  
  368. //
  369. SSLSocket* ssls = new SSLSocket();
  370. sock = ssls;
  371. ssls->Disconnect(); // free newly created socket
  372. sock->SetSocket(s);
  373.  
  374. if(!ssls->StartSSL(server.c_str()))
  375. {
  376. currentError = SSL_ERROR_CONNECT;
  377. puts(__FUNCTION__ " Cannot connect");
  378. return false;
  379. }
  380.  
  381. // RFC dictates that both sides must discard any information already obtained
  382. supportedFunctions.clear();
  383.  
  384. std::string EHLO = "EHLO " + ehloName + "\r\n";
  385. sock->Send(EHLO);
  386.  
  387. // Read in second banner
  388. // This response contains both banner and commands -> Read only banner (first line)
  389. sock->RecvLine(buffer, length, _countof(buffer));
  390. connected = ('2' == buffer[0]);
  391. if(!connected)
  392. {
  393. currentError = SSL_HELO_NOT_ACCEPTED;
  394. return false;
  395. }
  396. currentStatus = BANNER_RECVIVED;
  397.  
  398. usingSSL = true;
  399.  
  400. if(get_supported_functions())
  401. currentStatus = EHLO_ACCEPTED;
  402. return connected;
  403. }
  404.  
  405. bool SMTP::is_login_possible()
  406. {
  407. return supports_feature("LOGIN") || supports_feature("AUTH");
  408. }
  409.  
  410. bool SMTP::is_TLS_possible()
  411. {
  412. return supports_feature("STARTTLS");
  413. }
  414.  
  415. bool SMTP::authenticate(std::string user, std::string plainPassword)
  416. {
  417. if((user.empty() && plainPassword.empty()) || !supports_feature("LOGIN"))
  418. return TRUE;
  419.  
  420. // Send user
  421. std::string authLoginWithUser = "AUTH LOGIN ";
  422. append_base64(authLoginWithUser, (const unsigned char*)user.c_str(), user.length(), true);
  423. authLoginWithUser.append("\r\n");
  424.  
  425. sock->Send(authLoginWithUser);
  426.  
  427. // Did anything go wrong?
  428. if(!is_successful("334"))
  429. {
  430. currentError = LOGIN_NOT_POSSIBLE;
  431. return false;
  432. }
  433.  
  434. std::string base64Pass = "";
  435. append_base64(base64Pass, (const unsigned char*)plainPassword.c_str(), plainPassword.length(), false);
  436. base64Pass.append("\r\n");
  437.  
  438. sock->Send(base64Pass);
  439.  
  440. // Did anything go wrong?
  441. if(!is_successful())
  442. {
  443. currentError = CREDENTIALS_WRONG;
  444. return false;
  445. }
  446.  
  447. authenticated = true;
  448. return true;
  449. }
  450.  
  451. // Initalization
  452. SMTP::SMTP()
  453. {
  454. length = 0;
  455. connected = false; // Actually having a ready connection
  456.  
  457. currentError = NONE;
  458. currentStatus = NOTHING;
  459.  
  460. sock = new NormalSocket();
  461. sock->SetTimeout(SMTP_CONNECT_TIMEOUT);
  462. }
  463.  
  464. SMTP::~SMTP()
  465. {
  466. if(!connected)
  467. return;
  468. connected = false;
  469.  
  470. // disconnect nicely
  471. sock->Send("QUIT\r\n", strlen("QUIT\r\n"));
  472.  
  473. recv_reply();
  474. sock->Disconnect();
  475. }
  476.  
  477.  
  478. bool SMTP::connect(std::string server, std::string ehloName, bool useAuthenticatedPort)
  479. {
  480. // Possibly needed for SSL
  481. this->ehloName = ehloName;
  482. this->server = server;
  483.  
  484. if(!sock->Connect(server.c_str(), (useAuthenticatedPort ? 587 : 25)))
  485. {
  486. currentError = CANNOT_CONNECT;
  487.  
  488. wsprintfA(buffer, "sock->t Error: %i\n", sock->GetLastError());
  489.  
  490. sock->Disconnect();
  491. return false;
  492. }
  493. currentStatus = CONNECTED;
  494.  
  495. // Read welcome message
  496. // TODO: If(!strstr(reply, "ESMTP")) send("HELO");
  497. if(!is_successful())
  498. {
  499. currentError = REJECT_ON_CONNECT;
  500. return false;
  501. }
  502. currentStatus = BANNER_RECVIVED;
  503.  
  504. std::string EHLO = "EHLO " + ehloName + "\r\n";
  505. sock->Send(EHLO);
  506.  
  507. if(!get_supported_functions())
  508. {
  509. currentError = HELO_NOT_ACCEPTED;
  510. return false;
  511. }
  512. currentStatus = EHLO_ACCEPTED;
  513. connected = true;
  514.  
  515. return true;
  516. }
  517.  
  518. bool SMTP::connect(std::vector<MX_Information> servers, std::string ehloName, bool useAuthenticatedConnection)
  519. {
  520. for(std::vector<MX_Information>::iterator i = servers.begin(); i != servers.end(); i++)
  521. {
  522. if(connect(i->name, ehloName, useAuthenticatedConnection))
  523. {
  524. //printf("Y%50s (%i)\n", i->name.c_str(), i->prefernce);
  525. return true;
  526. }
  527. /*
  528. else
  529. {
  530. printf("N%50s (%i)\n", i->name.c_str(), i->prefernce);
  531. }*/
  532. }
  533.  
  534. return false;
  535. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement