// if enabled, logs commands and replies private void logCmdAndResp(string direction, string line) { if (AppGlobals.logVerbose) { AppGlobals.logMessage("{0}:{1} {2}: {3}", this._clientIP, this._sessionID, direction, line); } }
// dump the current settings private static void dumpSettings() { // base/network AppGlobals.writeConsole("Host name..................: {0}", AppGlobals.hostName); AppGlobals.writeConsole("listen IP..................: {0}", AppGlobals.listenAddress); AppGlobals.writeConsole("listen port................: {0}", AppGlobals.listenPort); AppGlobals.writeConsole("Receive timeout............: {0}", AppGlobals.receiveTimeout); // hardlimits AppGlobals.writeConsole("Max errors.................: {0}", AppGlobals.maxSmtpErr); AppGlobals.writeConsole("Max NOOP...................: {0}", AppGlobals.maxSmtpNoop); AppGlobals.writeConsole("Max VRFY/EXPN..............: {0}", AppGlobals.maxSmtpVrfy); AppGlobals.writeConsole("Max RCPT TO................: {0}", AppGlobals.maxSmtpRcpt); // sessions AppGlobals.writeConsole("Max messages per session...: {0}", AppGlobals.maxMessages); AppGlobals.writeConsole("Max parallel sessions......: {0}", AppGlobals.maxSessions); // messages AppGlobals.writeConsole("Store message data.........: {0}", AppGlobals.storeData); AppGlobals.writeConsole("Storage path...............: {0}", AppGlobals.storePath); AppGlobals.writeConsole("Max message size...........: {0}", AppGlobals.maxDataSize); // logs AppGlobals.writeConsole("Logfiles path..............: {0}", AppGlobals.logPath); AppGlobals.writeConsole("Verbose logging............: {0}", AppGlobals.logVerbose); // tarpitting AppGlobals.writeConsole("Initial banner delay.......: {0}", AppGlobals.bannerDelay); AppGlobals.writeConsole("Error delay................: {0}", AppGlobals.errorDelay); // filtering/rejecting AppGlobals.writeConsole("Do tempfail (4xx) on DATA..: {0}", AppGlobals.doTempFail); AppGlobals.writeConsole("Check for early talkers....: {0}", AppGlobals.earlyTalkers); // DNS filtering AppGlobals.writeConsole("DNS Whitelists.............: {0}", AppGlobals.whiteLists.Length); AppGlobals.writeConsole("DNS Blacklists.............: {0}", AppGlobals.blackLists.Length); // local domains/mailboxes AppGlobals.writeConsole("Local domains..............: {0}", AppGlobals.LocalDomains.Count); AppGlobals.writeConsole("Local mailboxes............: {0}", AppGlobals.LocalMailBoxes.Count); }
// closes the socket, terminates the session private void closeSession() { if (null != this._client) { if (this._client.Connected) { sleepDown(25); } try { this._client.Close(); this._client = null; } catch { } if (!string.IsNullOrEmpty(this._clientIP)) { AppGlobals.writeConsole("client {0} disconnected, sess={1}, ID={2}.", this._clientIP, this._sessCount, this._sessionID); } } this._initOk = false; long sesscount = AppGlobals.removeSession(); resetSession(); }
private string _mailDom = null; // domain part of a mail address #endregion #region "instance" // init public SMTPsession(TcpClient client) { try { this._sessCount = AppGlobals.addSession(); this._sessionID = AppGlobals.sessionID(); this._hostName = AppGlobals.hostName; if (null != AppGlobals.LocalDomains) { this._mailDomains = AppGlobals.LocalDomains; } if (null != AppGlobals.LocalMailBoxes) { this._mailBoxes = AppGlobals.LocalMailBoxes; } this._client = client; this._clientIP = this._client.Client.RemoteEndPoint.ToString(); int i = this._clientIP.IndexOf(':'); if (-1 != i) { this._clientIP = this._clientIP.Substring(0, i); } this._client.ReceiveTimeout = AppGlobals.receiveTimeout; this._stream = this._client.GetStream(); this._reader = new StreamReader(this._stream); this._writer = new StreamWriter(this._stream); this._writer.NewLine = "\r\n"; this._writer.AutoFlush = true; AppGlobals.writeConsole("client {0} connected, sess={1}, ID={2}.", this._clientIP, this._sessCount, this._sessionID); this._initOk = true; } catch (Exception ex) { AppGlobals.writeConsole("SMTPsession::Exception: " + ex.Message); closeSession(); } }
// logs session infos to logfile (at each mail); if you want to change // the log record format, this is the place to do it, just change the // "cols.Add" to include the columns you want and there you'll go :-) private void logSession() { // check if already logged if (this._lastMsgID == this._msgCount) { return; } this._lastMsgID = this._msgCount; // check if we got some data if (string.IsNullOrEmpty(this._heloStr)) { this._heloStr = "-no-helo-"; } if (string.IsNullOrEmpty(this._mailFrom)) { this._mailFrom = "-no-from-"; } // if (0 == this._rcptTo.Count) return; // build the log array List <string> cols = new List <string>(); // current date/time cols.Add(DateTime.UtcNow.ToString("u")); // start date, session ID, client IP, helo cols.Add(this._startDate.ToString("u")); cols.Add(this._sessionID.ToString()); cols.Add(this._clientIP); cols.Add(this._heloStr); // mail from if (!string.IsNullOrEmpty(this._mailFrom)) { cols.Add(this._mailFrom); } else { cols.Add(""); } // rcpt to if (this._rcptTo.Count > 0) { cols.Add(this._rcptTo.Count.ToString()); cols.Add(string.Join(",", this._rcptTo)); } else { cols.Add("0"); cols.Add("-no-rcpt-"); } // message # and message file name (if any) cols.Add(this._msgCount.ToString()); if (!string.IsNullOrEmpty(this._msgFile)) { cols.Add(this._msgFile); } else { cols.Add("-no-file-"); } // dns listing if (!string.IsNullOrEmpty(this._dnsListType)) { cols.Add(this._dnsListType); cols.Add(this._dnsListName); cols.Add(this._dnsListValue); } else { cols.Add("-not-listed-"); cols.Add("-none-"); cols.Add("0.0.0.0"); } // early talker if (this._earlyTalker) { cols.Add("1"); } else { cols.Add("0"); } // noop/vrfy/err cols.Add(this._noopCount.ToString()); cols.Add(this._vrfyCount.ToString()); cols.Add(this._errCount.ToString()); // builds and logs the record //string logRec = string.Join("|", cols); //AppGlobals.logSession("{0}", logRec); // builds the log record format string StringBuilder logFmt = new StringBuilder("{0}"); for (int i = 1; i < cols.Count; i++) { logFmt.Append("|{" + i + "}"); } // log the record AppGlobals.logSession(logFmt.ToString(), cols.ToArray <string>()); }
// main entry point static int Main(string[] args) { // our internal stuff IPAddress listenAddr = IPAddress.Loopback; int listenPort = 25; int retCode = 0; // load the config loadConfig(); // tell we're starting up and, if verbose, dump config parameters AppGlobals.writeConsole("{0} {1} starting up (NET {2})", AppGlobals.appName, AppGlobals.appVersion, AppGlobals.appRuntime); if (AppGlobals.logVerbose) { dumpSettings(); } // setup the listening IP:port listenAddr = AppGlobals.listenIP; listenPort = AppGlobals.listenPort; // try starting the listener try { listener = new TcpListener(listenAddr, listenPort); listener.Start(); } catch (Exception ex) { AppGlobals.writeConsole("Listener::Error: " + ex.Message); return(1); } // tell we're ready to accept connections AppGlobals.writeConsole("Listening for connections on {0}:{1}", listenAddr, listenPort); // run until interrupted (Ctrl-C in our case) while (!timeToStop) { try { // wait for an incoming connection, accept it and spawn a thread to handle it SMTPsession handler = new SMTPsession(listener.AcceptTcpClient()); Thread thread = new System.Threading.Thread(new ThreadStart(handler.handleSession)); thread.Start(); } catch (Exception ex) { // we got an error retCode = 2; AppGlobals.writeConsole("Handler::Error: " + ex.Message); timeToStop = true; } } // finalize if (null != listener) { try { listener.Stop(); } catch { } } return(retCode); }
// loads/parses the config values static void loadConfig() { // listen address IPAddress listenIP = IPAddress.Loopback; string listenAddress = ConfigurationManager.AppSettings["ListenAddress"]; if (String.IsNullOrEmpty(listenAddress)) { listenAddress = "127.0.0.1"; } if (false == IPAddress.TryParse(listenAddress, out listenIP)) { listenAddress = "127.0.0.1"; listenIP = IPAddress.Loopback; } // listen port int listenPort = int.Parse(ConfigurationManager.AppSettings["ListenPort"]); if ((listenPort < 1) || (listenPort > 65535)) { listenPort = 25; } // receive timeout int receiveTimeout = int.Parse(ConfigurationManager.AppSettings["ReceiveTimeOut"]); if (receiveTimeout < 0) { receiveTimeout = 0; } // hostname (for the banner) string hostName = ConfigurationManager.AppSettings["HostName"]; if (string.IsNullOrEmpty(hostName)) { hostName = System.Net.Dns.GetHostEntry("").HostName; } // true=emits a "tempfail" when receiving the DATA command bool doTempFail = bool.Parse(ConfigurationManager.AppSettings["DoTempFail"]); // true=stores the email envelope and data into files bool storeData = bool.Parse(ConfigurationManager.AppSettings["StoreData"]); // max size for a given email message long storeSize = long.Parse(ConfigurationManager.AppSettings["MaxDataSize"]); if (storeSize < 0) { storeSize = 0; } // max # of messages for a session int maxMsgs = int.Parse(ConfigurationManager.AppSettings["MaxMessages"]); if (maxMsgs < 1) { maxMsgs = 10; } // path for the email storage string storePath = ConfigurationManager.AppSettings["StorePath"]; if (String.IsNullOrEmpty(storePath)) { storePath = Path.GetTempPath(); } if (!storePath.EndsWith("\\")) { storePath = storePath + "\\"; } // max # of parallel sessions, further requests will be rejected long maxSessions = long.Parse(ConfigurationManager.AppSettings["MaxSessions"]); if (maxSessions < 1) { maxSessions = 16; } // path for the log file string logPath = ConfigurationManager.AppSettings["LogPath"]; if (String.IsNullOrEmpty(logPath)) { logPath = Path.GetTempPath(); } if (!logPath.EndsWith("\\")) { logPath = logPath + "\\"; } // verbose logging bool verboseLog = bool.Parse(ConfigurationManager.AppSettings["VerboseLogging"]); // early talker detection bool earlyTalk = bool.Parse(ConfigurationManager.AppSettings["DoEarlyTalk"]); // DNS whitelist providers, empty to not perform the check string whiteLists = ConfigurationManager.AppSettings["RWLproviders"]; string[] RWL = null; if (!string.IsNullOrEmpty(whiteLists)) { RWL = whiteLists.Split(','); } // DNS blacklist providers, empty to not perform the check string blackLists = ConfigurationManager.AppSettings["RBLproviders"]; string[] RBL = null; if (!string.IsNullOrEmpty(blackLists)) { RBL = blackLists.Split(','); } // hardlimits for errors, noop etc.. int maxErrors = int.Parse(ConfigurationManager.AppSettings["MaxSmtpErrors"]); if (maxErrors < 1) { maxErrors = 5; } int maxNoop = int.Parse(ConfigurationManager.AppSettings["MaxSmtpNoop"]); if (maxNoop < 1) { maxNoop = 7; } int maxVrfy = int.Parse(ConfigurationManager.AppSettings["MaxSmtpVrfy"]); if (maxVrfy < 1) { maxVrfy = 10; } int maxRcpt = int.Parse(ConfigurationManager.AppSettings["MaxSmtpRcpt"]); if (maxRcpt < 1) { maxRcpt = 100; } // delays (tarpitting) int bannerDelay = int.Parse(ConfigurationManager.AppSettings["BannerDelay"]); if (bannerDelay < 0) { bannerDelay = 0; } int errorDelay = int.Parse(ConfigurationManager.AppSettings["ErrorDelay"]); if (errorDelay < 0) { errorDelay = 0; } // local domains and mailboxes List <string> domains = new List <string>(); List <string> mailboxes = new List <string>(); string fileName = ConfigurationManager.AppSettings["LocalDomains"]; if (!string.IsNullOrEmpty(fileName)) { domains = AppGlobals.loadFile(fileName); } fileName = ConfigurationManager.AppSettings["LocalMailBoxes"]; if (!string.IsNullOrEmpty(fileName)) { mailboxes = AppGlobals.loadFile(fileName); } // set the global values AppGlobals.listenIP = listenIP; AppGlobals.listenAddress = listenAddress; AppGlobals.listenPort = listenPort; AppGlobals.receiveTimeout = receiveTimeout; AppGlobals.hostName = hostName.ToLower(); AppGlobals.doTempFail = doTempFail; AppGlobals.storeData = storeData; AppGlobals.maxDataSize = storeSize; AppGlobals.maxMessages = maxMsgs; AppGlobals.storePath = storePath; AppGlobals.maxSessions = maxSessions; AppGlobals.logPath = logPath; AppGlobals.logVerbose = verboseLog; AppGlobals.earlyTalkers = earlyTalk; AppGlobals.whiteLists = RWL; AppGlobals.blackLists = RBL; AppGlobals.maxSmtpErr = maxErrors; AppGlobals.maxSmtpNoop = maxNoop; AppGlobals.maxSmtpVrfy = maxVrfy; AppGlobals.maxSmtpRcpt = maxRcpt; AppGlobals.bannerDelay = bannerDelay; AppGlobals.errorDelay = errorDelay; AppGlobals.LocalDomains = domains; AppGlobals.LocalMailBoxes = mailboxes; }