// VRFY/EXPN private string _CmdVrfy(string cmdLine) { var id = _GetCommandId(cmdLine); _vrfyCount++; var parts = _ParseCmdLine(id, cmdLine); if (2 != parts.Count) { _errCount++; return String.Format("501 {0} needs argument", parts[0]); } if (!_CheckMailAddr(parts[1])) { _errCount++; return String.Format("553 Invalid address {0}", parts[1]); } _lastCmd = id; if (id == _CmdId.Vrfy) return "252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"; return String.Format("250 {0}", parts[1]); }
// splits an SMTP command into command and argument(s) private List<string> _ParseCmdLine(_CmdId id, string cmdLine) { var parts = new List<string>(); if (string.IsNullOrEmpty(cmdLine)) return parts; try { var cmdStr = _cmdList[(int)id]; var pos = cmdLine.IndexOf(cmdStr.Contains(':') ? ':' : ' '); if (-1 != pos) { var cmd = _CleanupString(cmdLine.Substring(0, pos)); var arg = _CleanupString(cmdLine.Substring(pos + 1)); parts.Add(cmd.ToUpper()); parts.Add(arg); } else parts.Add(_CleanupString(cmdLine).ToUpper()); } catch { parts = new List<string>(); } return parts; }
// RSET private string _CmdRset() { _ResetSession(); _lastCmd = _CmdId.Rset; return "250 Reset Ok"; }
// unknown/unsupported private string _CmdUnknown(string cmdLine) { _errCount++; _lastCmd = _CmdId.Invalid; return string.IsNullOrEmpty(cmdLine) ? "500 Command unrecognized" : string.Format("500 Command unrecognized ({0})", cmdLine); }
// QUIT private string _CmdQuit() { _lastCmd = _CmdId.Quit; return "221 Closing connection."; }
// RCPT TO: private string _CmdRcpt(string cmdLine) { if (string.IsNullOrEmpty(_mailFrom)) { _errCount++; return "503 Need MAIL before RCPT"; } var parts = _ParseCmdLine(_CmdId.RcptTo, cmdLine); if (2 != parts.Count) { _errCount++; return String.Format("501 {0} needs argument", parts[0]); } if (!_CheckMailAddr(parts[1])) { _errCount++; return String.Format("553 Invalid address {0}", parts[1]); } if (!_IsLocalDomain(_mailDom)) { // relaying not allowed... _errCount++; return "530 Relaying not allowed for policy reasons"; } if (!_IsLocalBox(_mailBox, _mailDom)) { // unkown/invalid recipient _errCount++; return String.Format("553 Unknown email address {0}", parts[1]); } _rcptTo.Add(parts[1]); _lastCmd = _CmdId.RcptTo; return string.Format("250 {0}... Recipient ok", parts[1]); }
// MAIL FROM: private string _CmdMail(string cmdLine) { if (string.IsNullOrEmpty(_heloStr)) { _errCount++; return "503 HELO/EHLO Command not issued"; } if (!string.IsNullOrEmpty(_mailFrom)) { _errCount++; return "503 Nested MAIL command"; } var parts = _ParseCmdLine(_CmdId.MailFrom, cmdLine); if (2 != parts.Count) { _errCount++; return String.Format("501 {0} needs argument", parts[0]); } if (!_CheckMailAddr(parts[1])) { _errCount++; return String.Format("553 Invalid address {0}", parts[1]); } _mailFrom = parts[1]; _lastCmd = _CmdId.MailFrom; return string.Format("250 {0}... Sender ok", parts[1]); }
// HELO/EHLO private string _CmdHelo(string cmdLine) { var id = _GetCommandId(cmdLine); var parts = _ParseCmdLine(id, cmdLine); if (2 != parts.Count) { _errCount++; return String.Format("501 {0} needs argument", parts[0]); } if (!string.IsNullOrEmpty(_heloStr)) { _errCount++; return string.Format("503 you already sent {0} ...", parts[0]); } if (AppGlobals.CheckHelloFormat && !_CheckHelo(parts[1])) { _errCount++; return String.Format("501 Invalid {0}", parts[0]); } if (parts[1].ToLower().Equals("localhost") || parts[1].ToLower().Equals(AppGlobals.HostName) || parts[1].StartsWith("[127.") || parts[1].Equals("[" + AppGlobals.ListenAddress + "]") ) { _errCount++; return String.Format("501 spoofed {0}", parts[0]); } _heloStr = parts[1]; _lastCmd = id; if (id == _CmdId.Helo) return String.Format("250 Hello {0} ([{1}]), nice to meet you.", parts[1], _clientIp); return String.Format("250 Hello {0} ([{1}]), nice to meet you.\r\n250-HELP\r\n250-VRFY\r\n250-EXPN\r\n250 NOOP", parts[1], _clientIp); }
// end of DATA (dot) private string _CmdDot() { _lastCmd = _CmdId.Noop; return "250 Queued mail for delivery"; }
// DATA private string _CmdData() { if (_rcptTo.Count < 1) { _errCount++; return "471 Bad or missing RCPT command"; } _lastCmd = _CmdId.Data; return "354 Start mail input; end with <CRLF>.<CRLF>"; }
public void HandleSession() { var cmdLine = "?"; var currCmd = _CmdId.Invalid; if (false == _initOk) { _CloseSession(); return; } // sessions limit reached, reject session if (_sessCount > AppGlobals.MaxSessions) { _SendLine(_tempfailMsg); _CloseSession(); return; } // if the remote IP isn't a private one if (!_IsPrivateIp(_clientIp)) { // checks the incoming IP against whitelists, if listed skip blacklist checks var isDnsListed = _IsListed(_clientIp, AppGlobals.WhiteLists, "white"); if (!isDnsListed) { // check the IP against blacklists isDnsListed = _IsListed(_clientIp, AppGlobals.BlackLists, "black"); if ((isDnsListed) && (!AppGlobals.StoreData)) { // if blacklisted and NOT storing messages _SendLine(string.Format(_dnsblMsg, _clientIp, _dnsListName)); _CloseSession(); return; } } } // add a short delay before banner and check for early talker // see http://wiki.asrg.sp.am/wiki/Early_talker_detection _SleepDown(AppGlobals.BannerDelay); _earlyTalker = _IsEarlyTalker(); if (_earlyTalker) { _SendLine(_etalkerMsg); _CloseSession(); return; } // all ok, send out our banner var connOk = _SendLine(_CmdBanner()); while ((null != cmdLine) && connOk) { string response; if (_lastCmd == _CmdId.Data) { var mailMsg = _RecvData(); if (_timedOut) { // got a receive timeout during the DATA phase _SendLine(_timeoutMsg); _CloseSession(); return; } response = _CmdDot(); if (String.IsNullOrEmpty(mailMsg)) response = "422 Recipient mailbox exceeded quota limit."; else { _StoreMailMsg(mailMsg); if (AppGlobals.DoTempFail) { // emit a tempfail AFTER storing the mail DATA _SendLine(_tempfailMsg); _CloseSession(); return; } } _ResetSession(); } else { // read an SMTP command line and deal with the command cmdLine = _RecvLine(); if (null != cmdLine) { _LogCmdAndResp(_dirRx, cmdLine); currCmd = _GetCommandId(cmdLine); switch (currCmd) { case _CmdId.Helo: // HELO response = _CmdHelo(cmdLine); break; case _CmdId.Ehlo: // EHLO response = _CmdHelo(cmdLine); break; case _CmdId.MailFrom: // MAIL FROM: response = _CmdMail(cmdLine); break; case _CmdId.RcptTo: // RCPT TO: response = _CmdRcpt(cmdLine); break; case _CmdId.Data: // DATA if ((AppGlobals.DoTempFail) && (!AppGlobals.StoreData)) { // emit a tempfail upon receiving the DATA command response = _tempfailMsg; cmdLine = null; _lastCmd = currCmd = _CmdId.Quit; } else response = _CmdData(); break; case _CmdId.Rset: // RSET response = _CmdRset(); break; case _CmdId.Quit: // QUIT response = _CmdQuit(); cmdLine = null; // force closing break; case _CmdId.Vrfy: // VRFY response = _CmdVrfy(cmdLine); break; case _CmdId.Expn: // EXPN response = _CmdVrfy(cmdLine); break; case _CmdId.Help: // HELP response = _CmdHelp(); break; case _CmdId.Noop: // NOOP response = _CmdNoop(cmdLine); break; default: // unkown/unsupported response = _CmdUnknown(cmdLine); break; } } else { // the read timed out (or we got an error), emit a message and drop the connection response = _timeoutMsg; currCmd = _CmdId.Quit; } } // send response if ((_errCount > 0) && (_CmdId.Quit != currCmd)) { // tarpit a bad client, time increases with error count _SleepDown(AppGlobals.ErrorDelay * _errCount); } else { // add a short delay _SleepDown(25); } // checks for early talkers _earlyTalker = _IsEarlyTalker(); // send out the response connOk = _SendLine(response); // check/enforce hard limits (errors, vrfy ...) if (_CmdId.Quit != currCmd && connOk) { string errMsg = null; if (_msgCount > AppGlobals.MaxMessages) { // above max # of message in a single session errMsg = "451 Session messages count exceeded"; } else if (_errCount > AppGlobals.MaxSmtpErr) { // too many errors errMsg = "550 Max errors exceeded"; } else if (_vrfyCount > AppGlobals.MaxSmtpVrfy) { // tried to VRFY/EXPN too many addresses errMsg = "451 Max recipient verification exceeded"; } else if (_noopCount > AppGlobals.MaxSmtpNoop) { // entered too many NOOP commands errMsg = "451 Max NOOP count exceeded"; } else if (_rcptTo.Count > AppGlobals.MaxSmtpRcpt) { // too many recipients for a single message errMsg = "452 Too many recipients"; } else if (_earlyTalker) { // early talker errMsg = _etalkerMsg; } if (null != errMsg) { connOk = _SendLine(errMsg); cmdLine = null; // force closing } } // check if connection Ok if (connOk) connOk = _client.Connected; } // while null... // close/reset this session _CloseSession(); }