Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

SNFMilter.cpp 62KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  1. // SNFMilter.cpp
  2. // Copyright (C) 2008 ARM Research Labs, LLC.
  3. // See www.armresearch.com for the copyright terms.
  4. //
  5. // This file contains the "guts" of the SNFMilter interface. Specifically,
  6. // the SNFMilter() function.
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <errno.h>
  10. #include <unistd.h>
  11. #include <grp.h>
  12. #include "SNFMulti.hpp"
  13. #include "configuration.hpp"
  14. #include "SNFMilter.hpp"
  15. #include "config.h"
  16. #include <syslog.h>
  17. #include <sstream>
  18. #if SMFI_VERSION > 3
  19. #define NEW_LIBMILTER
  20. #endif
  21. SNFMilterContextPool* MilterContexts = 0; // The global contexts handle.
  22. bool MilterDebugMode; // True if debug mode is on.
  23. sfsistat SkipReturn = SMFIS_CONTINUE; // libmilter return value when further
  24. // callbacks of the same type are to be skipped.
  25. /// Get the connection context object.
  26. //
  27. // \param[in] Ctx is the libmilter context object.
  28. //
  29. // \returns the pointer to the connection context object.
  30. //
  31. // \throws runtime_error if the obtained pointer is 0.
  32. //
  33. SNFMilterContext*
  34. getContextFromCtx(SMFICTX* Ctx) {
  35. SNFMilterContext* Context = (SNFMilterContext*) smfi_getpriv(Ctx); // Get the context object.
  36. if(0 == Context) throw runtime_error("Got NULL from smfi_getpriv()");
  37. return Context;
  38. }
  39. /// Get a new connection object and assign it to the context.
  40. //
  41. // \param[in] Ctx is the libmilter context object.
  42. //
  43. // \returns the pointer to the connection context object.
  44. //
  45. // \throws runtime_error if the obtained pointer is 0.
  46. //
  47. SNFMilterContext*
  48. assignContextToCtx(SMFICTX* Ctx) {
  49. SNFMilterContext* Context = MilterContexts->grab(); // Get any existing context object.
  50. if(0 == Context) // Check address.
  51. throw runtime_error("Got NULL from MilterContexts->grab()");
  52. smfi_setpriv(Ctx, Context); // Save the context object.
  53. Context->ConnectionData.clear(); // Clear the connection context object.
  54. return Context;
  55. }
  56. // Function to output an info message.
  57. void logInfo(const string ContextName, int Code, std::string Message) {
  58. cout << ContextName << " (code " << Code << "): " << Message << endl;
  59. #if 0
  60. syslog(LOG_MAIL | LOG_DEBUG, "%s", Message.c_str()); // Output to system mail log.
  61. #endif
  62. MilterContexts->logThisInfo(ContextName, Code, Message); // Log message.
  63. }
  64. // Function to output an error message.
  65. void logError(const string &ContextName, const int &Code, const std::string &Message) {
  66. cout << ContextName << " (code " << Code << "): " << Message << endl;
  67. #if 0
  68. syslog(LOG_MAIL | LOG_DEBUG, "%s", Message.c_str()); // Output to system mail log.
  69. #endif
  70. MilterContexts->logThisError(ContextName, Code, Message); // Log message.
  71. }
  72. class ResultActionHandler : public codedweller::Configurator {
  73. private:
  74. SNFMilterEngine* Target;
  75. public:
  76. ResultActionHandler(SNFMilterEngine* T) : Target(T) {}
  77. void operator()(codedweller::ConfigurationElement& E, codedweller::ConfigurationData& D) {
  78. Target->setResultAction(
  79. Code,
  80. (Action >= Allow && Action <= Quarantine) ?
  81. static_cast<SNFMilterAction>(Action)
  82. : Allow
  83. );
  84. }
  85. int Code;
  86. int Action;
  87. };
  88. const int NoSuchCode = -1; // Magic number for no such code
  89. const int NoSuchAction = -1; // Magic number for no such action
  90. void SNFMilterEngine::readConfiguration() { // Parse the configuration.
  91. string NewConfiguration = myRulebase->PlatformConfiguration(); // Get the latest configuration.
  92. if(0 != RunningConfiguration.compare(NewConfiguration)) { // If it does not match, read it!
  93. RunningConfiguration = NewConfiguration; // Capture the latest.
  94. for(int i = 1; i < ResultCodesCount; i++) { // Init Result/Action config.
  95. if ( (i >= MinErrorResultCode) && (i <= MaxErrorResultCode) )
  96. ResultActions[i] = Error;
  97. else
  98. ResultActions[i] = NoAction;
  99. }
  100. ResultActions[0] = Allow;
  101. NonZeroAction = NoAction; // NoAction if no configuration for
  102. // non-zero result.
  103. ResultActionHandler ResultActionConfigurator(this); // Create a Result/Action handler.
  104. codedweller::ConfigurationElement Reader("milter"); // Create a configuration reader.
  105. Reader
  106. .Element("connect")
  107. .Element("white")
  108. .Attribute("action", reinterpret_cast<int&>(WhiteAction), static_cast<int>(Allow))
  109. .Mnemonic("Allow", AllowActionMnemonic)
  110. .Mnemonic("Accept", AcceptActionMnemonic)
  111. .Mnemonic("Retry", RetryActionMnemonic)
  112. .Mnemonic("Reject", RejectActionMnemonic)
  113. .Mnemonic("Discard", DiscardActionMnemonic)
  114. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  115. .End("white")
  116. .Element("caution")
  117. .Attribute("action", reinterpret_cast<int&>(CautionAction), static_cast<int>(Allow))
  118. .Mnemonic("Allow", AllowActionMnemonic)
  119. .Mnemonic("Accept", AcceptActionMnemonic)
  120. .Mnemonic("Retry", RetryActionMnemonic)
  121. .Mnemonic("Reject", RejectActionMnemonic)
  122. .Mnemonic("Discard", DiscardActionMnemonic)
  123. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  124. .End("caution")
  125. .Element("black")
  126. .Attribute("action", reinterpret_cast<int&>(BlackAction), static_cast<int>(Allow))
  127. .Mnemonic("Allow", AllowActionMnemonic)
  128. .Mnemonic("Accept", AcceptActionMnemonic)
  129. .Mnemonic("Retry", RetryActionMnemonic)
  130. .Mnemonic("Reject", RejectActionMnemonic)
  131. .Mnemonic("Discard", DiscardActionMnemonic)
  132. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  133. .End("black")
  134. .Element("truncate")
  135. .Attribute("action", reinterpret_cast<int&>(TruncateAction), static_cast<int>(Allow))
  136. .Mnemonic("Allow", AllowActionMnemonic)
  137. .Mnemonic("Accept", AcceptActionMnemonic)
  138. .Mnemonic("Retry", RetryActionMnemonic)
  139. .Mnemonic("Reject", RejectActionMnemonic)
  140. .Mnemonic("Discard", DiscardActionMnemonic)
  141. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  142. .End("truncate")
  143. .End("connect")
  144. .Element("scan")
  145. .Element("result")
  146. .atEndCall(ResultActionConfigurator)
  147. .Attribute("code", ResultActionConfigurator.Code, NoSuchCode)
  148. .Attribute("action", ResultActionConfigurator.Action, NoSuchAction)
  149. .Mnemonic("Allow", AllowActionMnemonic)
  150. .Mnemonic("Accept", AcceptActionMnemonic)
  151. .Mnemonic("Retry", RetryActionMnemonic)
  152. .Mnemonic("Reject", RejectActionMnemonic)
  153. .Mnemonic("Discard", DiscardActionMnemonic)
  154. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  155. .End("result")
  156. .Element("nonzero")
  157. .Attribute("action", reinterpret_cast<int&>(NonZeroAction), static_cast<int>(NoAction))
  158. .Mnemonic("Allow", AllowActionMnemonic)
  159. .Mnemonic("Accept", AcceptActionMnemonic)
  160. .Mnemonic("Retry", RetryActionMnemonic)
  161. .Mnemonic("Reject", RejectActionMnemonic)
  162. .Mnemonic("Discard", DiscardActionMnemonic)
  163. .Mnemonic("Quarantine", QuarantineActionMnemonic)
  164. .End("nonzero")
  165. .End("scan")
  166. .End("milter");
  167. codedweller::ConfigurationData ConfigurationData( // Convert our configuration string
  168. NewConfiguration.c_str(), // to a configuration data buffer.
  169. NewConfiguration.length());
  170. Reader.initialize(); // Initialize the defaults.
  171. Reader.interpret(ConfigurationData); // Read the new configuration.
  172. }
  173. }
  174. void SNFMilterEngine::setResultAction(int Result, SNFMilterAction Action) { // Set a result / action pair.
  175. if(
  176. 0 <= Result &&
  177. ResultCodesCount > Result // If the Result code is in
  178. ) { ResultActions[Result] = Action; } // range then set the action.
  179. }
  180. void SNFMilterEngine::checkConfiguration() { // Reload the config if it is old.
  181. if(ConfigurationCheckTime.isExpired()) readConfiguration();
  182. }
  183. SNFMilterEngine::SNFMilterEngine(snf_RulebaseHandler* R) : // Construct the engine.
  184. myRulebase(R), // Remember our rulebase.
  185. myEngine(0), // We need to set this later.
  186. WhiteAction(Allow), // Initialize our default actions.
  187. CautionAction(Allow),
  188. BlackAction(Allow),
  189. TruncateAction(Allow),
  190. ConfigurationCheckTime(ConfigurationLifetime)
  191. {
  192. myEngine = new snf_EngineHandler(); // Create an engine handler.
  193. myEngine->open(myRulebase); // Connect it to the rulebase.
  194. readConfiguration(); // Read our configuration.
  195. }
  196. SNFMilterEngine::~SNFMilterEngine() { // Destroy the engine.
  197. try {
  198. codedweller::ScopeMutex EngineLock(ConfigMutex); // Don't die while scanning.
  199. if(myEngine) { // If we're not dead then die.
  200. myEngine->close(); // Close the engine.
  201. delete myEngine; // Delete it.
  202. myEngine = 0; // Forget it.
  203. myRulebase = 0; // Forget (don't delete) this too.
  204. }
  205. }
  206. catch(...) {} // Silently capture exceptions.
  207. }
  208. SNFMilterAction SNFMilterEngine::scanIP(unsigned long int IP) { // Scans an IP.
  209. IPTestRecord Tester(IP); // Make up a test record for this IP.
  210. codedweller::ScopeMutex ConfigurationLock(ConfigMutex); // Lock our configuration.
  211. if(0 == myEngine) throw runtime_error("Null engine when scanning IP"); // Skip safely if we're down.
  212. checkConfiguration(); // Re-read our config if it is old.
  213. myRulebase->performIPTest(Tester); // Tun it past the engine.
  214. SNFMilterAction TestResult = Allow; // Allow by default.
  215. switch(Tester.R) { // Convert the result to an action.
  216. case snfIPRange::White: { TestResult = WhiteAction; break; } // If the IP scan range is recognized
  217. case snfIPRange::Caution: { TestResult = CautionAction; break; } // in our configuration then we will
  218. case snfIPRange::Black: { TestResult = BlackAction; break; } // return the action code that is
  219. case snfIPRange::Truncate: { TestResult = TruncateAction; break; } // configured. Otherwise we will return
  220. default: break;
  221. } // the default "Allow" action.
  222. return TestResult; // Tell them what we've got.
  223. }
  224. SNFMilterAction SNFMilterEngine::scanMessage( // Scans a message.
  225. const unsigned char* bfr, // Requires a pointer to the buffer.
  226. int length) { // Requires the buffer length.
  227. codedweller::ScopeMutex ConfigurationLock(ConfigMutex); // Lock the configuration.
  228. if(0 == myEngine) throw runtime_error("Null engine when scanning message"); // Skip safely if we're down.
  229. checkConfiguration(); // Re-read our config if it is old.
  230. int R = myEngine->scanMessage(bfr, length, "", 0); // Scan the message & get the result.
  231. if(0 > R || ResultCodesCount <= R) return Error; // If R is out of range, return Error.
  232. if (0 == R || NoAction != ResultActions[R]) return ResultActions[R]; // Return the translated action.
  233. return NonZeroAction;
  234. }
  235. string SNFMilterEngine::XHeaders() { // Return X headers from last scan.
  236. codedweller::ScopeMutex EngineLock(ConfigMutex); // Use myEngine safely.
  237. if(0 == myEngine) return ""; // Return no headers if dead.
  238. return myEngine->getXHDRs(); // Simply return them.
  239. }
  240. //// SNFMilterContext
  241. SNFMilterContext::SNFMilterContext(snf_RulebaseHandler *myRulebase) :
  242. milterEngine(myRulebase),
  243. MessageData(MailBufferReserveSize) {}
  244. SNFMilterContext::~SNFMilterContext() {}
  245. /// Return the local received header.
  246. //
  247. // \returns the local received header.
  248. //
  249. string
  250. SNFMilterContext::getLocalReceivedHeader() {
  251. string locHeader;
  252. locHeader = "Received: from " + ConnectionData.HostName;
  253. locHeader += " [" + (string) ConnectionData.HostIP;
  254. locHeader += "] by " PACKAGE_NAME;
  255. return locHeader;
  256. }
  257. /// Map return value from SNFMilterEngine scan to libmilter return value.
  258. //
  259. // \param[in] MilterAction is the return from an SNFMilterEngine scan.
  260. //
  261. // \returns the return value for the libmilter callback.
  262. //
  263. // \throws std::out_of_range if MilterAction of out of range.
  264. //
  265. sfsistat
  266. SNFMilterContext::smfisReturn(SNFMilterAction MilterAction) {
  267. static const sfsistat ReturnValue[] = {
  268. SMFIS_CONTINUE,
  269. SMFIS_ACCEPT,
  270. SMFIS_TEMPFAIL,
  271. SMFIS_REJECT,
  272. SMFIS_DISCARD,
  273. SMFIS_CONTINUE
  274. };
  275. if ( (MilterAction < 0) || (MilterAction >= NMilterActions) ) {
  276. ostringstream Temp;
  277. Temp << "Illegal value of SNFMilterAction in SNFMilterContext::smfisReturn ("
  278. << MilterAction << ") while processing message from "
  279. << MessageData.SenderAddress
  280. << " (connection from " << ConnectionData.HostName << " ("
  281. << (string) ConnectionData.HostIP << ").";
  282. throw std::out_of_range(Temp.str());
  283. }
  284. return ReturnValue[MilterAction];
  285. }
  286. //// SNFMilterContextPool
  287. SNFMilterContextPool::SNFMilterContextPool(snf_RulebaseHandler* Rulebase) : // Ctor needs a live rulebase handler.
  288. myRulebase(Rulebase), // Capture the rulebase handler.
  289. MilterSocketPort(0) {
  290. string NewConfiguration = myRulebase->PlatformConfiguration(); // Get the latest configuration.
  291. codedweller::ConfigurationElement Reader("milter"); // Create a configuration reader.
  292. Reader
  293. .Element("socket")
  294. .Attribute("type", reinterpret_cast<int&>(MilterSocketType), static_cast<int>(NOMilterSocket))
  295. .Mnemonic("unix", UNIXMilterSocketMnemonic)
  296. .Mnemonic("tcp", TCPMilterSocketMnemonic)
  297. .Attribute("path", MilterSocketPath, "")
  298. .Attribute("group", MilterSocketGroup, "")
  299. .Attribute("ip", MilterSocketIP, "")
  300. .Attribute("port", MilterSocketPort, 0)
  301. .End("socket")
  302. .End("milter");
  303. codedweller::ConfigurationData ConfigurationData( // Convert our configuration string
  304. NewConfiguration.c_str(), // to a configuration data buffer.
  305. NewConfiguration.length());
  306. Reader.initialize(); // Initialize the defaults.
  307. Reader.interpret(ConfigurationData); // Read the new configuration.
  308. switch (MilterSocketType) { // Named pipe.
  309. case UNIXMilterSocket:
  310. if (0 == MilterSocketPath.size()) { // Path specified?
  311. ostringstream Temp; // No.
  312. Temp << "Path needs to be specified for socket type \"unix\"";
  313. throw runtime_error(Temp.str());
  314. }
  315. if ( (0 != MilterSocketIP.size()) || // These should not be specified.
  316. (0 != MilterSocketPort) ) {
  317. ostringstream Temp;
  318. Temp << "IP (" << MilterSocketIP << ") and/or port (" << MilterSocketPort
  319. << ") were specified for socket type \"unix\". They should not be specified.";
  320. throw runtime_error(Temp.str());
  321. }
  322. break;
  323. case TCPMilterSocket:
  324. if (0 == MilterSocketIP.size()) { // IP/host specified?
  325. ostringstream Temp; // No.
  326. Temp << "Host or IP address needs to be specified for socket type \"inet\"";
  327. throw runtime_error(Temp.str());
  328. }
  329. if (0 == MilterSocketPort) { // Port specified?
  330. ostringstream Temp; // No.
  331. Temp << "Port needs to be specified for socket type \"inet\"";
  332. throw runtime_error(Temp.str());
  333. }
  334. if ( (0 != MilterSocketPath.size()) || // These should not be specified.
  335. (0 != MilterSocketGroup.size()) ) {
  336. ostringstream Temp;
  337. Temp << "Path (" << MilterSocketPath << ") and/or group (" << MilterSocketGroup
  338. << ") were specified for socket type \"inet\". They should not be specified.";
  339. throw runtime_error(Temp.str());
  340. }
  341. break;
  342. case NOMilterSocket:
  343. {
  344. ostringstream Temp;
  345. Temp << "The required <socket> element was not present in the configuration file.";
  346. throw runtime_error(Temp.str());
  347. }
  348. break;
  349. default:
  350. {
  351. ostringstream Temp;
  352. Temp << "The type of the <socket> element configuration file is invalid. "
  353. "The type must by \"unix\" or \"inet\"";
  354. throw runtime_error(Temp.str());
  355. }
  356. }
  357. }
  358. SNFMilterSocketType SNFMilterContextPool::getSocketType() {
  359. return MilterSocketType;
  360. }
  361. string SNFMilterContextPool::getSocketPath() { return MilterSocketPath; }
  362. string SNFMilterContextPool::getSocketGroup() { return MilterSocketGroup; }
  363. string SNFMilterContextPool::getSocketIP() { return MilterSocketIP; }
  364. int SNFMilterContextPool::getSocketPort() { return MilterSocketPort; }
  365. SNFMilterContextPool::~SNFMilterContextPool() { // Dtor gracefully discards contexts.
  366. codedweller::ScopeMutex ContextPoolLock(ContextAllocationControl); // Lock the context allocation system.
  367. myRulebase = 0; // Forget our rulebase. We're dead.
  368. for( // Loop through the context pool
  369. vector<SNFMilterContext*>::iterator iC = ContextPool.begin(); // and delete any contexts we have
  370. iC != ContextPool.end(); // allocated.
  371. iC++) { delete (*iC); }
  372. }
  373. SNFMilterContext* SNFMilterContextPool::grab() { // Get a context to use.
  374. codedweller::ScopeMutex ContextPoolLock(ContextAllocationControl); // Lock the context allocation system.
  375. if(0 == myRulebase) return 0; // No contexts left if we're dead.
  376. if(1 > AvailableContexts.size()) { // If we need more contexts then
  377. SNFMilterContext* N = new SNFMilterContext(myRulebase); // Create a new context,
  378. ContextPool.push_back(N); // add it to the pool,
  379. AvailableContexts.give(N); // and make it available.
  380. }
  381. return AvailableContexts.take(); // Return the next avialable context.
  382. }
  383. void SNFMilterContextPool::drop(SNFMilterContext* E) { // Drop a context after use.
  384. // Update context state.
  385. E->State = SNFMilterContext::Pooled;
  386. AvailableContexts.give(E); // Make this context available.
  387. }
  388. bool SNFMilterContextPool::allUnused() {
  389. return (AvailableContexts.size() == ContextPool.size());
  390. }
  391. void SNFMilterContextPool::logThisError(string ContextName, int Code, string Text) {
  392. myRulebase->logThisError(ContextName, Code, Text);
  393. }
  394. void SNFMilterContextPool::logThisInfo(string ContextName, int Code, string Text) {
  395. myRulebase->logThisInfo(ContextName, Code, Text);
  396. }
  397. // End of configuration setup and engine and context interface components
  398. ////////////////////////////////////////////////////////////////////////////////
  399. ////////////////////////////////////////////////////////////////////////////////
  400. // SNFMilter callback definitions, constants, and global data.
  401. extern "C" {
  402. // Connection callback.
  403. //
  404. // This callback is invoked when a new connection is made to the
  405. // MTA. It obtains an available connection context object if one
  406. // hasn't already been assigned, and saves the IP address and name
  407. // of the connecting MTA. Next, it performs an IP scan, and
  408. // returns the appropriate response.
  409. //
  410. // Returns: SMFIS_CONTINUE, SMFIS_ACCEPT, SMFIS_TEMPFAIL, or
  411. // SMFIS_REJECT according to the following mapping:
  412. //
  413. // IPScanResult return mlfi_connect return
  414. //
  415. // Quarantine, Discard, Error FailSafeMilterResponse
  416. //
  417. // Anything else SNFMilterContext::smfisReturn(IPScanResult)
  418. //
  419. sfsistat
  420. mlfi_connect(SMFICTX *Ctx, char *HostName, _SOCK_ADDR *HostAddr)
  421. {
  422. const string ContextName = PACKAGE_NAME "::mlfi_connect";
  423. int ErrorCode = 1;
  424. int InfoCode = 1;
  425. sfsistat CallbackResult = FailSafeMilterResponse;
  426. try {
  427. SNFMilterContext *Context = assignContextToCtx(Ctx); // Get the existing context object,
  428. // or assign a new context object.
  429. Context->State = SNFMilterContext::Connect; // Update context state.
  430. sockaddr_in *SaIn = (sockaddr_in *) HostAddr; // Fetch the IP address.
  431. Context->ConnectionData.HostName = HostName; // Load the info.
  432. if (0 == SaIn) { // If HostAddr is 0...
  433. Context->ConnectionData.HostIP = "127.0.0.1"; // Set to a valid value.
  434. } else {
  435. Context->ConnectionData.HostIP = ntohl(SaIn->sin_addr.s_addr);
  436. }
  437. if (MilterDebugMode) {
  438. ostringstream Temp;
  439. Temp << "Connect from " << Context->ConnectionData.HostName << " ("
  440. << (string) Context->ConnectionData.HostIP << ").";
  441. logInfo(ContextName, InfoCode, Temp.str());
  442. }
  443. if (0 == SaIn) { // If HostAddr is 0, don't do a scan.
  444. return FailSafeMilterResponse;
  445. }
  446. SNFMilterAction IpScanResult; // Perform IP scan.
  447. IpScanResult = Context->milterEngine.scanIP(Context->ConnectionData.HostIP);
  448. if (MilterDebugMode) {
  449. ostringstream Temp;
  450. Temp << "IP scan result for connection from "
  451. << Context->ConnectionData.HostName << " ("
  452. << (string) Context->ConnectionData.HostIP << "): " << IpScanResult << ".";
  453. logInfo(ContextName, InfoCode, Temp.str());
  454. }
  455. if ( (Error == IpScanResult) || // Check for error
  456. (Quarantine == IpScanResult) ||
  457. (Discard == IpScanResult) ) {
  458. std::ostringstream Temp; // Illegal result.
  459. Temp << "Illegal result from IP scan for "
  460. << (string) Context->ConnectionData.HostIP
  461. << ": " << IpScanResult;
  462. throw std::runtime_error(Temp.str());
  463. }
  464. CallbackResult = Context->smfisReturn(IpScanResult); // Load return value.
  465. } catch (exception &E) {
  466. logError(ContextName, ErrorCode, E.what());
  467. } catch (...) {
  468. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  469. }
  470. return CallbackResult;
  471. }
  472. //
  473. // HELO callback.
  474. //
  475. // This callback is invoked when the connecting MTA sends a HELO
  476. // message. It saves the argument of the HELO command in the
  477. // connection context object.
  478. //
  479. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  480. // otherwise.
  481. //
  482. sfsistat
  483. mlfi_helo(SMFICTX *Ctx, char *heloHost)
  484. {
  485. const string ContextName = PACKAGE_NAME "::mlfi_helo";
  486. int ErrorCode = 1;
  487. int InfoCode = 1;
  488. sfsistat CallbackResult = FailSafeMilterResponse;
  489. try {
  490. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  491. Context->State = SNFMilterContext::Helo; // Update context state.
  492. Context->ConnectionData.HostHelo = heloHost; // Save the helo host name.
  493. if (MilterDebugMode) {
  494. ostringstream Temp;
  495. Temp << "HELO " << Context->ConnectionData.HostHelo << " from "
  496. << Context->ConnectionData.HostName << " ("
  497. << (string) Context->ConnectionData.HostIP << ").";
  498. logInfo(ContextName, InfoCode, Temp.str());
  499. }
  500. CallbackResult = SMFIS_CONTINUE; // Load return value.
  501. } catch (exception &E) {
  502. logError(ContextName, ErrorCode, E.what());
  503. } catch (...) {
  504. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  505. }
  506. return CallbackResult;
  507. }
  508. //
  509. // envfrom callback.
  510. //
  511. // This callback is invoked to process the envelope from line of the
  512. // mail message. It clears the message buffer, and adds the local
  513. // received header to the message buffer to scan. The local received
  514. // header is added to the email message in mlfi_eom.
  515. //
  516. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  517. // otherwise.
  518. //
  519. sfsistat
  520. mlfi_envfrom(SMFICTX *Ctx, char **argv)
  521. {
  522. const string ContextName = PACKAGE_NAME "::mlfi_envfrom";
  523. int ErrorCode = 1;
  524. int InfoCode = 1;
  525. sfsistat CallbackResult = FailSafeMilterResponse;
  526. try {
  527. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  528. Context->State = SNFMilterContext::EnvFrom; // Update context state.
  529. Context->MessageData.clear(); // This is the beginning of a new message.
  530. // Clear data from any previous message.
  531. if (MilterDebugMode) {
  532. Context->MessageData.SenderAddress = argv[0];
  533. ostringstream Temp;
  534. Temp << "Processing sender address " << Context->MessageData.SenderAddress
  535. << " (connection from " << Context->ConnectionData.HostName << " ("
  536. << (string) Context->ConnectionData.HostIP << ")).\n";
  537. logInfo(ContextName, InfoCode, Temp.str());
  538. }
  539. CallbackResult = SMFIS_CONTINUE; // Load return value.
  540. } catch (exception &E) {
  541. logError(ContextName, ErrorCode, E.what());
  542. } catch (...) {
  543. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  544. }
  545. return CallbackResult;
  546. }
  547. //
  548. // envrcpt callback.
  549. //
  550. // This callback is invoked to process the envelope receipt line of
  551. // the mail message.
  552. //
  553. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  554. // otherwise.
  555. //
  556. sfsistat
  557. mlfi_envrcpt(SMFICTX *Ctx, char **argv)
  558. {
  559. const string ContextName = PACKAGE_NAME "::mlfi_envrcpt";
  560. int ErrorCode = 1;
  561. int InfoCode = 1;
  562. sfsistat CallbackResult = FailSafeMilterResponse;
  563. try {
  564. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  565. // Update context state.
  566. Context->State = SNFMilterContext::EnvRcpt;
  567. if (MilterDebugMode) {
  568. ostringstream Temp;
  569. Temp << "Processing recipient " << argv[0] << " for message from "
  570. << Context->MessageData.SenderAddress
  571. << " (connection from " << Context->ConnectionData.HostName << " ("
  572. << (string) Context->ConnectionData.HostIP << ")).";
  573. logInfo(ContextName, InfoCode, Temp.str());
  574. }
  575. CallbackResult = SMFIS_CONTINUE; // Load return value.
  576. } catch (exception &E) {
  577. logError(ContextName, ErrorCode, E.what());
  578. } catch (...) {
  579. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  580. }
  581. return CallbackResult;
  582. }
  583. //
  584. // header callback.
  585. //
  586. // This callback is invoked to process the a header line of the mail
  587. // message. It writes the header line + SMTPENDL to the message buffer
  588. // that is scanned.
  589. //
  590. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  591. // otherwise.
  592. //
  593. sfsistat
  594. mlfi_header(SMFICTX *Ctx, char *headerf, char *headerv)
  595. {
  596. const string ContextName = PACKAGE_NAME "::mlfi_header";
  597. int ErrorCode = 1;
  598. int InfoCode = 1;
  599. sfsistat CallbackResult = FailSafeMilterResponse;
  600. try {
  601. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  602. Context->State = SNFMilterContext::Header; // Update context state.
  603. if (MilterDebugMode) {
  604. ostringstream Temp;
  605. Temp << "Processing header '" << headerf << ": " << headerv
  606. << "' for message from "
  607. << Context->MessageData.SenderAddress
  608. << " (connection from " << Context->ConnectionData.HostName << " ("
  609. << (string) Context->ConnectionData.HostIP << ")).";
  610. logInfo(ContextName, InfoCode, Temp.str());
  611. }
  612. Context->MessageData.MessageBuffer += headerf; // Add the header.
  613. Context->MessageData.MessageBuffer += ": ";
  614. Context->MessageData.MessageBuffer += headerv;
  615. Context->MessageData.MessageBuffer += SMTPENDL;
  616. CallbackResult = SMFIS_CONTINUE; // Load return value.
  617. } catch (exception &E) {
  618. logError(ContextName, ErrorCode, E.what());
  619. } catch (...) {
  620. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  621. }
  622. return CallbackResult;
  623. }
  624. //
  625. // End of header callback.
  626. //
  627. // This callback is invoked after the last header of the mail
  628. // message is sent. It writes SMTPENDL SMTPENDL to the message
  629. // buffer that is scanned.
  630. //
  631. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  632. // otherwise.
  633. //
  634. sfsistat
  635. mlfi_eoh(SMFICTX *Ctx)
  636. {
  637. const string ContextName = PACKAGE_NAME "::mlfi_eoh";
  638. int ErrorCode = 1;
  639. int InfoCode = 1;
  640. sfsistat CallbackResult = FailSafeMilterResponse;
  641. try {
  642. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  643. Context->State = SNFMilterContext::EOH; // Update context state.
  644. Context->MessageData.MessageBuffer += SMTPENDL + SMTPENDL; // Add the blank lines..
  645. if (MilterDebugMode) {
  646. ostringstream Temp;
  647. Temp << "All headers received for message from "
  648. << Context->MessageData.SenderAddress
  649. << " (connection from " << Context->ConnectionData.HostName << " ("
  650. << (string) Context->ConnectionData.HostIP << ")). Message buffer--\n'"
  651. << Context->MessageData.MessageBuffer << "'.";
  652. logInfo(ContextName, InfoCode, Temp.str());
  653. }
  654. CallbackResult = SMFIS_CONTINUE; // Load return value.
  655. } catch (exception &E) {
  656. logError(ContextName, ErrorCode, E.what());
  657. } catch (...) {
  658. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  659. }
  660. return CallbackResult;
  661. }
  662. //
  663. // message body callback.
  664. //
  665. // This callback is invoked zero or more times to send the body of the
  666. // mail message is sent. It writes the body to the message buffer that
  667. // is scanned.
  668. //
  669. // Returns: SMFIS_CONTINUE if more of the message body is needed,
  670. // SkipReturn if more of the message body is not needed, or
  671. // FailSafeMilterResponse if an error occurs. Context is the
  672. // connection context object.
  673. //
  674. sfsistat
  675. mlfi_body(SMFICTX *Ctx, unsigned char *Bodyp, size_t Bodylen)
  676. {
  677. const string ContextName = PACKAGE_NAME "::mlfi_body";
  678. int ErrorCode = 1;
  679. int InfoCode = 1;
  680. sfsistat CallbackResult = FailSafeMilterResponse;
  681. try {
  682. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  683. Context->State = SNFMilterContext::Body; // Update context state.
  684. if (Context->MessageData.MessageBuffer.size() >= // Return if there is enough in the
  685. MailBufferReserveSize) { // message buffer.
  686. return SkipReturn;
  687. }
  688. if (Context->MessageData.MessageBuffer.size() < // Do we need to copy this?
  689. MailBufferReserveSize) {
  690. string::size_type NCharToTransfer = MailBufferReserveSize - // Yes. How much more?
  691. Context->MessageData.MessageBuffer.size();
  692. if (NCharToTransfer > Bodylen) { // Don't transfer more characters
  693. // than are available.
  694. NCharToTransfer = Bodylen;
  695. }
  696. Context->MessageData.MessageBuffer.append((const char *) Bodyp, // Append the message.
  697. NCharToTransfer);
  698. if (MilterDebugMode) {
  699. ostringstream Temp;
  700. Temp << "Appended " << NCharToTransfer << " bytes to "
  701. << "message from " << Context->MessageData.SenderAddress
  702. << " (connection from " << Context->ConnectionData.HostName << " ("
  703. << (string) Context->ConnectionData.HostIP << ")). Message length: "
  704. << Context->MessageData.MessageBuffer.size() << ".";
  705. logInfo(ContextName, InfoCode, Temp.str());
  706. }
  707. } else {
  708. if (MilterDebugMode) {
  709. ostringstream Temp;
  710. Temp << "Discarded " << Bodylen << " bytes "
  711. << "message from " << Context->MessageData.SenderAddress
  712. << " (connection from " << Context->ConnectionData.HostName << " ("
  713. << (string) Context->ConnectionData.HostIP << ")) because "
  714. << MailBufferReserveSize << " bytes have already been "
  715. << "transferred. Message length: "
  716. << Context->MessageData.MessageBuffer.size() << ".";
  717. logInfo(ContextName, InfoCode, Temp.str());
  718. }
  719. }
  720. CallbackResult = SMFIS_CONTINUE; // Load return value.
  721. } catch (exception &E) {
  722. logError(ContextName, ErrorCode, E.what());
  723. } catch (...) {
  724. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  725. }
  726. return CallbackResult;
  727. }
  728. //
  729. // End-Of-Message callback.
  730. //
  731. // This callback is invoked to when the entire message has been sent
  732. // to SNFMilter. It adds the local received header to the email
  733. // message, and then scans the message body. If the scan result
  734. // indicates that the message is to be quarantined, then the message
  735. // is set to be quarantined (using smfi_quarantine).
  736. //
  737. // Returns: SMFIS_CONTINUE, SMFIS_ACCEPT, SMFIS_TEMPFAIL, or
  738. // SMFIS_DISCARD, SMFIS_REJECT, or FailSafeMilterResponse
  739. // according to the following mapping:
  740. //
  741. // MessageScan return mlfi_connect return
  742. //
  743. // Error FailSafeMilterResponse
  744. //
  745. // Anything else SNFMilterContext::smfisReturn(IPScanResult)
  746. //
  747. // Side effect: If the MessageScan result is Quarantine, the
  748. // message is quarantined using smfi_quarantine().
  749. //
  750. sfsistat
  751. mlfi_eom(SMFICTX *Ctx)
  752. {
  753. const string ContextName = PACKAGE_NAME "::mlfi_eom";
  754. int ErrorCode = 1;
  755. int InfoCode = 1;
  756. sfsistat CallbackResult = FailSafeMilterResponse;
  757. try {
  758. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  759. Context->State = SNFMilterContext::EOM; // Update context state.
  760. if (MilterDebugMode) {
  761. ostringstream Temp;
  762. Temp << "End of message from " << Context->MessageData.SenderAddress
  763. << " (connection from " << Context->ConnectionData.HostName << " ("
  764. << (string) Context->ConnectionData.HostIP << ")).";
  765. logInfo(ContextName, InfoCode, Temp.str());
  766. }
  767. if (PrependLocalReceivedHeader) {
  768. Context->MessageData.MessageBuffer.
  769. insert(0, Context->getLocalReceivedHeader() + SMTPENDL); // Prepend local received header line
  770. // to the message buffer.
  771. }
  772. SNFMilterAction MsgScanResult; // Perform scan.
  773. MsgScanResult =
  774. Context->milterEngine.scanMessage((unsigned char *) Context->MessageData.MessageBuffer.c_str(),
  775. Context->MessageData.MessageBuffer.size());
  776. if (MilterDebugMode) {
  777. ostringstream Temp;
  778. Temp << "Message scan result for message from " << Context->MessageData.SenderAddress
  779. << " (connection from " << Context->ConnectionData.HostName << " ("
  780. << (string) Context->ConnectionData.HostIP << ")): " << MsgScanResult << ".";
  781. logInfo(ContextName, InfoCode, Temp.str());
  782. }
  783. if (Error == MsgScanResult) { // Check for scan error
  784. // Illegal result.
  785. std::ostringstream Temp;
  786. Temp << "Illegal message scan result for message from "
  787. << Context->MessageData.SenderAddress
  788. << " (connection from " << Context->ConnectionData.HostName << " ("
  789. << (string) Context->ConnectionData.HostIP << ")): " << MsgScanResult;
  790. throw std::runtime_error(Temp.str());
  791. }
  792. string XHeaders = Context->milterEngine.XHeaders(); // Fetch X-headers to submit.
  793. MailHeaders MailHeadersParse; // Object to parse X-headers.
  794. MailHeadersParse.loadHeaders(XHeaders); // Load the headers to be parsed.
  795. string HeaderName; // Name of X-header.
  796. string HeaderBody; // Body of X-header, formatted for libmilter.
  797. while (MailHeadersParse.getNameBody(&HeaderName, &HeaderBody)) { // While there is an X-Header...
  798. if (MilterDebugMode) {
  799. ostringstream Temp;
  800. Temp << "Processed X-Header for message from " << Context->MessageData.SenderAddress
  801. << " (connection from " << Context->ConnectionData.HostName << " ("
  802. << (string) Context->ConnectionData.HostIP << ")). X-Header name: '"
  803. << HeaderName << "'. X-Header body: '" << HeaderBody << "'.";
  804. logInfo(ContextName, InfoCode, Temp.str());
  805. }
  806. if (MI_SUCCESS != smfi_addheader(Ctx, // Add header to the email message.
  807. (char *) HeaderName.c_str(),
  808. (char *) HeaderBody.c_str())) {
  809. ostringstream Temp;
  810. Temp << "Error adding X-header to message from " << Context->MessageData.SenderAddress
  811. << " (connection from " << Context->ConnectionData.HostName << " ("
  812. << (string) Context->ConnectionData.HostIP
  813. << ")).\nX-Header name: '" << HeaderName << "'. X-Header body--\n'"
  814. << HeaderBody << "'.";
  815. logError(ContextName, 1, Temp.str());
  816. }
  817. }
  818. if (Quarantine == MsgScanResult) { // Quarantine the message?
  819. if (MilterDebugMode) {
  820. ostringstream Temp;
  821. Temp << "Quarantining message from " << Context->MessageData.SenderAddress
  822. << " (connection from " << Context->ConnectionData.HostName << " ("
  823. << (string) Context->ConnectionData.HostIP << ")).";
  824. logInfo(ContextName, InfoCode, Temp.str());
  825. }
  826. if (MI_SUCCESS != smfi_quarantine(Ctx,
  827. (char *) "Quarantined by " PACKAGE_NAME)) {
  828. ostringstream Temp;
  829. Temp << "Error quarantining message from "
  830. << Context->MessageData.SenderAddress
  831. << " (connection from " << Context->ConnectionData.HostName << " ("
  832. << (string) Context->ConnectionData.HostIP << ")).";
  833. logError(ContextName, 1, Temp.str());
  834. }
  835. return SMFIS_CONTINUE;
  836. }
  837. CallbackResult = Context->smfisReturn(MsgScanResult); // Load return value.
  838. } catch (exception &E) {
  839. logError(ContextName, ErrorCode, E.what());
  840. } catch (...) {
  841. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  842. }
  843. return CallbackResult;
  844. }
  845. //
  846. // Callback for aborting the message.
  847. //
  848. // This callback is invoked to when the processing of the current
  849. // message is to be aborted. It logs the abort as an info event,
  850. // clears the email message buffer, and sets the state to
  851. // connected.
  852. //
  853. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  854. // otherwise.
  855. //
  856. sfsistat
  857. mlfi_abort(SMFICTX *Ctx)
  858. {
  859. const string ContextName = PACKAGE_NAME "::mlfi_abort";
  860. int ErrorCode = 1;
  861. int InfoCode = 1;
  862. sfsistat CallbackResult = FailSafeMilterResponse;
  863. try {
  864. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  865. Context->State = SNFMilterContext::Connect; // Update context state.
  866. ostringstream Temp; // Log message.
  867. Temp << "Aborted processing of message from " << Context->MessageData.SenderAddress
  868. << " (connection from " << Context->ConnectionData.HostName << " ("
  869. << (string) Context->ConnectionData.HostIP << ")).";
  870. logInfo(ContextName, InfoCode, Temp.str());
  871. Context->MessageData.clear(); // Clear data for this message.
  872. CallbackResult = SMFIS_CONTINUE; // Load return value.
  873. } catch (exception &E) {
  874. logError(ContextName, ErrorCode, E.what());
  875. } catch (...) {
  876. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  877. }
  878. return CallbackResult;
  879. }
  880. //
  881. // Callback for closing the connection.
  882. //
  883. // This callback is invoked to when the connection with the remote MTA is closed.
  884. // It returns the connection context object to the pool.
  885. //
  886. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  887. // otherwise.
  888. //
  889. sfsistat
  890. mlfi_close(SMFICTX *Ctx)
  891. {
  892. const string ContextName = PACKAGE_NAME "::mlfi_close";
  893. int ErrorCode = 1;
  894. int InfoCode = 1;
  895. sfsistat CallbackResult = FailSafeMilterResponse;
  896. try {
  897. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  898. Context->State = SNFMilterContext::Close; // Update context state.
  899. if (MilterDebugMode) {
  900. ostringstream Temp;
  901. Temp << "Closing connection from "
  902. << Context->ConnectionData.HostName << " ("
  903. << (string) Context->ConnectionData.HostIP << ").";
  904. logInfo(ContextName, InfoCode, Temp.str());
  905. }
  906. MilterContexts->drop(Context); // Return the context object.
  907. smfi_setpriv(Ctx, 0);
  908. CallbackResult = SMFIS_CONTINUE; // Load return value.
  909. } catch (exception &E) {
  910. logError(ContextName, ErrorCode, E.what());
  911. } catch (...) {
  912. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  913. }
  914. return CallbackResult;
  915. }
  916. #ifdef NEW_LIBMILTER
  917. //
  918. // Callback for unknown SMTP command.
  919. //
  920. // This callback is invoked to when an unknown SMTP command is
  921. // received by the local MTA. The unknown command is logged.
  922. //
  923. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  924. // otherwise.
  925. //
  926. sfsistat
  927. mlfi_unknown(SMFICTX *Ctx, const char *Cmd)
  928. {
  929. const string ContextName = PACKAGE_NAME "::mlfi_unknown";
  930. int ErrorCode = 1;
  931. int InfoCode = 1;
  932. sfsistat CallbackResult = FailSafeMilterResponse;
  933. try {
  934. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  935. ostringstream Temp; // Log.
  936. Temp << "Unknown SMTP command from "
  937. << Context->ConnectionData.HostName << " ("
  938. << (string) Context->ConnectionData.HostIP << "): '"
  939. << *Cmd << "'";
  940. logInfo(ContextName, InfoCode, Temp.str());
  941. CallbackResult = SMFIS_CONTINUE; // Load return value.
  942. } catch (exception &E) {
  943. logError(ContextName, ErrorCode, E.what());
  944. } catch (...) {
  945. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  946. }
  947. return CallbackResult;
  948. }
  949. //
  950. // data callback.
  951. //
  952. // This callback is invoked when the connecting MTA sends a DATA
  953. // message.
  954. //
  955. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  956. // otherwise.
  957. //
  958. sfsistat
  959. mlfi_data(SMFICTX *Ctx)
  960. {
  961. const string ContextName = PACKAGE_NAME "::mlfi_data";
  962. int ErrorCode = 1;
  963. int InfoCode = 1;
  964. sfsistat CallbackResult = FailSafeMilterResponse;
  965. try {
  966. SNFMilterContext *Context = getContextFromCtx(Ctx); // Get the context object.
  967. Context->State = SNFMilterContext::Data; // Update context state.
  968. if (MilterDebugMode) {
  969. ostringstream Temp;
  970. Temp << "DATA for message from "
  971. << Context->MessageData.SenderAddress
  972. << " (connection from " << Context->ConnectionData.HostName << " ("
  973. << (string) Context->ConnectionData.HostIP << ")).";
  974. logInfo(ContextName, InfoCode, Temp.str());
  975. }
  976. CallbackResult = SMFIS_CONTINUE; // Load return value.
  977. } catch (exception &E) {
  978. logError(ContextName, ErrorCode, E.what());
  979. } catch (...) {
  980. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  981. }
  982. return CallbackResult;
  983. }
  984. //
  985. // Callback for negotiating the capabilities of the MTA.
  986. //
  987. // This callback is invoked at the start of each SMTP connection.
  988. // It obtains an available connection context object if one hasn't
  989. // already been assigned, checks whether the MTA can accept an
  990. // SMFIS_SKIP return, and configures the connection context object
  991. // to return an acceptable value.
  992. //
  993. // Returns: SMFIS_CONTINUE if no error, FailSafeMilterResponse
  994. // otherwise.
  995. //
  996. sfsistat
  997. mlfi_negotiate(SMFICTX *Ctx,
  998. unsigned long F0,
  999. unsigned long F1,
  1000. unsigned long F2,
  1001. unsigned long F3,
  1002. unsigned long *PF0,
  1003. unsigned long *PF1,
  1004. unsigned long *PF2,
  1005. unsigned long *PF3)
  1006. {
  1007. const string ContextName = PACKAGE_NAME "::mlfi_negotiate";
  1008. int ErrorCode = 1;
  1009. int InfoCode = 1;
  1010. sfsistat CallbackResult = SMFIS_ALL_OPTS;
  1011. try {
  1012. bool AcceptsSkip = F1 & SMFIP_SKIP;
  1013. if (AcceptsSkip) { // MTA accepts SMFIS_SKIP return?
  1014. SkipReturn = SMFIS_SKIP; // Yes. Use SMFIS_SKIP.
  1015. } else {
  1016. SkipReturn = SMFIS_CONTINUE; // No. Use SMFIS_CONTINUE.
  1017. }
  1018. if (MilterDebugMode) {
  1019. ostringstream Temp; // Log message.
  1020. Temp << "MTA does " << (AcceptsSkip ? "" : "not ") << "accept SMFIS_SKIP.";
  1021. logInfo(ContextName, InfoCode, Temp.str());
  1022. }
  1023. CallbackResult = SMFIS_ALL_OPTS; // Load return value.
  1024. } catch (exception &E) {
  1025. logError(ContextName, ErrorCode, E.what());
  1026. } catch (...) {
  1027. logError(ContextName, ErrorCode, UnknownExceptionMessage);
  1028. }
  1029. return CallbackResult;
  1030. }
  1031. #endif
  1032. }
  1033. // End of SNFMilter callback definitions, constants, and global data.
  1034. ////////////////////////////////////////////////////////////////////////////////
  1035. ////////////////////////////////////////////////////////////////////////////////
  1036. // SNFMilter setup & run function.
  1037. // The runLibMilter function establishes the appropriate libmilter call backs and
  1038. // accepts calls from the MTA via libmilter until it is told to quit. When it
  1039. // is told to quit it gracefully closes down, reclaims any memory it allocated,
  1040. // and returns.
  1041. void runLibMilter(SNFMilterContextPool* Contexts, bool DebugMode) { // Run the milter 'til it's done.
  1042. MilterContexts = Contexts; // Save the pool of context objects.
  1043. MilterDebugMode = DebugMode; // Save the debug mode flag.
  1044. #if 0
  1045. if (MilterDebugMode) {
  1046. openlog(PACKAGE_NAME, LOG_PID | LOG_PERROR, LOG_MAIL); // Initialize system logging to log
  1047. // messages to mail file.
  1048. }
  1049. #endif
  1050. struct smfiDesc smfilter = { // Load structure containing callbacks.
  1051. (char *) PACKAGE_NAME, // Filter name.
  1052. SMFI_VERSION, // Version code -- do not change.
  1053. SMFIF_ADDHDRS | SMFIF_QUARANTINE, // Flags.
  1054. mlfi_connect, // Connection info callback.
  1055. mlfi_helo, // SMTP HELO command callback.
  1056. mlfi_envfrom, // Envelope sender callback.
  1057. mlfi_envrcpt, // Envelope recipient callback.
  1058. mlfi_header, // Header callback.
  1059. mlfi_eoh, // End of headers callback.
  1060. mlfi_body, // Body block callback.
  1061. mlfi_eom, // End of message callback.
  1062. mlfi_abort, // Message abort callback.
  1063. mlfi_close // Connection closed callback.
  1064. #ifdef NEW_LIBMILTER
  1065. ,
  1066. mlfi_unknown, // Unknown SMTP command callback.
  1067. mlfi_data, // DATA ccallback.
  1068. mlfi_negotiate // Negotiation at the start of each SMTP
  1069. #endif
  1070. // connection callback.
  1071. };
  1072. string MilterConnSpec;
  1073. switch (Contexts->getSocketType()) { // Configure the connection.
  1074. case UNIXMilterSocket:
  1075. MilterConnSpec = "unix:" + Contexts->getSocketPath(); // Generate the connection spec.
  1076. break;
  1077. case TCPMilterSocket:
  1078. {
  1079. ostringstream Temp;
  1080. Temp << "inet:" << Contexts->getSocketPort() << "@" // Generate the connection spec.
  1081. << Contexts->getSocketIP();
  1082. MilterConnSpec = Temp.str();
  1083. }
  1084. break;
  1085. default:
  1086. {
  1087. ostringstream Temp;
  1088. Temp << PACKAGE " internal error: Invalid socket type from SNFMilterContextPool.";
  1089. throw logic_error(Temp.str());
  1090. }
  1091. }
  1092. if (MI_FAILURE == smfi_setconn(const_cast<char *>(MilterConnSpec.c_str()))) {
  1093. ostringstream Temp;
  1094. Temp << "smfi_setconn failed with input \"" << MilterConnSpec << "\"";
  1095. throw std::runtime_error(Temp.str());
  1096. }
  1097. if (MI_FAILURE == smfi_register(smfilter)) { // Register the callbacks.
  1098. string msg = "smfi_register failed";
  1099. throw std::runtime_error(msg);
  1100. }
  1101. if (UNIXMilterSocket == Contexts->getSocketType()) {
  1102. if (MI_FAILURE == smfi_opensocket(true)) { // Create the named pipe.
  1103. ostringstream Temp;
  1104. Temp << "smfi_opensocket failed for \"" << MilterConnSpec << "\"";
  1105. throw std::runtime_error(Temp.str());
  1106. }
  1107. string MilterConnPath = Contexts->getSocketPath();
  1108. if (chmod(MilterConnPath.c_str(), // Set permissions.
  1109. S_IRUSR | S_IWUSR |
  1110. S_IRGRP | S_IWGRP) != 0) {
  1111. ostringstream Temp;
  1112. Temp << "Error setting permissions of " << MilterConnPath << ": "
  1113. << strerror(errno);
  1114. throw runtime_error(Temp.str());
  1115. }
  1116. string GroupName = Contexts->getSocketGroup();
  1117. if (0 != GroupName.size()) { // Was a group specified?
  1118. struct group *Group = getgrnam(GroupName.c_str()); // Get the ID of the group.
  1119. errno = 0;
  1120. if (NULL == Group) {
  1121. ostringstream Temp;
  1122. if (0 != errno) { // Error?
  1123. Temp << "Error obtaining the group ID of " << GroupName << ": "
  1124. << strerror(errno);
  1125. } else { // Group not found.
  1126. Temp << "Error obtaining the group ID of " << GroupName << ": "
  1127. << "No such group";
  1128. }
  1129. throw runtime_error(Temp.str());
  1130. }
  1131. if (chown(MilterConnPath.c_str(), // Set group.
  1132. -1,
  1133. Group->gr_gid) != 0) {
  1134. ostringstream Temp;
  1135. Temp << "Error setting group of " << MilterConnPath << " to "
  1136. << Group->gr_name << ": " << strerror(errno);
  1137. throw runtime_error(Temp.str());
  1138. }
  1139. }
  1140. }
  1141. if (MI_FAILURE == smfi_main()) { // Hand control to libmilter
  1142. string msg = "smfi_main failed";
  1143. throw std::runtime_error(msg);
  1144. }
  1145. const string ContextName = "--EXITING--";
  1146. int ErrorCode = 1;
  1147. int InfoCode = 1;
  1148. logInfo(ContextName, InfoCode, "Shutdown command received. Waiting for message processing to complete...");
  1149. codedweller::Sleeper WaitATic;
  1150. try {
  1151. WaitATic.setMillisecondsToSleep(ShutdownWaitPeriod_ms); // Learn to wait.
  1152. }
  1153. catch(...) {
  1154. ostringstream Temp;
  1155. Temp << "Invalid value for ShutdownWaitPeriod_ms: " << ShutdownWaitPeriod_ms << ".";
  1156. throw out_of_range(Temp.str());
  1157. }
  1158. int iPeriod = 0; // Number of periods waited.
  1159. while (!MilterContexts->allUnused() && iPeriod < ShutdownPeriods) {
  1160. iPeriod++;
  1161. WaitATic(); // Wait a period.
  1162. }
  1163. if (!MilterContexts->allUnused()) {
  1164. logError(ContextName, ErrorCode, "Not all messages finished processing.");
  1165. }
  1166. logInfo(ContextName, InfoCode, "Exiting");
  1167. WaitATic(); // Wait for messages to be logged.
  1168. MilterContexts = 0; // Turn off.
  1169. }