// scanner.cpp // // (C) 2002-2020 MicroNeil Research Corporation // 20041117 _M - Included new improved Filter Chain module UrlDecode. This module // scans each anchor or image tag for URL encoded characters and converts them to // their singly byte counterparts. If a characters is converted then the decoded // anchor tag is injected into the scan stream immediately after the source link. // 20041114 _M - Included new Filter Chain module: Defunker. The Defunker re-emits // the message to the scanner with all of the HTML and some coding removed. This // allows HTML obfuscated patterns to be recognized by the scanning engine. // 20040113 _M - New Reset() method used in ScanMessage() to keep things nice and // tidy. Also, modified ScanText() to create a new evaluation matrix if it is // needed, and to append to the existing one if there is one. // 20030928 _M - Moving toward the peer-server architecture and V3. The message // scanning component has been moved into it's own object called "scanner". From // now on, a message, or text will be passed to the scanner and the scanner will // return an evaulation matrix. As always, if something goes wrong it will throw. // This allows us to separate the creation of a scanner, and it's use, from any // other nifty logic. So, if I'm in a server mode, I can take my scanner and throw // messages at it as often as I like. Each message I pump in one side comes out the // other side as an evaluation matrix. This will work well for SMTP based engines // as well as peer-server, or any other "service pipeline". // // Note that the scanner object has two ways it will accept data. One way is as a // message via .ScanMessage(c_str). This method employs the filter chain system and // expects to see an SMTP message. The second way is as plain text via .ScanText(c_str). // This method is useful for "internal" purposes such as secondary scans used to // locate compound rules or parameter scans used to pick up tuning data from the // rulebase. #include "scanner.hpp" // Scanner::LoadRuleBase(RuleFileName, SecurityKey) void Scanner::LoadRuleBase(std::string& RuleFileName, std::string& SecurityKey) { RuleBase.Load(RuleFileName); // Load the rulebase file. RuleBase.Validate(SecurityKey); // Validate the rulebase file. } // Scanner::ScanMessage(MessageBuffer) EvaluationMatrix* Scanner::ScanMessage(unsigned char* MessageBuffer) { // Scan with the filter chain. FilterChainCString IV(MessageBuffer); // Set up the filter chain. FilterChainBase64 IW(&IV); // Include Base64 decoding. FilterChainQuotedPrintable IX(&IW); // Include Quoted Printable decoding. FilterChainUrlDecode IY(&IX); // Include URL decoder. FilterChainDefunker IZ(&IY); // Include Defunking. // Reset and create a new EvaluationMatrix object to use for this scan. // ScanMessage is always called with a new message. Reset(); // Reset for the new message. myEvaluationMatrix = // Allocate a new evaluation matrix new EvaluationMatrix(&RuleBase); // using the current rulebase. if(!myEvaluationMatrix) // If the allocation fails then throw BadMatrixAllocation(); // throw an appropriate exception. try { // Message header rules in earlier versions occasionally failed because there was not // a new-line character in front of the very first header. So, now we insert one :-) // This allows all header rules to start off with a ^ indicating the start of the line. myEvaluationMatrix->EvaluateThis('\n'); // Insert a newline ahead of each message. // Scan each byte in the file up to the horizon or the end of the message. // If something goes wrong, an exception will be thrown. while(myEvaluationMatrix->CountOfCharacters < ScanHorizon) myEvaluationMatrix->EvaluateThis(IZ.GetByte()); } catch(const FilterChain::Empty&) { // We're expecting this so it's ok, but } // anything else will still be thrown! return myEvaluationMatrix; // Return our results. } // Scanner::ScanText(TextBuffer) EvaluationMatrix* Scanner::ScanText(unsigned char* TextBuffer) { // Scan without the filter chain. // If needed, create a new EvaluationMatrix object to use for this scan. // If not needed, we'll add this scanning to the existing matrix. if(!myEvaluationMatrix) { myEvaluationMatrix = // Allocate a new evaluation matrix new EvaluationMatrix(&RuleBase); // using the current rulebase. if(!myEvaluationMatrix) // If the allocation fails then throw BadMatrixAllocation(); // throw an appropriate exception. } int index=0; // Set up an index at zero... while( // For as long as we're TextBuffer[index]!=0 && // not yet terminated and myEvaluationMatrix->CountOfCharacters < ScanHorizon) // not at the horizon then myEvaluationMatrix->EvaluateThis(TextBuffer[index++]); // scan this byte & move. return myEvaluationMatrix; // Return our results. }