12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403 |
- // SNFMilter.cpp
- // Copyright (C) 2008 ARM Research Labs, LLC.
- // See www.armresearch.com for the copyright terms.
- //
- // This file contains the "guts" of the SNFMilter interface. Specifically,
- // the SNFMilter() function.
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <unistd.h>
- #include <grp.h>
-
- #include "SNFMulti.hpp"
- #include "configuration.hpp"
- #include "SNFMilter.hpp"
- #include "config.h"
-
- #include <syslog.h>
- #include <sstream>
-
- #if SMFI_VERSION > 3
- #define NEW_LIBMILTER
- #endif
-
- SNFMilterContextPool* MilterContexts = 0; // The global contexts handle.
- bool MilterDebugMode; // True if debug mode is on.
- sfsistat SkipReturn = SMFIS_CONTINUE; // libmilter return value when further
- // callbacks of the same type are to be skipped.
-
- /// Get the connection context object.
- //
- // \param[in] Ctx is the libmilter context object.
- //
- // \returns the pointer to the connection context object.
- //
- // \throws runtime_error if the obtained pointer is 0.
- //
- SNFMilterContext*
- getContextFromCtx(SMFICTX* Ctx) {
- SNFMilterContext* Context = (SNFMilterContext*) smfi_getpriv(Ctx); // Get the context object.
- if(0 == Context) throw runtime_error("Got NULL from smfi_getpriv()");
- return Context;
- }
-
- /// Get a new connection object and assign it to the context.
- //
- // \param[in] Ctx is the libmilter context object.
- //
- // \returns the pointer to the connection context object.
- //
- // \throws runtime_error if the obtained pointer is 0.
- //
- SNFMilterContext*
- assignContextToCtx(SMFICTX* Ctx) {
- SNFMilterContext* Context = MilterContexts->grab(); // Get any existing context object.
- if(0 == Context) // Check address.
- throw runtime_error("Got NULL from MilterContexts->grab()");
- smfi_setpriv(Ctx, Context); // Save the context object.
- Context->ConnectionData.clear(); // Clear the connection context object.
- return Context;
- }
-
- // Function to output an info message.
- void logInfo(const string ContextName, int Code, std::string Message) {
- cout << ContextName << " (code " << Code << "): " << Message << endl;
- #if 0
- syslog(LOG_MAIL | LOG_DEBUG, "%s", Message.c_str()); // Output to system mail log.
- #endif
- MilterContexts->logThisInfo(ContextName, Code, Message); // Log message.
- }
-
- // Function to output an error message.
- void logError(const string &ContextName, const int &Code, const std::string &Message) {
- cout << ContextName << " (code " << Code << "): " << Message << endl;
- #if 0
- syslog(LOG_MAIL | LOG_DEBUG, "%s", Message.c_str()); // Output to system mail log.
- #endif
- MilterContexts->logThisError(ContextName, Code, Message); // Log message.
- }
-
- class ResultActionHandler : public codedweller::Configurator {
- private:
- SNFMilterEngine* Target;
-
- public:
- ResultActionHandler(SNFMilterEngine* T) : Target(T) {}
- void operator()(codedweller::ConfigurationElement& E, codedweller::ConfigurationData& D) {
- Target->setResultAction(
- Code,
- (Action >= Allow && Action <= Quarantine) ?
- static_cast<SNFMilterAction>(Action)
- : Allow
- );
- }
- int Code;
- int Action;
- };
-
- const int NoSuchCode = -1; // Magic number for no such code
- const int NoSuchAction = -1; // Magic number for no such action
-
- void SNFMilterEngine::readConfiguration() { // Parse the configuration.
- string NewConfiguration = myRulebase->PlatformConfiguration(); // Get the latest configuration.
- if(0 != RunningConfiguration.compare(NewConfiguration)) { // If it does not match, read it!
- RunningConfiguration = NewConfiguration; // Capture the latest.
- for(int i = 1; i < ResultCodesCount; i++) { // Init Result/Action config.
- if ( (i >= MinErrorResultCode) && (i <= MaxErrorResultCode) )
- ResultActions[i] = Error;
- else
- ResultActions[i] = NoAction;
- }
- ResultActions[0] = Allow;
- NonZeroAction = NoAction; // NoAction if no configuration for
- // non-zero result.
- ResultActionHandler ResultActionConfigurator(this); // Create a Result/Action handler.
- codedweller::ConfigurationElement Reader("milter"); // Create a configuration reader.
- Reader
- .Element("connect")
- .Element("white")
- .Attribute("action", reinterpret_cast<int&>(WhiteAction), static_cast<int>(Allow))
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("white")
- .Element("caution")
- .Attribute("action", reinterpret_cast<int&>(CautionAction), static_cast<int>(Allow))
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("caution")
- .Element("black")
- .Attribute("action", reinterpret_cast<int&>(BlackAction), static_cast<int>(Allow))
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("black")
- .Element("truncate")
- .Attribute("action", reinterpret_cast<int&>(TruncateAction), static_cast<int>(Allow))
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("truncate")
- .End("connect")
- .Element("scan")
- .Element("result")
- .atEndCall(ResultActionConfigurator)
- .Attribute("code", ResultActionConfigurator.Code, NoSuchCode)
- .Attribute("action", ResultActionConfigurator.Action, NoSuchAction)
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("result")
- .Element("nonzero")
- .Attribute("action", reinterpret_cast<int&>(NonZeroAction), static_cast<int>(NoAction))
- .Mnemonic("Allow", AllowActionMnemonic)
- .Mnemonic("Accept", AcceptActionMnemonic)
- .Mnemonic("Retry", RetryActionMnemonic)
- .Mnemonic("Reject", RejectActionMnemonic)
- .Mnemonic("Discard", DiscardActionMnemonic)
- .Mnemonic("Quarantine", QuarantineActionMnemonic)
- .End("nonzero")
- .End("scan")
- .End("milter");
- codedweller::ConfigurationData ConfigurationData( // Convert our configuration string
- NewConfiguration.c_str(), // to a configuration data buffer.
- NewConfiguration.length());
-
- Reader.initialize(); // Initialize the defaults.
- Reader.interpret(ConfigurationData); // Read the new configuration.
- }
-
- }
-
- void SNFMilterEngine::setResultAction(int Result, SNFMilterAction Action) { // Set a result / action pair.
- if(
- 0 <= Result &&
- ResultCodesCount > Result // If the Result code is in
- ) { ResultActions[Result] = Action; } // range then set the action.
- }
-
- void SNFMilterEngine::checkConfiguration() { // Reload the config if it is old.
- if(ConfigurationCheckTime.isExpired()) readConfiguration();
- }
-
- SNFMilterEngine::SNFMilterEngine(snf_RulebaseHandler* R) : // Construct the engine.
- myRulebase(R), // Remember our rulebase.
- myEngine(0), // We need to set this later.
- WhiteAction(Allow), // Initialize our default actions.
- CautionAction(Allow),
- BlackAction(Allow),
- TruncateAction(Allow),
- ConfigurationCheckTime(ConfigurationLifetime)
- {
- myEngine = new snf_EngineHandler(); // Create an engine handler.
- myEngine->open(myRulebase); // Connect it to the rulebase.
- readConfiguration(); // Read our configuration.
- }
-
- SNFMilterEngine::~SNFMilterEngine() { // Destroy the engine.
- try {
- codedweller::ScopeMutex EngineLock(ConfigMutex); // Don't die while scanning.
- if(myEngine) { // If we're not dead then die.
- myEngine->close(); // Close the engine.
- delete myEngine; // Delete it.
- myEngine = 0; // Forget it.
- myRulebase = 0; // Forget (don't delete) this too.
- }
- }
- catch(...) {} // Silently capture exceptions.
- }
-
- SNFMilterAction SNFMilterEngine::scanIP(unsigned long int IP) { // Scans an IP.
- IPTestRecord Tester(IP); // Make up a test record for this IP.
- codedweller::ScopeMutex ConfigurationLock(ConfigMutex); // Lock our configuration.
- if(0 == myEngine) throw runtime_error("Null engine when scanning IP"); // Skip safely if we're down.
- checkConfiguration(); // Re-read our config if it is old.
- myRulebase->performIPTest(Tester); // Tun it past the engine.
- SNFMilterAction TestResult = Allow; // Allow by default.
- switch(Tester.R) { // Convert the result to an action.
- case snfIPRange::White: { TestResult = WhiteAction; break; } // If the IP scan range is recognized
- case snfIPRange::Caution: { TestResult = CautionAction; break; } // in our configuration then we will
- case snfIPRange::Black: { TestResult = BlackAction; break; } // return the action code that is
- case snfIPRange::Truncate: { TestResult = TruncateAction; break; } // configured. Otherwise we will return
- default: break;
- } // the default "Allow" action.
- return TestResult; // Tell them what we've got.
- }
-
- SNFMilterAction SNFMilterEngine::scanMessage( // Scans a message.
- const unsigned char* bfr, // Requires a pointer to the buffer.
- int length) { // Requires the buffer length.
- codedweller::ScopeMutex ConfigurationLock(ConfigMutex); // Lock the configuration.
- if(0 == myEngine) throw runtime_error("Null engine when scanning message"); // Skip safely if we're down.
- checkConfiguration(); // Re-read our config if it is old.
- int R = myEngine->scanMessage(bfr, length, "", 0); // Scan the message & get the result.
- if(0 > R || ResultCodesCount <= R) return Error; // If R is out of range, return Error.
- if (0 == R || NoAction != ResultActions[R]) return ResultActions[R]; // Return the translated action.
- return NonZeroAction;
- }
-
- string SNFMilterEngine::XHeaders() { // Return X headers from last scan.
- codedweller::ScopeMutex EngineLock(ConfigMutex); // Use myEngine safely.
- if(0 == myEngine) return ""; // Return no headers if dead.
- return myEngine->getXHDRs(); // Simply return them.
- }
-
- //// SNFMilterContext
-
- SNFMilterContext::SNFMilterContext(snf_RulebaseHandler *myRulebase) :
- milterEngine(myRulebase),
- MessageData(MailBufferReserveSize) {}
-
- SNFMilterContext::~SNFMilterContext() {}
-
- /// Return the local received header.
- //
- // \returns the local received header.
- //
- string
- SNFMilterContext::getLocalReceivedHeader() {
-
- string locHeader;
-
- locHeader = "Received: from " + ConnectionData.HostName;
- locHeader += " [" + (string) ConnectionData.HostIP;
- locHeader += "] by " PACKAGE_NAME;
- return locHeader;
- }
-
- /// Map return value from SNFMilterEngine scan to libmilter return value.
- //
- // \param[in] MilterAction is the return from an SNFMilterEngine scan.
- //
- // \returns the return value for the libmilter callback.
- //
- // \throws std::out_of_range if MilterAction of out of range.
- //
- sfsistat
- SNFMilterContext::smfisReturn(SNFMilterAction MilterAction) {
-
- static const sfsistat ReturnValue[] = {
- SMFIS_CONTINUE,
- SMFIS_ACCEPT,
- SMFIS_TEMPFAIL,
- SMFIS_REJECT,
- SMFIS_DISCARD,
- SMFIS_CONTINUE
- };
-
- if ( (MilterAction < 0) || (MilterAction >= NMilterActions) ) {
- ostringstream Temp;
- Temp << "Illegal value of SNFMilterAction in SNFMilterContext::smfisReturn ("
- << MilterAction << ") while processing message from "
- << MessageData.SenderAddress
- << " (connection from " << ConnectionData.HostName << " ("
- << (string) ConnectionData.HostIP << ").";
- throw std::out_of_range(Temp.str());
- }
-
- return ReturnValue[MilterAction];
-
- }
-
- //// SNFMilterContextPool
-
- SNFMilterContextPool::SNFMilterContextPool(snf_RulebaseHandler* Rulebase) : // Ctor needs a live rulebase handler.
- myRulebase(Rulebase), // Capture the rulebase handler.
- MilterSocketPort(0) {
-
- string NewConfiguration = myRulebase->PlatformConfiguration(); // Get the latest configuration.
- codedweller::ConfigurationElement Reader("milter"); // Create a configuration reader.
- Reader
- .Element("socket")
- .Attribute("type", reinterpret_cast<int&>(MilterSocketType), static_cast<int>(NOMilterSocket))
- .Mnemonic("unix", UNIXMilterSocketMnemonic)
- .Mnemonic("tcp", TCPMilterSocketMnemonic)
- .Attribute("path", MilterSocketPath, "")
- .Attribute("group", MilterSocketGroup, "")
- .Attribute("ip", MilterSocketIP, "")
- .Attribute("port", MilterSocketPort, 0)
- .End("socket")
- .End("milter");
-
- codedweller::ConfigurationData ConfigurationData( // Convert our configuration string
- NewConfiguration.c_str(), // to a configuration data buffer.
- NewConfiguration.length());
-
- Reader.initialize(); // Initialize the defaults.
- Reader.interpret(ConfigurationData); // Read the new configuration.
-
- switch (MilterSocketType) { // Named pipe.
- case UNIXMilterSocket:
- if (0 == MilterSocketPath.size()) { // Path specified?
- ostringstream Temp; // No.
- Temp << "Path needs to be specified for socket type \"unix\"";
- throw runtime_error(Temp.str());
- }
- if ( (0 != MilterSocketIP.size()) || // These should not be specified.
- (0 != MilterSocketPort) ) {
- ostringstream Temp;
- Temp << "IP (" << MilterSocketIP << ") and/or port (" << MilterSocketPort
- << ") were specified for socket type \"unix\". They should not be specified.";
- throw runtime_error(Temp.str());
- }
-
- break;
- case TCPMilterSocket:
- if (0 == MilterSocketIP.size()) { // IP/host specified?
- ostringstream Temp; // No.
- Temp << "Host or IP address needs to be specified for socket type \"inet\"";
- throw runtime_error(Temp.str());
- }
- if (0 == MilterSocketPort) { // Port specified?
- ostringstream Temp; // No.
- Temp << "Port needs to be specified for socket type \"inet\"";
- throw runtime_error(Temp.str());
- }
- if ( (0 != MilterSocketPath.size()) || // These should not be specified.
- (0 != MilterSocketGroup.size()) ) {
- ostringstream Temp;
- Temp << "Path (" << MilterSocketPath << ") and/or group (" << MilterSocketGroup
- << ") were specified for socket type \"inet\". They should not be specified.";
- throw runtime_error(Temp.str());
- }
-
- break;
-
- case NOMilterSocket:
- {
- ostringstream Temp;
- Temp << "The required <socket> element was not present in the configuration file.";
- throw runtime_error(Temp.str());
- }
-
- break;
-
- default:
- {
- ostringstream Temp;
- Temp << "The type of the <socket> element configuration file is invalid. "
- "The type must by \"unix\" or \"inet\"";
- throw runtime_error(Temp.str());
- }
- }
- }
-
- SNFMilterSocketType SNFMilterContextPool::getSocketType() {
- return MilterSocketType;
- }
-
- string SNFMilterContextPool::getSocketPath() { return MilterSocketPath; }
-
- string SNFMilterContextPool::getSocketGroup() { return MilterSocketGroup; }
-
- string SNFMilterContextPool::getSocketIP() { return MilterSocketIP; }
-
- int SNFMilterContextPool::getSocketPort() { return MilterSocketPort; }
-
- SNFMilterContextPool::~SNFMilterContextPool() { // Dtor gracefully discards contexts.
- codedweller::ScopeMutex ContextPoolLock(ContextAllocationControl); // Lock the context allocation system.
- myRulebase = 0; // Forget our rulebase. We're dead.
- for( // Loop through the context pool
- vector<SNFMilterContext*>::iterator iC = ContextPool.begin(); // and delete any contexts we have
- iC != ContextPool.end(); // allocated.
- iC++) { delete (*iC); }
- }
-
- SNFMilterContext* SNFMilterContextPool::grab() { // Get a context to use.
- codedweller::ScopeMutex ContextPoolLock(ContextAllocationControl); // Lock the context allocation system.
- if(0 == myRulebase) return 0; // No contexts left if we're dead.
- if(1 > AvailableContexts.size()) { // If we need more contexts then
- SNFMilterContext* N = new SNFMilterContext(myRulebase); // Create a new context,
- ContextPool.push_back(N); // add it to the pool,
- AvailableContexts.give(N); // and make it available.
- }
- return AvailableContexts.take(); // Return the next avialable context.
- }
-
- void SNFMilterContextPool::drop(SNFMilterContext* E) { // Drop a context after use.
- // Update context state.
- E->State = SNFMilterContext::Pooled;
-
- AvailableContexts.give(E); // Make this context available.
- }
-
- bool SNFMilterContextPool::allUnused() {
- return (AvailableContexts.size() == ContextPool.size());
- }
-
- void SNFMilterContextPool::logThisError(string ContextName, int Code, string Text) {
- myRulebase->logThisError(ContextName, Code, Text);
- }
-
- void SNFMilterContextPool::logThisInfo(string ContextName, int Code, string Text) {
- myRulebase->logThisInfo(ContextName, Code, Text);
- }
-
- // End of configuration setup and engine and context interface components
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // SNFMilter callback definitions, constants, and global data.
-
- extern "C" {
-
- // Connection callback.
- //
- // This callback is invoked when a new connection is made to the
- // MTA. It obtains an available connection context object if one
- // hasn't already been assigned, and saves the IP address and name
- // of the connecting MTA. Next, it performs an IP scan, and
- // returns the appropriate response.
- //
- // Returns: SMFIS_CONTINUE, SMFIS_ACCEPT, SMFIS_TEMPFAIL, or
- // SMFIS_REJECT according to the following mapping:
- //
- // IPScanResult return mlfi_connect return
- //
- // Quarantine, Discard, Error FailSafeMilterResponse
- //
- // Anything else SNFMilterContext::smfisReturn(IPScanResult)
- //
- sfsistat
- mlfi_connect(SMFICTX *Ctx, char *HostName, _SOCK_ADDR *HostAddr)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_connect";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = assignContextToCtx(Ctx); // Get the existing context object,
- // or assign a new context object.
-
- Context->State = SNFMilterContext::Connect; // Update context state.
-
- sockaddr_in *SaIn = (sockaddr_in *) HostAddr; // Fetch the IP address.
-
- Context->ConnectionData.HostName = HostName; // Load the info.
-
- if (0 == SaIn) { // If HostAddr is 0...
-
- Context->ConnectionData.HostIP = "127.0.0.1"; // Set to a valid value.
-
- } else {
-
- Context->ConnectionData.HostIP = ntohl(SaIn->sin_addr.s_addr);
-
- }
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Connect from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ").";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- if (0 == SaIn) { // If HostAddr is 0, don't do a scan.
-
- return FailSafeMilterResponse;
-
- }
-
- SNFMilterAction IpScanResult; // Perform IP scan.
-
- IpScanResult = Context->milterEngine.scanIP(Context->ConnectionData.HostIP);
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "IP scan result for connection from "
- << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << "): " << IpScanResult << ".";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- if ( (Error == IpScanResult) || // Check for error
- (Quarantine == IpScanResult) ||
- (Discard == IpScanResult) ) {
- std::ostringstream Temp; // Illegal result.
- Temp << "Illegal result from IP scan for "
- << (string) Context->ConnectionData.HostIP
- << ": " << IpScanResult;
- throw std::runtime_error(Temp.str());
- }
-
- CallbackResult = Context->smfisReturn(IpScanResult); // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // HELO callback.
- //
- // This callback is invoked when the connecting MTA sends a HELO
- // message. It saves the argument of the HELO command in the
- // connection context object.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_helo(SMFICTX *Ctx, char *heloHost)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_helo";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
-
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Helo; // Update context state.
-
- Context->ConnectionData.HostHelo = heloHost; // Save the helo host name.
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "HELO " << Context->ConnectionData.HostHelo << " from "
- << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ").";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // envfrom callback.
- //
- // This callback is invoked to process the envelope from line of the
- // mail message. It clears the message buffer, and adds the local
- // received header to the message buffer to scan. The local received
- // header is added to the email message in mlfi_eom.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_envfrom(SMFICTX *Ctx, char **argv)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_envfrom";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::EnvFrom; // Update context state.
-
- Context->MessageData.clear(); // This is the beginning of a new message.
- // Clear data from any previous message.
-
- if (MilterDebugMode) {
- Context->MessageData.SenderAddress = argv[0];
- ostringstream Temp;
- Temp << "Processing sender address " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).\n";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // envrcpt callback.
- //
- // This callback is invoked to process the envelope receipt line of
- // the mail message.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_envrcpt(SMFICTX *Ctx, char **argv)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_envrcpt";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- // Update context state.
- Context->State = SNFMilterContext::EnvRcpt;
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Processing recipient " << argv[0] << " for message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // header callback.
- //
- // This callback is invoked to process the a header line of the mail
- // message. It writes the header line + SMTPENDL to the message buffer
- // that is scanned.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_header(SMFICTX *Ctx, char *headerf, char *headerv)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_header";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Header; // Update context state.
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Processing header '" << headerf << ": " << headerv
- << "' for message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
-
- Context->MessageData.MessageBuffer += headerf; // Add the header.
- Context->MessageData.MessageBuffer += ": ";
- Context->MessageData.MessageBuffer += headerv;
- Context->MessageData.MessageBuffer += SMTPENDL;
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // End of header callback.
- //
- // This callback is invoked after the last header of the mail
- // message is sent. It writes SMTPENDL SMTPENDL to the message
- // buffer that is scanned.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_eoh(SMFICTX *Ctx)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_eoh";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::EOH; // Update context state.
-
- Context->MessageData.MessageBuffer += SMTPENDL + SMTPENDL; // Add the blank lines..
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "All headers received for message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")). Message buffer--\n'"
- << Context->MessageData.MessageBuffer << "'.";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // message body callback.
- //
- // This callback is invoked zero or more times to send the body of the
- // mail message is sent. It writes the body to the message buffer that
- // is scanned.
- //
- // Returns: SMFIS_CONTINUE if more of the message body is needed,
- // SkipReturn if more of the message body is not needed, or
- // FailSafeMilterResponse if an error occurs. Context is the
- // connection context object.
- //
- sfsistat
- mlfi_body(SMFICTX *Ctx, unsigned char *Bodyp, size_t Bodylen)
- {
-
- const string ContextName = PACKAGE_NAME "::mlfi_body";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Body; // Update context state.
-
- if (Context->MessageData.MessageBuffer.size() >= // Return if there is enough in the
- MailBufferReserveSize) { // message buffer.
-
- return SkipReturn;
-
- }
-
- if (Context->MessageData.MessageBuffer.size() < // Do we need to copy this?
- MailBufferReserveSize) {
-
- string::size_type NCharToTransfer = MailBufferReserveSize - // Yes. How much more?
- Context->MessageData.MessageBuffer.size();
-
- if (NCharToTransfer > Bodylen) { // Don't transfer more characters
- // than are available.
- NCharToTransfer = Bodylen;
- }
-
- Context->MessageData.MessageBuffer.append((const char *) Bodyp, // Append the message.
- NCharToTransfer);
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Appended " << NCharToTransfer << " bytes to "
- << "message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")). Message length: "
- << Context->MessageData.MessageBuffer.size() << ".";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- } else {
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Discarded " << Bodylen << " bytes "
- << "message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")) because "
- << MailBufferReserveSize << " bytes have already been "
- << "transferred. Message length: "
- << Context->MessageData.MessageBuffer.size() << ".";
- logInfo(ContextName, InfoCode, Temp.str());
- }
- }
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // End-Of-Message callback.
- //
- // This callback is invoked to when the entire message has been sent
- // to SNFMilter. It adds the local received header to the email
- // message, and then scans the message body. If the scan result
- // indicates that the message is to be quarantined, then the message
- // is set to be quarantined (using smfi_quarantine).
- //
- // Returns: SMFIS_CONTINUE, SMFIS_ACCEPT, SMFIS_TEMPFAIL, or
- // SMFIS_DISCARD, SMFIS_REJECT, or FailSafeMilterResponse
- // according to the following mapping:
- //
- // MessageScan return mlfi_connect return
- //
- // Error FailSafeMilterResponse
- //
- // Anything else SNFMilterContext::smfisReturn(IPScanResult)
- //
- // Side effect: If the MessageScan result is Quarantine, the
- // message is quarantined using smfi_quarantine().
- //
- sfsistat
- mlfi_eom(SMFICTX *Ctx)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_eom";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::EOM; // Update context state.
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "End of message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
-
- if (PrependLocalReceivedHeader) {
- Context->MessageData.MessageBuffer.
- insert(0, Context->getLocalReceivedHeader() + SMTPENDL); // Prepend local received header line
- // to the message buffer.
- }
-
- SNFMilterAction MsgScanResult; // Perform scan.
-
- MsgScanResult =
- Context->milterEngine.scanMessage((unsigned char *) Context->MessageData.MessageBuffer.c_str(),
- Context->MessageData.MessageBuffer.size());
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Message scan result for message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")): " << MsgScanResult << ".";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
-
- if (Error == MsgScanResult) { // Check for scan error
-
- // Illegal result.
- std::ostringstream Temp;
-
- Temp << "Illegal message scan result for message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")): " << MsgScanResult;
- throw std::runtime_error(Temp.str());
-
- }
-
- string XHeaders = Context->milterEngine.XHeaders(); // Fetch X-headers to submit.
- MailHeaders MailHeadersParse; // Object to parse X-headers.
-
- MailHeadersParse.loadHeaders(XHeaders); // Load the headers to be parsed.
-
- string HeaderName; // Name of X-header.
- string HeaderBody; // Body of X-header, formatted for libmilter.
-
- while (MailHeadersParse.getNameBody(&HeaderName, &HeaderBody)) { // While there is an X-Header...
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Processed X-Header for message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")). X-Header name: '"
- << HeaderName << "'. X-Header body: '" << HeaderBody << "'.";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- if (MI_SUCCESS != smfi_addheader(Ctx, // Add header to the email message.
- (char *) HeaderName.c_str(),
- (char *) HeaderBody.c_str())) {
- ostringstream Temp;
- Temp << "Error adding X-header to message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP
- << ")).\nX-Header name: '" << HeaderName << "'. X-Header body--\n'"
- << HeaderBody << "'.";
- logError(ContextName, 1, Temp.str());
- }
- }
-
- if (Quarantine == MsgScanResult) { // Quarantine the message?
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Quarantining message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- if (MI_SUCCESS != smfi_quarantine(Ctx,
- (char *) "Quarantined by " PACKAGE_NAME)) {
-
- ostringstream Temp;
- Temp << "Error quarantining message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logError(ContextName, 1, Temp.str());
-
- }
-
- return SMFIS_CONTINUE;
- }
-
- CallbackResult = Context->smfisReturn(MsgScanResult); // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // Callback for aborting the message.
- //
- // This callback is invoked to when the processing of the current
- // message is to be aborted. It logs the abort as an info event,
- // clears the email message buffer, and sets the state to
- // connected.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_abort(SMFICTX *Ctx)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_abort";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Connect; // Update context state.
-
- ostringstream Temp; // Log message.
- Temp << "Aborted processing of message from " << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
-
- Context->MessageData.clear(); // Clear data for this message.
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
-
- }
-
- //
- // Callback for closing the connection.
- //
- // This callback is invoked to when the connection with the remote MTA is closed.
- // It returns the connection context object to the pool.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_close(SMFICTX *Ctx)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_close";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Close; // Update context state.
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "Closing connection from "
- << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ").";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
-
- MilterContexts->drop(Context); // Return the context object.
-
- smfi_setpriv(Ctx, 0);
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
-
- }
-
- #ifdef NEW_LIBMILTER
- //
- // Callback for unknown SMTP command.
- //
- // This callback is invoked to when an unknown SMTP command is
- // received by the local MTA. The unknown command is logged.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_unknown(SMFICTX *Ctx, const char *Cmd)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_unknown";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- ostringstream Temp; // Log.
- Temp << "Unknown SMTP command from "
- << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << "): '"
- << *Cmd << "'";
- logInfo(ContextName, InfoCode, Temp.str());
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
-
- }
-
- //
- // data callback.
- //
- // This callback is invoked when the connecting MTA sends a DATA
- // message.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_data(SMFICTX *Ctx)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_data";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = FailSafeMilterResponse;
-
- try {
- SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
-
- Context->State = SNFMilterContext::Data; // Update context state.
-
- if (MilterDebugMode) {
- ostringstream Temp;
- Temp << "DATA for message from "
- << Context->MessageData.SenderAddress
- << " (connection from " << Context->ConnectionData.HostName << " ("
- << (string) Context->ConnectionData.HostIP << ")).";
- logInfo(ContextName, InfoCode, Temp.str());
- }
-
- CallbackResult = SMFIS_CONTINUE; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
- }
-
- //
- // Callback for negotiating the capabilities of the MTA.
- //
- // This callback is invoked at the start of each SMTP connection.
- // It obtains an available connection context object if one hasn't
- // already been assigned, checks whether the MTA can accept an
- // SMFIS_SKIP return, and configures the connection context object
- // to return an acceptable value.
- //
- // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
- // otherwise.
- //
- sfsistat
- mlfi_negotiate(SMFICTX *Ctx,
- unsigned long F0,
- unsigned long F1,
- unsigned long F2,
- unsigned long F3,
- unsigned long *PF0,
- unsigned long *PF1,
- unsigned long *PF2,
- unsigned long *PF3)
- {
- const string ContextName = PACKAGE_NAME "::mlfi_negotiate";
- int ErrorCode = 1;
- int InfoCode = 1;
- sfsistat CallbackResult = SMFIS_ALL_OPTS;
-
- try {
- bool AcceptsSkip = F1 & SMFIP_SKIP;
- if (AcceptsSkip) { // MTA accepts SMFIS_SKIP return?
- SkipReturn = SMFIS_SKIP; // Yes. Use SMFIS_SKIP.
- } else {
- SkipReturn = SMFIS_CONTINUE; // No. Use SMFIS_CONTINUE.
- }
-
- if (MilterDebugMode) {
- ostringstream Temp; // Log message.
- Temp << "MTA does " << (AcceptsSkip ? "" : "not ") << "accept SMFIS_SKIP.";
- logInfo(ContextName, InfoCode, Temp.str());
- }
- CallbackResult = SMFIS_ALL_OPTS; // Load return value.
- } catch (exception &E) {
- logError(ContextName, ErrorCode, E.what());
- } catch (...) {
- logError(ContextName, ErrorCode, UnknownExceptionMessage);
- }
-
- return CallbackResult;
-
- }
-
- #endif
-
- }
-
- // End of SNFMilter callback definitions, constants, and global data.
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // SNFMilter setup & run function.
-
- // 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.
-
- void runLibMilter(SNFMilterContextPool* Contexts, bool DebugMode) { // Run the milter 'til it's done.
-
- MilterContexts = Contexts; // Save the pool of context objects.
- MilterDebugMode = DebugMode; // Save the debug mode flag.
-
- #if 0
- if (MilterDebugMode) {
- openlog(PACKAGE_NAME, LOG_PID | LOG_PERROR, LOG_MAIL); // Initialize system logging to log
- // messages to mail file.
- }
- #endif
-
- struct smfiDesc smfilter = { // Load structure containing callbacks.
- (char *) PACKAGE_NAME, // Filter name.
- SMFI_VERSION, // Version code -- do not change.
- SMFIF_ADDHDRS | SMFIF_QUARANTINE, // Flags.
- mlfi_connect, // Connection info callback.
- mlfi_helo, // SMTP HELO command callback.
- mlfi_envfrom, // Envelope sender callback.
- mlfi_envrcpt, // Envelope recipient callback.
- mlfi_header, // Header callback.
- mlfi_eoh, // End of headers callback.
- mlfi_body, // Body block callback.
- mlfi_eom, // End of message callback.
- mlfi_abort, // Message abort callback.
- mlfi_close // Connection closed callback.
- #ifdef NEW_LIBMILTER
- ,
- mlfi_unknown, // Unknown SMTP command callback.
- mlfi_data, // DATA ccallback.
- mlfi_negotiate // Negotiation at the start of each SMTP
- #endif
- // connection callback.
- };
-
- string MilterConnSpec;
-
- switch (Contexts->getSocketType()) { // Configure the connection.
- case UNIXMilterSocket:
-
- MilterConnSpec = "unix:" + Contexts->getSocketPath(); // Generate the connection spec.
-
- break;
-
- case TCPMilterSocket:
- {
- ostringstream Temp;
-
- Temp << "inet:" << Contexts->getSocketPort() << "@" // Generate the connection spec.
- << Contexts->getSocketIP();
- MilterConnSpec = Temp.str();
- }
-
- break;
-
- default:
- {
- ostringstream Temp;
- Temp << PACKAGE " internal error: Invalid socket type from SNFMilterContextPool.";
- throw logic_error(Temp.str());
- }
- }
-
- if (MI_FAILURE == smfi_setconn(const_cast<char *>(MilterConnSpec.c_str()))) {
- ostringstream Temp;
- Temp << "smfi_setconn failed with input \"" << MilterConnSpec << "\"";
- throw std::runtime_error(Temp.str());
- }
-
- if (MI_FAILURE == smfi_register(smfilter)) { // Register the callbacks.
- string msg = "smfi_register failed";
- throw std::runtime_error(msg);
- }
-
- if (UNIXMilterSocket == Contexts->getSocketType()) {
- if (MI_FAILURE == smfi_opensocket(true)) { // Create the named pipe.
- ostringstream Temp;
- Temp << "smfi_opensocket failed for \"" << MilterConnSpec << "\"";
- throw std::runtime_error(Temp.str());
- }
-
- string MilterConnPath = Contexts->getSocketPath();
-
- if (chmod(MilterConnPath.c_str(), // Set permissions.
- S_IRUSR | S_IWUSR |
- S_IRGRP | S_IWGRP) != 0) {
- ostringstream Temp;
- Temp << "Error setting permissions of " << MilterConnPath << ": "
- << strerror(errno);
- throw runtime_error(Temp.str());
- }
-
- string GroupName = Contexts->getSocketGroup();
- if (0 != GroupName.size()) { // Was a group specified?
- struct group *Group = getgrnam(GroupName.c_str()); // Get the ID of the group.
- errno = 0;
- if (NULL == Group) {
- ostringstream Temp;
- if (0 != errno) { // Error?
- Temp << "Error obtaining the group ID of " << GroupName << ": "
- << strerror(errno);
- } else { // Group not found.
- Temp << "Error obtaining the group ID of " << GroupName << ": "
- << "No such group";
- }
- throw runtime_error(Temp.str());
- }
-
- if (chown(MilterConnPath.c_str(), // Set group.
- -1,
- Group->gr_gid) != 0) {
- ostringstream Temp;
- Temp << "Error setting group of " << MilterConnPath << " to "
- << Group->gr_name << ": " << strerror(errno);
- throw runtime_error(Temp.str());
- }
- }
- }
-
- if (MI_FAILURE == smfi_main()) { // Hand control to libmilter
- string msg = "smfi_main failed";
- throw std::runtime_error(msg);
- }
-
- const string ContextName = "--EXITING--";
- int ErrorCode = 1;
- int InfoCode = 1;
-
- logInfo(ContextName, InfoCode, "Shutdown command received. Waiting for message processing to complete...");
-
- codedweller::Sleeper WaitATic;
-
- try {
-
- WaitATic.setMillisecondsToSleep(ShutdownWaitPeriod_ms); // Learn to wait.
-
- }
- catch(...) {
-
- ostringstream Temp;
- Temp << "Invalid value for ShutdownWaitPeriod_ms: " << ShutdownWaitPeriod_ms << ".";
- throw out_of_range(Temp.str());
-
- }
-
- int iPeriod = 0; // Number of periods waited.
-
- while (!MilterContexts->allUnused() && iPeriod < ShutdownPeriods) {
-
- iPeriod++;
- WaitATic(); // Wait a period.
-
- }
-
- if (!MilterContexts->allUnused()) {
-
- logError(ContextName, ErrorCode, "Not all messages finished processing.");
-
- }
-
- logInfo(ContextName, InfoCode, "Exiting");
-
- WaitATic(); // Wait for messages to be logged.
-
- MilterContexts = 0; // Turn off.
-
- }
|