// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000-2019 Tripwire, // Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights // reserved. // // This program is free software. The contents of this file are subject // to the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. You may redistribute it and/or modify it // only in compliance with the GNU General Public License. // // This program is distributed in the hope that it will be useful. // However, this program is distributed AS-IS WITHOUT ANY // WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. Please see the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // Nothing in the GNU General Public License or any other license to use // the code or files shall permit you to use Tripwire's trademarks, // service marks, or other intellectual property without Tripwire's // prior written consent. // // If you have any questions, please contact Tripwire, Inc. at either // info@tripwire.org or www.tripwire.org. // #include "stdtripwire.h" #include "core/debug.h" #include "mailmessage.h" #include "tw/configfile.h" #include "tw/twutil.h" #include "tripwirestrings.h" #include "core/stringutil.h" #if HAVE_SSTREAM #include #elif HAVE_STRSTREAM #include #endif #include "core/file.h" #include #if SUPPORTS_NETWORKING //All the spleck that it takes to run sockets in Unix... #include #if HAVE_SYS_SOCKET_H # include # include # include # include #endif #include #if HAVE_SYS_TIME_H # include #endif #if HAVE_SYS_UTSNAME_H # include #endif #if HAVE_SYS_SELECT_H # include #endif /* Some systems like Solaris and AIX don't define * INADDR_NONE, but it's pretty standard. If not, * then the OS _should_ define it for us. */ #ifndef INADDR_NONE # define INADDR_NONE 0xffffffff #endif #include #ifndef INVALID_SOCKET # define INVALID_SOCKET -1 #endif #if IS_AROS # ifndef HAVE_GETHOSTNAME # define HAVE_GETHOSTNAME 1 # endif #endif #ifndef HAVE_GETHOSTNAME static int gethostname(char* name, int namelen) { name[0] = '\0'; #if HAVE_SYS_UTSNAME_H struct utsname myname; uname(&myname); if (strlen(myname.nodename) < (unsigned int)namelen) { strncpy(name, myname.nodename, namelen); return 0; } else { //Not enough room in the buffer for the nodename return -1; // equivalent of SOCKET_ERROR } #else strncpy(name, "localhost", namelen); #endif } #endif //HAVE_GETHOSTNAME // // TODO - maybe convert this SMTP code to non-blocking socket calls, or use // another thread, or make it fail gracefully when the server fails to // respond at all. // /////////////////////////////////////////////////////////////////////////////// // // Construct the SMTP Mail Message Sender // cSMTPMailMessage::cSMTPMailMessage(TSTRING strServerName, unsigned short portNumber) { mstrServerName = strServerName; mPortNumber = portNumber; mSocket = INVALID_SOCKET; } /////////////////////////////////////////////////////////////////////////////// // // Clean up any thing left over from sending or failing to send the message. // cSMTPMailMessage::~cSMTPMailMessage() { } /////////////////////////////////////////////////////////////////////////////// // // Get the IP address from the the server string. It's OK to have // this function look up a string like "192.34.64.23" or one like // "mail.stinkycheese.com" // long cSMTPMailMessage::GetServerAddress() { bool bIsNumeric = true; // Decide if the string is in the form "127.0.0.1" or "mail.domain.com" // by looking for an character that is not a digit or a period. for (std::vector::size_type i = 0; i < mstrServerName.length(); i++) { if (mstrServerName[i] != '.' && (mstrServerName[i] < '0' || mstrServerName[i] > '9')) { bIsNumeric = false; } } std::string sNarrowString = cStringUtil::TstrToStr(mstrServerName); if (bIsNumeric) { // convert the numberic address to a long return inet_addr(sNarrowString.c_str()); } else { // do a DNS lookup of the hostname and get the long hostent* ent = gethostbyname(sNarrowString.c_str()); if (!ent) return INADDR_NONE; else return *(long*)ent->h_addr_list[0]; } } /////////////////////////////////////////////////////////////////////////////// // // Create and open the socket connection // bool cSMTPMailMessage::OpenConnection() { // Initialize the socket structure sockaddr_in sockAddrIn; memset(&sockAddrIn, 0, sizeof(sockaddr)); sockAddrIn.sin_family = AF_INET; sockAddrIn.sin_port = htons(mPortNumber); uint32_t iServerAddress = GetServerAddress(); sockAddrIn.sin_addr.s_addr = iServerAddress; if (iServerAddress == INADDR_NONE) { DecodeError(); TOSTRINGSTREAM estr; estr << TSS_GetString(cTripwire, tripwire::STR_ERR2_MAIL_MESSAGE_SERVER) << mstrServerName; tss_mkstr(errStr, estr); throw eMailSMTPIPUnresolvable(errStr); return false; } // Create the socket mSocket = socket(AF_INET, SOCK_STREAM, 0); if (mSocket == INVALID_SOCKET) { DecodeError(); throw eMailSMTPSocket(); return false; } // Make the connection int connectVal = connect(mSocket, (struct sockaddr*)&sockAddrIn, sizeof(sockAddrIn)); if (connectVal < 0) { DecodeError(); TOSTRINGSTREAM estr; estr << TSS_GetString(cTripwire, tripwire::STR_ERR2_MAIL_MESSAGE_SERVER) << mstrServerName; tss_mkstr(errStr, estr); throw eMailSMTPOpenConnection(errStr); return false; } return true; } /////////////////////////////////////////////////////////////////////////////// // // Close the socket connection // bool cSMTPMailMessage::CloseConnection() { if (INVALID_SOCKET != mSocket) { // close the connection int closeVal = close(mSocket); if (closeVal != 0) { DecodeError(); throw eMailSMTPCloseConnection(); return false; } return true; } else { return true; } } /////////////////////////////////////////////////////////////////////////////// // // Call this function to send the mail message once the requisite // methods have been called to define the mail message. // bool cSMTPMailMessage::Send() { // Be sure that everything that needs to be set has been set if (!Ready()) { // the message has not been adequately defined and cannot be sent. return false; } bool errorOccured = false; if ((errorOccured = !OpenConnection()) == false) { errorOccured |= !MailMessage(); errorOccured |= !CloseConnection(); } return !errorOccured; } /////////////////////////////////////////////////////////////////////////////// // // Once the connection is set, this function conducts the SMTP protocol // to actually send the message. // bool cSMTPMailMessage::MailMessage() { cDebug d("cSMTPMailMessage::MailMessage()"); std::string sNarrowString; // Get the greeting message from the SMTP server if (!GetAcknowledgement()) return false; char sLocalHost[256]; // It's alright for this to be a fixed buffer, since // we will be explicitely passing it's length to // mpfnGethostname (see below). It won't be used // after that. #if !ARCHAIC_STL std::ostringstream strmSend; #else strstream strmSend; #endif // This should be a stream object, so we don't have // to use nasty calls to sprintf that might overflow // the buffer. Before, we used a fixed buffer of 512 // characters, and there's really no guarantee that any // of the string objects (that we are printing to the buffer // from) will be below this limit. ASSERT(strmSend.str().length() == 0); // This bad boy better be empty. // get our hostname for the HELO message if (gethostname(sLocalHost, 256) < 0) { DecodeError(); return false; } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Set up connection //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Say hello #if !ARCHAIC_STL strmSend.str(""); //Clear the stream buffer. #else // TODO #endif strmSend << "HELO " << sLocalHost << "\r\n"; //Fill the stream buffer. SendString(strmSend.str()); if (!GetAcknowledgement()) return false; #if !ARCHAIC_STL strmSend.str(""); //Clear the stream buffer. #else // TODO #endif strmSend << "MAIL FROM:<" << cStringUtil::TstrToStr(mstrFrom) << ">\r\n"; SendString(strmSend.str()); if (!GetAcknowledgement()) return false; // Say who all we're sending to #if !ARCHAIC_STL strmSend.str(""); //Clear the stream buffer. #else // TODO #endif for (std::vector::size_type i = 0; i < mvstrRecipients.size(); i++) { sNarrowString = cStringUtil::TstrToStr(mvstrRecipients[i]); strmSend << "RCPT TO:<" << cStringUtil::TstrToStr(mvstrRecipients[i]) << ">\r\n"; SendString(strmSend.str()); if (!GetAcknowledgement()) return false; } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Start data //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send start the message process SendString("DATA\r\n"); if (!GetAcknowledgement()) return false; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send Header //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // set up header #if !ARCHAIC_STL strmSend.str(""); #else // TODO #endif strmSend << cMailMessage::Create822Header(); SendString(strmSend.str()); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Get Body and Attachments //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool allOK = true; // get body std::string sNBody = cStringUtil::TstrToStr(mstrBody); std::string sAttachments; if (!GetAttachmentsAsString(sAttachments)) { sAttachments.erase(); allOK = false; } std::string sSend = sNBody + sAttachments; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Make sure that there's no lone LFs //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cMailMessageUtil::LFToCRLF(sSend); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Determine encoding needed for body or attachments //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - std::string sContentType = "Content-Transfer-Encoding: "; if (cMailMessageUtil::HasNonAsciiChars(sSend)) { // encode text sSend = iMimeEncoding::GetInstance()->Encode(sSend, cMailMessageUtil::_MAX_RFC822_LINE_LEN); // identify content type sContentType += iMimeEncoding::GetInstance()->GetContentTypeIdentifier(); } else { // do no encoding // identify content type sContentType += "7bit"; } // send content type sContentType += "\r\n"; SendString(sContentType); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send Body and Attachments //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SendString("\r\n"); SendString(sSend); // send the end of message line SendString("\r\n.\r\n"); if (!GetAcknowledgement()) return false; // send the quit message SendString("QUIT"); return allOK; } /////////////////////////////////////////////////////////////////////////////// // // Get the response code from the server to see if the last command // we sent was accepted. // bool cSMTPMailMessage::GetAcknowledgement() { cDebug d("cSMTPMailMessage::GetAcknowledgement"); const int bufsize = 512; TCHAR sRecvString[bufsize + 1]; // This string is and should be unicode char sTempString[bufsize + 1]; // This string is not, and should not be unicode int bytes; int i = 0; // make socket array for the call to select fd_set socketSet; // need comment timeval tv; FD_ZERO(&socketSet); FD_SET(mSocket, &socketSet); // set the timeout time to sixty seconds tv.tv_sec = 60; tv.tv_usec = 0; // Wait up to sixty seconds fot data to show up on the socket to be read if (select(mSocket + 1, &socketSet, NULL, NULL, &tv) == 1) { // Get the reply message bytes = recv(mSocket, sTempString, 512, 0); // TODO:BAM -- this should be changed to use 'cStringUtil' for (int j = 0; j < bytes && i < bufsize; j++, i++) sRecvString[i] = sTempString[j]; sRecvString[i] = 0; std::string sIn(sTempString, bytes); d.TraceDebug("Received \"%s\"\n", sIn.c_str()); } else { d.TraceDebug("No Receive\n"); } // decode the numeric reply int code = _ttoi(sRecvString); if (code >= 200 && code < 400) { // Error codes in the range of 200-399 indicate success. See RFC 821 return true; } else { // Error codes other than 200-399 indicate an error or a failure. See RFC 821 TOSTRINGSTREAM estr; estr << TSS_GetString(cTripwire, tripwire::STR_ERR2_MAIL_MESSAGE_SERVER_RETURNED_ERROR) << sRecvString; tss_mkstr(errStr, estr); throw eMailSMTPServer(errStr); return false; } } void cSMTPMailMessage::SendString(const std::string& str) { cDebug d("util_SendString()"); if (str.length() < 800) d.TraceDebug("Sending \"%s\"\n", str.c_str()); else d.TraceDebug("Sending (truncated in this debug output)\"%s\"\n", std::string(str.c_str(), 800).c_str()); send(mSocket, str.c_str(), str.length(), 0); } /////////////////////////////////////////////////////////////////////////////// // // Get debug info when a error is encountered. // void cSMTPMailMessage::DecodeError() { # if defined(_DEBUG) // // TODO - Write what ever error reporting will be needed under unix. // # endif // defined(_DEBUG) } #endif // SUPPORTS_NETWORKING