566 lines
15 KiB
C++
566 lines
15 KiB
C++
//
|
|
// 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 <sstream>
|
|
#elif HAVE_STRSTREAM
|
|
#include <strstream>
|
|
#endif
|
|
#include "core/file.h"
|
|
|
|
#include <time.h>
|
|
|
|
#if SUPPORTS_NETWORKING
|
|
|
|
//All the spleck that it takes to run sockets in Unix...
|
|
#include <stdio.h>
|
|
|
|
#if HAVE_SYS_SOCKET_H
|
|
# include <sys/socket.h>
|
|
# include <netdb.h>
|
|
# include <netinet/in.h>
|
|
# include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#endif
|
|
|
|
#if HAVE_SYS_UTSNAME_H
|
|
# include <sys/utsname.h>
|
|
#endif
|
|
|
|
#if HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
#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 <unistd.h>
|
|
|
|
#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<TSTRING>::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<TSTRING>::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
|