git-svn-id: https://svn.microneil.com/svn/SNFMilter/trunk@1 2c985dca-31e6-41a4-b4a2-d8f5b7f8e074master
// MailHeaders.cpp | |||||
// Copyright (C) 2008 ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// This file contains the functions to extract the mail header and | |||||
// body from the string returned by SNFMilterEngine::XHeaders(). | |||||
#include <stdexcept> | |||||
#include <sstream> | |||||
#include "MailHeaders.hpp" | |||||
using namespace std; | |||||
const string DefaultSMTPENDL = "\r\n"; ///< Default SMTP endline. | |||||
const string DefaultLibmilterENDL = "\n "; ///< Default libmilter endline. | |||||
const string DefaultPrefix = "X-"; ///< Default start of header line. | |||||
MailHeaders::MailHeaders() : | |||||
SMTPENDL(DefaultSMTPENDL), | |||||
LibMilterENDL(DefaultLibmilterENDL) { | |||||
} | |||||
MailHeaders::~MailHeaders() { | |||||
} | |||||
void | |||||
MailHeaders::loadHeaders(const string &HeaderBuffer) { | |||||
Buffer = HeaderBuffer; | |||||
} | |||||
bool | |||||
MailHeaders::getHeaderName(string *HeaderName) { | |||||
if (Buffer.empty()) { // No header if the buffer is empty. | |||||
return false; | |||||
} | |||||
string::size_type endIndex = Buffer.find(SMTPENDL); | |||||
if (string::npos == endIndex) { // No SMPENDL. | |||||
ostringstream Temp; | |||||
Temp << "Invalid header line (no SMTP end-of-line): '" | |||||
<< Buffer << "'."; | |||||
throw std::invalid_argument(Temp.str()); | |||||
} | |||||
string Line(Buffer, 0, endIndex); // First line of the header. | |||||
string::size_type colonIndex = Line.find(":"); | |||||
if (string::npos == colonIndex) { // No ":". | |||||
ostringstream Temp; | |||||
Temp << "Invalid header line (no ':'): '" | |||||
<< Buffer << "'."; | |||||
throw std::invalid_argument(Temp.str()); | |||||
} | |||||
HeaderName->assign(Line, 0, colonIndex); | |||||
if (0 == HeaderName->size()) { // Null header name. | |||||
ostringstream Temp; | |||||
Temp << "Invalid header line (no name): '" | |||||
<< Buffer << "'."; | |||||
throw std::invalid_argument(Temp.str()); | |||||
} | |||||
Buffer.erase(0, colonIndex + 1); // Erase the header name and ":". | |||||
return true; | |||||
} | |||||
bool | |||||
MailHeaders::getHeaderLine(string *HeaderLine) { | |||||
if (Buffer.empty()) { // No line if the buffer is empty. | |||||
return false; | |||||
} | |||||
string::size_type EndIndex = Buffer.find(SMTPENDL); | |||||
if (string::npos == EndIndex) { // No SMPENDL. | |||||
ostringstream Temp; | |||||
Temp << "Invalid header line (no SMTP end-of-line): '" | |||||
<< Buffer << "'."; | |||||
throw std::invalid_argument(Temp.str()); | |||||
} | |||||
HeaderLine->assign(Buffer, 0, EndIndex); // Tentative line of the body. | |||||
string::size_type XIndex = HeaderLine->find("X-"); | |||||
if (XIndex == 0) { // Does it begin with "X-"? | |||||
return false; // Yes; it's not the body line. | |||||
} | |||||
Buffer.erase(0, EndIndex + SMTPENDL.size()); // Erase line and SMTPENDL. | |||||
return true; | |||||
} | |||||
bool | |||||
MailHeaders::getNameBody(string *HeaderName, string *FormattedHeaderBody) { | |||||
if (!getHeaderName(HeaderName)) { // Get the header. | |||||
return false; // No more headers. | |||||
} | |||||
FormattedHeaderBody->clear(); | |||||
string HeaderLine; // One line of the body. | |||||
bool FirstTime = true; | |||||
while (getHeaderLine(&HeaderLine)) { // While this header has lines... | |||||
if (FirstTime) { | |||||
*FormattedHeaderBody = HeaderLine; | |||||
FirstTime = false; | |||||
} else { | |||||
*FormattedHeaderBody += LibMilterENDL; | |||||
*FormattedHeaderBody += HeaderLine; | |||||
} | |||||
} | |||||
return true; | |||||
} |
// MailHeaders.hpp | |||||
// Copyright (C) 2008 ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// Header file for MailHeaders.cpp. | |||||
#ifndef MailHeadershpp_included | |||||
#define Mailheadershpp_included | |||||
#include <string> | |||||
using namespace std; | |||||
/// Class to extract mail header name/body pairs. | |||||
// | |||||
// This class extracts mail header name/body pairs from a string | |||||
// containing a series of mail headers. | |||||
// | |||||
class MailHeaders { | |||||
private: | |||||
string Buffer; ///< Contains the mail headers. | |||||
const string SMTPENDL; ///< STMP end-of-line string (e.g. "\r\n). | |||||
const string LibMilterENDL; ///< libmilter end-of-line string. | |||||
const string Prefix; ///< Start of header name (e.g. "X-"). | |||||
/// Get the next header name and body. | |||||
// | |||||
// This method inputs a string containing zero or more RFC | |||||
// 2822-compliant headers, and returns the header name and body in | |||||
// a format suitable for adding the header using the libmilter | |||||
// library. The header name, ":", body, and the trailing SMTPENDL | |||||
// are removed from the input string. | |||||
// | |||||
// \param[out] HeaderName contains the name of the header. | |||||
// | |||||
// \param[in,out] Buffer on input contains the headers. On output, | |||||
// the name of the first header, the ":", header body, and trailing | |||||
// SMTPENDL are removed from the beginning. | |||||
// | |||||
// \returns true if a header is found, false if Buffer is "". | |||||
// | |||||
// \throws std::invalid_argument if Buffer does not have the correct | |||||
// format. | |||||
// | |||||
// \see getHeaderLine. | |||||
// | |||||
// \see getHeaderBody. | |||||
// | |||||
bool getHeaderName(std::string *HeaderName); | |||||
/// Get the next line of a mail header. | |||||
// | |||||
// This method inputs a string containing zero or more lines of | |||||
// the body of a RFC 2822-compliant header, and returns the next | |||||
// line of the body of the header. The line and the SMTPENDL are | |||||
// removed from the input string Buffer. | |||||
// | |||||
// \param[out] HeaderLine contains the line of the header. | |||||
// | |||||
// \returns true if a line is found, false if Buffer is "". | |||||
// | |||||
// \throws std::invalid_argument if the first line of Buffer is not | |||||
// "", and does not end with SMTPENDL. | |||||
// | |||||
bool getHeaderLine(std::string *HeaderLine); | |||||
public: | |||||
MailHeaders(); ///< Constructer for MailHeaders. | |||||
~MailHeaders(); ///< Destructor for MailHeaders. | |||||
/// Load mail headers. | |||||
// | |||||
// This method loads the object with the headers. | |||||
// | |||||
// \param[in] HeaderBuffer contains the headers. | |||||
// | |||||
void loadHeaders(const string &HeaderBuffer); | |||||
/// Get the next header name and body. | |||||
// | |||||
// This method returns the next header name and body. The body is | |||||
// formatted so that it can be used as input for the libmilter | |||||
// smfi_addheader() function. | |||||
// | |||||
// \param[out] HeaderName contains the header name. | |||||
// | |||||
// \param[out] FormattedHeaderBody contains the formatted header | |||||
// body. | |||||
// | |||||
// \returns true if a header/body is found, false if the buffer is | |||||
// "". | |||||
// | |||||
// \throws exceptions thrown by getHeaderName and getHeaderLine. | |||||
// | |||||
// \see getHeaderName. | |||||
// | |||||
// \see getHeaderLine. | |||||
// | |||||
bool getNameBody(string *HeaderName, string *FormattedHeaderBody); | |||||
}; | |||||
#endif |
## Process this file with automake to produce Makefile.in | |||||
## | |||||
## $Id$ | |||||
## | |||||
## automake input for the MicroNeil SNFMilter application (SNFMilter directory). | |||||
## | |||||
## Author: Alban Deniz | |||||
## | |||||
## Copyright (C) 2009 ARM Research Labs, LLC. | |||||
## See www.armresearch.com for the copyright terms. | |||||
## | |||||
## | |||||
LIBS = @SNF_LIBS@ -L../SNFMulti -L../CodeDweller -lSNFMulti -lCodeDweller @LIBS@ | |||||
CXXFLAGS = $(SNF_CXXFLAGS) -I@top_srcdir@/SNFMulti -I@top_srcdir@/CodeDweller | |||||
sbin_PROGRAMS = \ | |||||
SNFMilter | |||||
SNFMilter_SOURCES = \ | |||||
@top_srcdir@/SNFMilter/main.cpp \ | |||||
@top_srcdir@/SNFMilter/SNFMilter.cpp \ | |||||
@top_srcdir@/SNFMilter/MailHeaders.cpp | |||||
noinst_HEADERS = \ | |||||
@top_srcdir@/SNFMilter/MailHeaders.hpp \ | |||||
@top_srcdir@/SNFMilter/ProductionQueue.hpp \ | |||||
@top_srcdir@/SNFMilter/SNFMilter.hpp | |||||
EXTRA_DIST = \ | |||||
Makefile.am | |||||
clean-local: | |||||
rm -f *.gcno *.gcov *.gcda *~ $(CONFDATA) |
// ProductionQueue.hpp | |||||
// Copyright (C) 2007 MicroNeil Research Corporation | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// A ProductionQueue is a templated, thread safe mechanism for implementing | |||||
// a producer/consumer relationship. The objects in the queue should be simple | |||||
// data so that they can be created, destroyed, and copied without trouble. Put | |||||
// another way - the objects in the ProductionQueue should be lightweight | |||||
// handles for other things. Those things should be created and destroyed | |||||
// elsewhere. | |||||
#include <queue> | |||||
#include "threading.hpp" | |||||
using namespace std; | |||||
template<typename T> // Templatized | |||||
class ProductionQueue { // Production Queue Class | |||||
private: | |||||
Mutex myMutex; // Contains a mutex and | |||||
volatile int LatestSize; // a volatile (blinking light) size | |||||
ProductionGateway myGateway; // integrated with a production | |||||
queue<T> myQueue; // gateway and a queue. | |||||
public: | |||||
ProductionQueue() : LatestSize(0) {} // The size always starts at zero. | |||||
T take() { // To consume a queued object | |||||
myGateway.consume(); // we wait on the production gateway | |||||
ScopeMutex OneAtATimePlease(myMutex); // and when we get through we lock | |||||
T O = myQueue.front(); // the mutext, take the object on the | |||||
myQueue.pop(); // front of the queue, pop it out, | |||||
LatestSize = myQueue.size(); // and rest our size (blinking light). | |||||
return O; // Then return the object we got. | |||||
} | |||||
void give(T O) { // To produce a queued object | |||||
ScopeMutex OneAtATimePlease(myMutex); // we wait on the mutex. When we | |||||
myQueue.push(O); // get through we push our object | |||||
LatestSize = myQueue.size(); // into the queue, reset our size | |||||
myGateway.produce(); // indicator and tell the gateway. | |||||
} // When we're done it can be grabbed. | |||||
int size() { // To check the size we look at | |||||
return LatestSize; // the blinking light. | |||||
} | |||||
}; | |||||
// SNFMilter.hpp | |||||
// Copyright (C) 2007 ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// This file defines the SNFMilter configuration data structures and interface. | |||||
#ifndef SNFMilterhpp_included | |||||
#define SNFMilterhpp_included | |||||
#include <signal.h> | |||||
#include <libmilter/mfapi.h> | |||||
#include <vector> | |||||
#include <string> | |||||
#include "ProductionQueue.hpp" | |||||
#include "MailHeaders.hpp" | |||||
#include "timing.hpp" | |||||
#include "threading.hpp" | |||||
using namespace std; | |||||
class snf_EngineHandler; // We must know that these exist. | |||||
class snf_RulebaseHandler; | |||||
// Connection types. | |||||
enum SNFMilterSocketType { | |||||
TCPMilterSocket = 1, | |||||
UNIXMilterSocket = 2, | |||||
NOMilterSocket = 3 | |||||
}; | |||||
// SNFMilterAction - What we can do. | |||||
enum SNFMilterAction { // SNFMilter Actions | |||||
Error = -1, // Error result. | |||||
Allow = 0, // Process the message. | |||||
Accept = 1, // White-List the message. | |||||
Retry = 2, // Return Try Again (tmp fail) | |||||
Reject = 3, // Reject the message. | |||||
Discard = 4, // Silently discard. | |||||
Quarantine = 5, // Store in quarantine. | |||||
NoAction = 6, // Take no action. | |||||
NMilterActions | |||||
}; | |||||
// | |||||
// Configuration. | |||||
// | |||||
const string AllowActionMnemonic = "0"; | |||||
const string AcceptActionMnemonic = "1"; | |||||
const string RetryActionMnemonic = "2"; | |||||
const string RejectActionMnemonic = "3"; | |||||
const string DiscardActionMnemonic = "4"; | |||||
const string QuarantineActionMnemonic = "5"; | |||||
const string NoActionMnemonic = "6"; | |||||
const string TCPMilterSocketMnemonic = "1"; | |||||
const string UNIXMilterSocketMnemonic = "2"; | |||||
const int ResultCodesCount = 64; // Number of valid result codes. | |||||
const int ConfigurationLifetime = 1000; // Config life time in ms. | |||||
const bool PrependLocalReceivedHeader = true; // True to prepend local received headers | |||||
// to the buffer to scan. | |||||
const sfsistat FailSafeMilterResponse = SMFIS_CONTINUE; // Return value to libmilter | |||||
// in case of error. | |||||
const string::size_type MailBufferReserveSize = 65536; // Maximum amount of email message to scan. | |||||
const string SMTPENDL = "\r\n"; // SMTP endline. | |||||
const int ShutdownPeriods = 5; // Number of time periods to wait while | |||||
// shutting down. | |||||
const int ShutdownWaitPeriod_ms = 1000; // Time period duration for shutting down. | |||||
const string UnknownExceptionMessage = "Unknown exception occurred."; // Logged when an unknown exception occurs. | |||||
const int ShutdownSignal = SIGTERM; // Signal for shutdown. | |||||
// | |||||
// End of configuration. | |||||
// | |||||
// The SNFMilterEngine class provides up to date configuration data and | |||||
// a scanning engine for the milter. It is presumed that at one of these objects | |||||
// will be used in each message scan. They can be created, used, and destroyed, | |||||
// or for a more optimized approach they can be created, used, stored in a pool, | |||||
// and refreshed, and used again for a new scan. They should all be properly | |||||
// destroyed before runLibMilter() returns. | |||||
class SNFMilterEngine { // Milter config objec. One per scan. | |||||
private: | |||||
Mutex ConfigMutex; // Configuration lock mutex. | |||||
snf_RulebaseHandler* myRulebase; // Where is my rulebase? | |||||
snf_EngineHandler* myEngine; // Where is my engine? | |||||
// Configuration data. | |||||
// IP scan actions | |||||
SNFMilterAction WhiteAction; // Action for White IP. | |||||
SNFMilterAction CautionAction; // Action for Caution IP. | |||||
SNFMilterAction BlackAction; // Action for Black IP. | |||||
SNFMilterAction TruncateAction; // Action for Truncate IP. | |||||
SNFMilterAction NonZeroAction; // Action for nonzero scans. | |||||
// Message scan actions | |||||
SNFMilterAction ResultActions[ResultCodesCount]; // Array of possible scan actions. | |||||
Timeout ConfigurationCheckTime; // Timer for checking the configuration. | |||||
string RunningConfiguration; // The current running config string. | |||||
void readConfiguration(); // Parses the configuration. | |||||
void checkConfiguration(); // Reload the config if it is old. | |||||
public: | |||||
SNFMilterEngine(snf_RulebaseHandler* R); // Construct the configuration. | |||||
~SNFMilterEngine(); // Destroy the configuration. | |||||
void setResultAction(int Result, SNFMilterAction Action); // Set a result / action pair. | |||||
SNFMilterAction scanIP(unsigned long int IP); // Scans an IP. | |||||
SNFMilterAction scanMessage(const unsigned char* bfr, int length); // Scans a message. | |||||
string XHeaders(); // Return X headers from last scan. | |||||
}; | |||||
// The SNFMilterContext class maintains the context for a connection | |||||
// from the MTA. There is one SNFMilterContext object for each | |||||
// connection from the MTA. They can be created, used, and destroyed, | |||||
// or for a more optimized approach they can be created, used, stored | |||||
// in a pool, and refreshed, and used again for a new scan. They | |||||
// should all be properly destroyed before runLibMilter() returns. | |||||
class SNFMilterContext { // Milter connection context object. | |||||
public: | |||||
// Object states. The object transitions to the corresponding state | |||||
// when a libmilter callback is invoked. The object is in the | |||||
// Pooled state when the object is not being used. | |||||
enum SNFMilterState { | |||||
Pooled, | |||||
Connect, | |||||
Helo, | |||||
EnvFrom, | |||||
EnvRcpt, | |||||
Data, | |||||
Header, | |||||
EOH, | |||||
Body, | |||||
EOM, | |||||
Close, | |||||
NMilterStates | |||||
} State; | |||||
SNFMilterContext(snf_RulebaseHandler *); | |||||
~SNFMilterContext(); | |||||
sfsistat SkipReturn; // libmilter return value when further | |||||
// callbacks of the same type are to be skipped. | |||||
// Map the scan result to the libmilter return value. | |||||
sfsistat | |||||
smfisReturn(SNFMilterAction); | |||||
// Scanning engine. | |||||
SNFMilterEngine milterEngine; | |||||
string getLocalReceivedHeader(); | |||||
// Connection data. | |||||
struct SNFMilterConnectionData { | |||||
string HostName; | |||||
IP4Address HostIP; | |||||
string HostHelo; | |||||
// Clear the object. | |||||
void clear() { | |||||
HostName.clear(); | |||||
HostIP = (long unsigned )0; | |||||
HostHelo.clear(); | |||||
} | |||||
} ConnectionData; | |||||
// Message buffer. | |||||
struct SNFMilterMessageData { | |||||
// Buffer to hold the message. | |||||
string MessageBuffer; | |||||
// Sender address. | |||||
string SenderAddress; | |||||
// Constructor reserves memory to hold the message. | |||||
SNFMilterMessageData(string::size_type reserveSize) { | |||||
MessageBuffer.reserve(reserveSize); | |||||
} | |||||
// Clear the object. | |||||
void clear() { | |||||
MessageBuffer.clear(); | |||||
SenderAddress.clear(); | |||||
} | |||||
} MessageData; | |||||
}; | |||||
class SNFMilterContextPool { // SNFMilter Pool Manager | |||||
private: | |||||
Mutex ContextAllocationControl; // Protects context allocation. | |||||
vector<SNFMilterContext*> ContextPool; // Contains all created contexts. | |||||
ProductionQueue<SNFMilterContext*> AvailableContexts; // Contains all available contexts. | |||||
snf_RulebaseHandler* myRulebase; // Rulebase handler. | |||||
// Connection info. | |||||
SNFMilterSocketType MilterSocketType; | |||||
string MilterSocketPath; | |||||
string MilterSocketGroup; | |||||
string MilterSocketIP; | |||||
int MilterSocketPort; | |||||
public: | |||||
SNFMilterContextPool(snf_RulebaseHandler* Rulebase); // Ctor needs a live rulebase handler. | |||||
~SNFMilterContextPool(); // Dtor gracefully discards contexts. | |||||
SNFMilterSocketType getSocketType(); // Named pipe or TCP socket specified | |||||
// in the configuration. | |||||
string getSocketPath(); // Path of named pipe specified in | |||||
// the configuration | |||||
string getSocketGroup(); // Group specified for named pipe | |||||
// in the conofiguration. | |||||
string getSocketIP(); // IP (host) specified in the | |||||
// configuration. | |||||
int getSocketPort(); // TCP port specified in the | |||||
// configuration | |||||
SNFMilterContext* grab(); // Get an context to use. | |||||
void drop(SNFMilterContext* E); // Drop an context after use. | |||||
bool allUnused(); // Return true if no contexts are in use. | |||||
void logThisError(string ContextName, int Code, string Text); // Log an error message. | |||||
void logThisInfo(string ContextName, int Code, string Text); // Log an informational message. | |||||
}; | |||||
// The runLibMilter function establishes the appropriate libmilter call backs and | |||||
// accepts calls from the MTA via libmilter until it is told to quit. When it | |||||
// is told to quit it gracefully closes down, reclaims any memory it allocated, | |||||
// and returns. | |||||
// The actual code for the runLibMilter() function call is found in SNFMilter.cpp. | |||||
void runLibMilter(SNFMilterContextPool* Contexts, bool DebugMode); // Run the milter 'til it's done. | |||||
#endif |
// main.cpp | |||||
// | |||||
// Copyright (C) 2007, ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// This is the entry point for the SNF Milter. | |||||
// The main() function acquires the configuration file path, sets up the | |||||
// SNF engine, and passes control to the SNFMilter module via runSNFMilter(). | |||||
// | |||||
// SNFMilter() makes use of the SNF engine until it is dismissed by the MTA via | |||||
// the appropriate libmilter call. | |||||
// | |||||
// While SNFMilter() is running, it will get new configuration data for each | |||||
// scan it is asked to perform. A configuration packet will be produced on | |||||
// demand by the main module. The main module will check the configuration once | |||||
// every second or so to make sure it has the correct current data. The | |||||
// configuration data structure is defined in SNFMilter.hpp. | |||||
// | |||||
// When the SNFMilter() function returns, the main() function closes down the | |||||
// SNF Engine and exits the program. | |||||
#include <errno.h> | |||||
#include <string.h> | |||||
#include <exception> | |||||
#include <iostream> | |||||
#include "SNFMulti.hpp" | |||||
#include "SNFMilter.hpp" | |||||
#include "config.h" | |||||
using namespace std; | |||||
const char* SNF_MILTER_VERSION = "SNFMilter " PACKAGE_VERSION " Build: " __DATE__ " " __TIME__; | |||||
static const string XCIShutdownResponse = | |||||
"<snf><xci><server><response message=\'shutdown in progress\' code=\'1\'/></server></xci></snf>\n"; | |||||
class XCIShutdownWatcher : public snfXCIServerCommandHandler { // Shutdown watcher. | |||||
public: | |||||
XCIShutdownWatcher(){} | |||||
string processXCIRequest(snf_xci& X) { // Here is how we process requests. | |||||
if(0 == X.xci_server_command.find("shutdown")) { // If we find shutdown then | |||||
smfi_stop(); // Stop processing messages. | |||||
return XCIShutdownResponse; // respond with a message. | |||||
} // If we get some other request | |||||
return XCIErrorResponse; // return the error response. | |||||
} | |||||
}; | |||||
class SignalCatcher : public Thread { // Class to catch a signal. | |||||
private: | |||||
sigset_t *MySignalSet; | |||||
snf_RulebaseHandler *MyRulebase; | |||||
public: | |||||
SignalCatcher(sigset_t *Signal_Set, snf_RulebaseHandler* Rulebase) : // Construct. | |||||
MySignalSet(Signal_Set), // Set of signals to catch. | |||||
MyRulebase(Rulebase) { // Used for error logging. | |||||
run(); // Start the thread. | |||||
} | |||||
void myTask() { | |||||
int ReceivedSignal; | |||||
int Status = sigwait(MySignalSet, &ReceivedSignal); | |||||
if (0 != Status) { | |||||
// Log error. | |||||
ostringstream Temp; | |||||
Temp << "Error waiting for signal: " << strerror(Status) << "."; | |||||
MyRulebase->logThisError("Signal Catcher", 1, Temp.str()); | |||||
return; | |||||
} | |||||
smfi_stop(); // Stop processing. | |||||
} | |||||
}; | |||||
const int CorrectARGc = 2; // How many arguments we expect. | |||||
void displayHelp() { // Display some help. | |||||
cout | |||||
<< SNF_MILTER_VERSION << endl | |||||
<< "Copyright (C) 2007, ARM Research Labs, LLC (www.armresearch.com)" << endl | |||||
<< endl | |||||
<< "Use snfmilter <full path to configuration file>" << endl | |||||
<< "Example: /home/snfmilter/snfmilter /home/snfmilter/snf_milter.xml" << endl; | |||||
}; | |||||
int main(int argc, char* argv[]) { | |||||
// Get and check the command line arguments. | |||||
if(CorrectARGc != argc) { // If our command line arguments | |||||
displayHelp(); // don't look right then display | |||||
return 0; // our help screen. | |||||
} | |||||
bool DebugMode = false; // This will be our debug mode. | |||||
string argv0(argv[0]); // Capture how we were called. | |||||
if( | |||||
string::npos != argv0.find("Debug") || // If we find "Debug" or | |||||
string::npos != argv0.find("debug") // "debug" in our command path | |||||
) { // then we are in DebugMode. | |||||
DebugMode = true; // Set the flag and tell the | |||||
cout << "Debug Mode" << endl; // watchers. | |||||
} | |||||
sigset_t SignalSet; // Set of signals to mask. | |||||
SignalCatcher *SigCatcher; // Object to catch signal. | |||||
try { // Catch anything that breaks loose. | |||||
int PthreadStatus; | |||||
if ( (0 != sigemptyset(&SignalSet)) || // Mask signal for this thread and | |||||
// all threads to be created. | |||||
(0 != sigaddset(&SignalSet, ShutdownSignal)) ) { | |||||
ostringstream Temp; | |||||
Temp << "Error masking signal on startup: " << strerror(errno) << "."; | |||||
throw runtime_error(Temp.str()); | |||||
} | |||||
PthreadStatus = pthread_sigmask(SIG_BLOCK, &SignalSet, 0); | |||||
if (0 != PthreadStatus) { | |||||
ostringstream Temp; | |||||
Temp << "Error masking signal on startup (pthread_sigmask): " << strerror(PthreadStatus) << "."; | |||||
throw runtime_error(Temp.str()); | |||||
} | |||||
snf_RulebaseHandler* MilterRulebase = new snf_RulebaseHandler(); // Allocate a rulebase handler. | |||||
MilterRulebase->PlatformVersion(SNF_MILTER_VERSION); // Record our version identification. | |||||
XCIShutdownWatcher ShutdownWatcher; // Make a server shutdown processor | |||||
MilterRulebase->XCIServerCommandHandler(ShutdownWatcher); // and register it with the engine. | |||||
MilterRulebase->open(argv[1], "", ""); // Open the rulebase. | |||||
SNFMilterContextPool* MilterContexts = | |||||
new SNFMilterContextPool(MilterRulebase); // Create the Milter Context Pool. | |||||
SigCatcher = new SignalCatcher(&SignalSet, MilterRulebase); // Create thread object to catch the signal. | |||||
runLibMilter(MilterContexts, DebugMode); // Run the milter. | |||||
ThreadState const &SigCatcherState = SigCatcher->MyState(); | |||||
if (Thread::ThreadStarted.Name == SigCatcherState.Name) { // Is the signal catcher thread running? | |||||
PthreadStatus = pthread_kill(SigCatcher->getMyThread(), ShutdownSignal); // Yes. Send it a signal. | |||||
if (0 == PthreadStatus) { | |||||
SigCatcher->join(); // Wait for signal catcher to complete. | |||||
} else { | |||||
ostringstream Temp; | |||||
Temp << "Error terminating signal catcher: " << strerror(PthreadStatus) << "."; | |||||
throw runtime_error(Temp.str()); | |||||
} | |||||
} | |||||
delete MilterContexts; // Destroy the context pool. | |||||
MilterContexts = 0; // Forget it. | |||||
MilterRulebase->close(); // Close down the rulebase handler. | |||||
delete MilterRulebase; // Destroy the rulebase. | |||||
MilterRulebase = 0; // Forget it. | |||||
delete SigCatcher; // Destroy the signal catcher. | |||||
SigCatcher = 0; // Forget it. | |||||
} // That's all folks. | |||||
catch(exception& e) { // Report any normal exceptions. | |||||
cerr << "SNFMilter Exception: " << e.what() << endl; | |||||
} | |||||
catch(...) { // Report any unexpected exceptions. | |||||
cerr << "SNFMilter Panic! Unknown Exception!" << endl; | |||||
} | |||||
return 0; // Normally we return zero. | |||||
} | |||||