// MAIL FROM: private string cmd_mail(string cmdLine) { if (string.IsNullOrEmpty(this._heloStr)) { this._errCount++; return("503 HELO/EHLO Command not issued"); } if (!string.IsNullOrEmpty(this._mailFrom)) { this._errCount++; return("503 Nested MAIL command"); } List <string> parts = parseCmdLine(cmdID.mailFrom, cmdLine); if (2 != parts.Count) { this._errCount++; return(String.Format("501 {0} needs argument", parts[0])); } if (!checkMailAddr(parts[1])) { this._errCount++; return(String.Format("553 Invalid address {0}", parts[1])); } this._mailFrom = parts[1]; this._lastCmd = cmdID.mailFrom; return(string.Format("250 {0}... Sender ok", parts[1])); }
// DATA private string cmd_data(string cmdLine) { if (this._rcptTo.Count < 1) { this._errCount++; return("471 Bad or missing RCPT command"); } this._lastCmd = cmdID.data; return("354 Start mail input; end with <CRLF>.<CRLF>"); }
// unknown/unsupported private string cmd_unknown(string cmdLine) { this._errCount++; this._lastCmd = cmdID.invalid; if (string.IsNullOrEmpty(cmdLine)) { return("500 Command unrecognized"); } else { return(string.Format("500 Command unrecognized ({0})", cmdLine)); } }
// retrieves the command ID from command line args private cmdID getCommandID(string cmdLine) { cmdID id = cmdID.invalid; string tmpBuff = cmdLine.ToUpperInvariant(); for (int i = 0; i < this.cmdList.Length; i++) { if (tmpBuff.StartsWith(this.cmdList[i])) { id = (cmdID)i; break; } } return(id); }
// splits an SMTP command into command and argument(s) private List <string> parseCmdLine(cmdID id, string cmdLine) { List <string> parts = new List <string>(); if (string.IsNullOrEmpty(cmdLine)) { return(parts); } try { string cmdStr = cmdList[(int)id]; string curCmd = cleanupString(cmdLine); int pos = -1; if (cmdStr.Contains(':')) { pos = cmdLine.IndexOf(':'); } else { pos = cmdLine.IndexOf(' '); } if (-1 != pos) { string cmd = cleanupString(cmdLine.Substring(0, pos)); string 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); }
// HELO/EHLO private string cmd_helo(string cmdLine) { cmdID id = getCommandID(cmdLine); List <string> parts = parseCmdLine(id, cmdLine); if (2 != parts.Count) { this._errCount++; return(String.Format("501 {0} needs argument", parts[0])); } if (!string.IsNullOrEmpty(this._heloStr)) { this._errCount++; return(string.Format("503 you already sent {0} ...", parts[0])); } /* * if (!checkHelo(parts[1])) * { * this._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 + "]") * ) * { * this._errCount++; * return String.Format("501 spoofed {0}", parts[0]); * } */ this._heloStr = parts[1]; this._lastCmd = id; if (id == cmdID.helo) { return(String.Format("250 Hello {0} ([{1}]), nice to meet you.", parts[1], this._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], this._clientIP)); }
// RCPT TO: private string cmd_rcpt(string cmdLine) { if (string.IsNullOrEmpty(this._mailFrom)) { this._errCount++; return("503 Need MAIL before RCPT"); } List <string> parts = parseCmdLine(cmdID.rcptTo, cmdLine); if (2 != parts.Count) { this._errCount++; return(String.Format("501 {0} needs argument", parts[0])); } if (!checkMailAddr(parts[1])) { this._errCount++; return(String.Format("553 Invalid address {0}", parts[1])); } if (!isLocalDomain(this._mailDom)) { // relaying not allowed... this._errCount++; return("530 Relaying not allowed for policy reasons"); } else if (!isLocalBox(this._mailBox, this._mailDom)) { // unkown/invalid recipient this._errCount++; return(String.Format("553 Unknown email address {0}", parts[1])); } this._rcptTo.Add(parts[1]); this._lastCmd = cmdID.rcptTo; return(string.Format("250 {0}... Recipient ok", parts[1])); }
// VRFY/EXPN private string cmd_vrfy(string cmdLine) { cmdID id = getCommandID(cmdLine); this._vrfyCount++; List <string> parts = parseCmdLine(id, cmdLine); if (2 != parts.Count) { this._errCount++; return(String.Format("501 {0} needs argument", parts[0])); } if (!checkMailAddr(parts[1])) { this._errCount++; return(String.Format("553 Invalid address {0}", parts[1])); } this._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])); }
// QUIT private string cmd_quit(string cmdLine) { this._lastCmd = cmdID.quit; return("221 Closing connection."); }
// RSET private string cmd_rset(string cmdLine) { resetSession(); this._lastCmd = cmdID.rset; return("250 Reset Ok"); }
// end of DATA (dot) private string cmd_dot(string cmdLine) { this._lastCmd = cmdID.noop; return("250 Queued mail for delivery"); }
public void handleSession() { string cmdLine = "?"; string response = cmd_ok(null); cmdID currCmd = cmdID.invalid; bool connOk = true; if (false == this._initOk) { closeSession(); return; } // sessions limit reached, reject session if (this._sessCount > AppGlobals.maxSessions) { if (connOk) { sendLine(TEMPFAIL_MSG); } closeSession(); return; } // if the remote IP isn't a private one if (!isPrivateIP(this._clientIP)) { // checks the incoming IP against whitelists, if listed skip blacklist checks bool isDnsListed = isListed(this._clientIP, AppGlobals.whiteLists, "white"); if (!isDnsListed) { // check the IP against blacklists isDnsListed = isListed(this._clientIP, AppGlobals.blackLists, "black"); if ((isDnsListed) && (!AppGlobals.storeData)) { // if blacklisted and NOT storing messages sendLine(string.Format(DNSBL_MSG, this._clientIP, this._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); this._earlyTalker = isEarlyTalker(); if (this._earlyTalker) { sendLine(ETALKER_MSG); closeSession(); return; } // all ok, send out our banner connOk = sendLine(cmd_banner(null)); while ((null != cmdLine) && (true == connOk)) { if (this._lastCmd == cmdID.data) { string mailMsg = recvData(); if (this._timedOut) { // got a receive timeout during the DATA phase if (connOk) { sendLine(TIMEOUT_MSG); } closeSession(); return; } response = cmd_dot(null); 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 if (connOk) { sendLine(TEMPFAIL_MSG); } closeSession(); return; } } resetSession(); } else { // read an SMTP command line and deal with the command cmdLine = recvLine(); if (null != cmdLine) { logCmdAndResp(DIR_RX, cmdLine); currCmd = getCommandID(cmdLine); Console.WriteLine(currCmd); switch (currCmd) { case cmdID.helo: // HELO response = cmd_helo(cmdLine); break; case cmdID.ehlo: // EHLO response = cmd_helo(cmdLine); break; case cmdID.mailFrom: // MAIL FROM: response = cmd_mail(cmdLine); break; case cmdID.rcptTo: // RCPT TO: response = cmd_rcpt(cmdLine); break; case cmdID.data: // DATA if ((AppGlobals.doTempFail) && (!AppGlobals.storeData)) { // emit a tempfail upon receiving the DATA command response = TEMPFAIL_MSG; cmdLine = null; this._lastCmd = currCmd = cmdID.quit; } else { response = cmd_data(cmdLine); } break; case cmdID.rset: // RSET response = cmd_rset(cmdLine); break; case cmdID.quit: // QUIT response = cmd_quit(cmdLine); cmdLine = null; // force closing break; case cmdID.vrfy: // VRFY response = cmd_vrfy(cmdLine); break; case cmdID.expn: // EXPN response = cmd_vrfy(cmdLine); break; case cmdID.help: // HELP response = cmd_help(cmdLine); break; case cmdID.noop: // NOOP response = cmd_noop(cmdLine); break; default: // unkown/unsupported response = cmd_unknown(cmdLine); break; } } else { // the read timed out (or we got an error), emit a message and drop the connection response = TIMEOUT_MSG; currCmd = cmdID.quit; } } // send response if ((this._errCount > 0) && (cmdID.quit != currCmd)) { // tarpit a bad client, time increases with error count sleepDown(AppGlobals.errorDelay * this._errCount); } else { // add a short delay sleepDown(25); } // checks for early talkers this._earlyTalker = isEarlyTalker(); // send out the response connOk = sendLine(response); // check/enforce hard limits (errors, vrfy ...) if ((cmdID.quit != currCmd) && (connOk)) { string errMsg = null; if (this._msgCount > AppGlobals.maxMessages) { // above max # of message in a single session errMsg = "451 Session messages count exceeded"; } else if (this._errCount > AppGlobals.maxSmtpErr) { // too many errors errMsg = "550 Max errors exceeded"; } else if (this._vrfyCount > AppGlobals.maxSmtpVrfy) { // tried to VRFY/EXPN too many addresses errMsg = "451 Max recipient verification exceeded"; } else if (this._noopCount > AppGlobals.maxSmtpNoop) { // entered too many NOOP commands errMsg = "451 Max NOOP count exceeded"; } else if (this._rcptTo.Count > AppGlobals.maxSmtpRcpt) { // too many recipients for a single message errMsg = "452 Too many recipients"; } else if (this._earlyTalker) { // early talker errMsg = ETALKER_MSG; } if (null != errMsg) { if (connOk) { connOk = sendLine(errMsg); } cmdLine = null; // force closing } } // check if connection Ok if (connOk) { connOk = this._client.Connected; } } // while null... // close/reset this session closeSession(); }