// 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 #include #include #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 = 256; // Number of valid result codes. const int MinErrorResultCode = 64; // Minimum result code for Error action. const int MaxErrorResultCode = 99; // Maximum result code for Error action. 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. // // 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: codedweller::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. codedweller::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(); // Map the scan result to the libmilter return value. sfsistat smfisReturn(SNFMilterAction); // Scanning engine. SNFMilterEngine milterEngine; string getLocalReceivedHeader(); // Connection data. struct SNFMilterConnectionData { string HostName; codedweller::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: codedweller::Mutex ContextAllocationControl; // Protects context allocation. vector ContextPool; // Contains all created contexts. codedweller::ProductionQueue 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