Пример #1
0
        public SmtpServer(TcpClient client, int sslPort) : base(client, sslPort)
        {
            bool   dataStarted = false;
            string mailMessage = String.Empty;

            eMail mail = new eMail();

            this.Connected += (object sender, TcpRequestEventArgs e) => {
                if (this.Verbose && e.RemoteEndPoint != null && e.LocalEndPoint != null)
                {
                    logger.Debug("connected from remote [{0}:{1}] to local [{2}:{3}]",
                                 e.RemoteEndPoint.Address.ToString(),
                                 e.RemoteEndPoint.Port,
                                 e.LocalEndPoint.Address.ToString(),
                                 e.LocalEndPoint.Port
                                 );
                }

                this.SendMessage("service ready", 220);
            };

            this.Disconnected += (object sender, TcpRequestEventArgs e) => {
                if (this.Verbose && e.RemoteEndPoint != null && e.LocalEndPoint != null)
                {
                    logger.Debug("disconnected from remote [{0}:{1}] to local [{2}:{3}]",
                                 e.RemoteEndPoint.Address.ToString(),
                                 e.RemoteEndPoint.Port,
                                 e.LocalEndPoint.Address.ToString(),
                                 e.LocalEndPoint.Port
                                 );
                }
            };

            this.LineReceived += (object sender, TcpLineReceivedEventArgs e) => {
                logger.Info(String.Format("[{0}:{1}] to [{2}:{3}] Received Line: \"{4}\"", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, this._localEndPoint.Address.ToString(), this._localEndPoint.Port, e.Line));

                switch (this._state)
                {
                case State.AuthenticateLoginUsername:
                    this._temporaryVariables["username"] = Encoding.UTF8.GetString(Convert.FromBase64String(e.Line)).Trim();
                    if (this._temporaryVariables["username"] != String.Empty)
                    {
                        this._state = State.AuthenticateLoginPassword;

                        if (User.NameExists(this._temporaryVariables["username"]) || User.EMailExists(this._temporaryVariables["username"]))
                        {
                            this.SendMessage(Convert.ToBase64String(Encoding.UTF8.GetBytes("Password:"******"5.7.8 Authentication credentials invalid", 535);
                        }
                    }
                    else
                    {
                        this._temporaryVariables.Remove("username");
                        this._state = State.Default;
                        this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                    }
                    break;

                case State.AuthenticateLoginPassword:
                    this._temporaryVariables["password"] = Encoding.UTF8.GetString(Convert.FromBase64String(e.Line)).Trim();
                    this._state = State.Default;
                    if (this._temporaryVariables["password"] != String.Empty)
                    {
                        string username = this._temporaryVariables["username"];
                        string password = this._temporaryVariables["password"];
                        this._temporaryVariables.Remove("username");
                        this._temporaryVariables.Remove("password");

                        if (this._user.RefreshByUsernamePassword(username, password) || this._user.RefreshByEMailPassword(username, password))
                        {
                            this.SendMessage("2.7.0 Authentication Succeeded", 235);
                        }
                        else
                        {
                            this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                        }
                    }
                    else
                    {
                        this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                    }
                    break;

                case State.AuthenticateCramMD5:
                    List <string> wordsCramMD5 = this.GetWordsFromBase64EncodedLine(e.Line);

                    this._state = State.Default;
                    if (wordsCramMD5.Count == 1)
                    {
                        string[] splittedWords = wordsCramMD5[0].Split(new char[] { ' ' });
                        if (splittedWords.Length == 2)
                        {
                            bool nameExists  = User.NameExists(splittedWords[0]);
                            bool eMailExists = User.EMailExists(splittedWords[0]);
                            if (nameExists || eMailExists)
                            {
                                string userId  = (nameExists) ? User.GetIdByName(splittedWords[0]) : User.GetIdByEMail(splittedWords[0]);
                                User   tmpUser = new User();
                                if (tmpUser.RefreshById(userId))
                                {
                                    string calculatedDigest = this.CalculateCramMD5Digest(tmpUser.Password, this._currentCramMD5Challenge);
                                    if (calculatedDigest == splittedWords[1])
                                    {
                                        this._user = tmpUser;
                                        this.SendMessage("2.7.0 Authentication Succeeded", 235);
                                    }
                                    else
                                    {
                                        this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                                    }
                                }
                                else
                                {
                                    this.SendMessage("4.7.0 Temporary authentication failure", 454);
                                }
                            }
                            else
                            {
                                this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                            }
                        }
                        else
                        {
                            this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                        }
                    }
                    else
                    {
                        this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                    }

                    this._currentCramMD5Challenge = String.Empty;
                    break;

                case State.Default:
                default:
                    if (!dataStarted)
                    {
                        if (e.Line.StartsWith("HELO "))
                        {
                            mail.SetClientName(e.Line.Substring(5));
                            this.SendMessage("OK", 250);
                        }
                        else if (e.Line.StartsWith("EHLO "))
                        {
                            mail.SetClientName(e.Line.Substring(5));
                            this.SendMessage("Hello " + mail.ClientName + " [" + this._remoteEndPoint.Address.ToString() + "]", "250-localhost");
                            if (!this.SslIsActive)
                            {
                                string capabilities = "LOGIN";
                                capabilities += " PLAIN CRAM-MD5";
                                this.SendMessage(capabilities, "250-AUTH");
                                this.SendMessage("STARTTLS", 250);
                            }
                            else
                            {
                                string capabilities = "AUTH LOGIN";
                                capabilities += " PLAIN CRAM-MD5";
                                this.SendMessage(capabilities, 250);
                            }
                        }
                        else if (e.Line.StartsWith("AUTH "))
                        {
                            Match authMatch = Regex.Match(e.Line, @"^AUTH\s+(PLAIN|CRAM-MD5|LOGIN)(.*)?", RegexOptions.IgnoreCase);
                            if (authMatch.Success)
                            {
                                switch (authMatch.Groups[1].Value.ToUpper())
                                {
                                case "PLAIN":
                                    List <string> words = new List <string>();
                                    try {
                                        words = this.GetWordsFromBase64EncodedLine(authMatch.Groups[2].Value);
                                    } catch (Exception) {
                                    }

                                    this._state = State.Default;
                                    if (words.Count == 2)
                                    {
                                        if (words[0] != String.Empty && words[1] != String.Empty)
                                        {
                                            if (this._user.RefreshByUsernamePassword(words[0], words[1]) || this._user.RefreshByEMailPassword(words[0], words[1]))
                                            {
                                                this.SendMessage("2.7.0 Authentication Succeeded", 235);
                                            }
                                            else
                                            {
                                                this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                                            }
                                        }
                                        else
                                        {
                                            this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                                        }
                                    }
                                    else
                                    {
                                        this.SendMessage("5.7.8 Authentication credentials invalid", 535);
                                    }
                                    break;

                                case "LOGIN":
                                    this._state = State.AuthenticateLoginUsername;
                                    this.SendMessage(Convert.ToBase64String(Encoding.UTF8.GetBytes("Username:"******"CRAM-MD5":
                                    this._state = State.AuthenticateCramMD5;
                                    string base64EncodedCramMD5Challenge = this.CalculateOneTimeBase64Challenge("localhost.de");
                                    this._currentCramMD5Challenge = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedCramMD5Challenge));
                                    this.SendMessage(base64EncodedCramMD5Challenge, 334);
                                    break;

                                default:
                                    this.SendMessage("Unrecognized authentication type", 504);
                                    break;
                                }
                            }
                        }
                        else if (e.Line.StartsWith("MAIL FROM:"))
                        {
                            string email = e.Line.Substring(10);
                            try {
                                mail.SetFrom(email);
                                this.SendMessage("OK", 250);
                            } catch (FormatException) {
                                this.SendMessage("BAD <" + email + ">... Denied due to invalid email-format", 555);
                            }
                        }
                        else if (e.Line.StartsWith("RCPT TO:"))
                        {
                            mail.SetRecipient(e.Line.Substring(8));
                            this.SendMessage("OK", 250);
                        }
                        else if (e.Line.StartsWith("STARTTLS"))
                        {
                            if (e.Line.Trim() == "STARTTLS")
                            {
                                this.SendMessage("Ready to start TLS", 220);
                                if (!this.StartTls())
                                {
                                    this.SendMessage("TLS not available due to temporary reason", 454);
                                }
                            }
                            else
                            {
                                this.SendMessage("Syntax error (no parameters allowed)", 501);
                            }
                        }
                        else if (e.Line == "DATA")
                        {
                            this.SendMessage("start mail input", 354);
                            dataStarted = true;
                        }
                        else if (e.Line == "QUIT")
                        {
                            if (eMailServer.Options.Verbose)
                            {
                                logger.Debug("[{0}:{1}] to [{2}:{3}] quit connection", this._localEndPoint.Address.ToString(), this._localEndPoint.Port, this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port);
                            }
                            this.Close();
                            return;
                        }
                        else
                        {
                            this.SendMessage("Syntax error, command unrecognized", 500);
                            if (eMailServer.Options.Verbose)
                            {
                                logger.Debug("[{0}:{1}] to [{2}:{3}] unknown command: {2}", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, this._localEndPoint.Address.ToString(), this._localEndPoint.Port, e.Line);
                            }
                        }
                    }
                    else
                    {
                        if (e.Line == ".")
                        {
                            mailMessage = mailMessage.Trim();
                            logger.Info("[{0}:{1}] to [{2}:{3}] eMail data received: {2}", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, mailMessage, this._localEndPoint.Address.ToString(), this._localEndPoint.Port);
                            dataStarted = false;

                            mail.ParseData(mailMessage);
                            if (mail.IsValid)
                            {
                                if (this._user.IsLoggedIn && User.EMailExists(mail.MailFrom))
                                {
                                    mail.SetUser(this._user);
                                    mail.SetFolder("SENT");
                                }
                                else
                                {
                                    mail.SetFolder("INBOX");
                                }

                                mail.SaveToMongoDB();
                            }
                            else
                            {
                                logger.Error("received message is invalid for saving to database.");
                            }

                            this.SendMessage("OK", 250);
                        }
                        else
                        {
                            mailMessage += e.Line + "\r\n";
                        }
                    }
                    break;
                }
            };
        }
Пример #2
0
		public SmtpServer(TcpClient client, int sslPort) : base(client, sslPort) {
			bool dataStarted = false;
			string mailMessage = String.Empty;

			eMail mail = new eMail();

			this.Connected += (object sender, TcpRequestEventArgs e) => {
				if (this.Verbose && e.RemoteEndPoint != null && e.LocalEndPoint != null) {
					logger.Debug("connected from remote [{0}:{1}] to local [{2}:{3}]",
						e.RemoteEndPoint.Address.ToString(),
					    e.RemoteEndPoint.Port,
					    e.LocalEndPoint.Address.ToString(),
					    e.LocalEndPoint.Port
					);
				}

				this.SendMessage("service ready", 220);
			};
			
			this.Disconnected += (object sender, TcpRequestEventArgs e) => {
				if (this.Verbose && e.RemoteEndPoint != null && e.LocalEndPoint != null) {
					logger.Debug("disconnected from remote [{0}:{1}] to local [{2}:{3}]",
						e.RemoteEndPoint.Address.ToString(),
					    e.RemoteEndPoint.Port,
					    e.LocalEndPoint.Address.ToString(),
					    e.LocalEndPoint.Port
					);
				}
			};

			this.LineReceived += (object sender, TcpLineReceivedEventArgs e) => {
				logger.Info(String.Format("[{0}:{1}] to [{2}:{3}] Received Line: \"{4}\"", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, this._localEndPoint.Address.ToString(), this._localEndPoint.Port, e.Line));

				switch(this._state) {
					case State.AuthenticateLoginUsername:
						this._temporaryVariables["username"] = Encoding.UTF8.GetString(Convert.FromBase64String(e.Line)).Trim();
						if (this._temporaryVariables["username"] != String.Empty) {
							this._state = State.AuthenticateLoginPassword;

							if (User.NameExists(this._temporaryVariables["username"]) || User.EMailExists(this._temporaryVariables["username"])) {
								this.SendMessage(Convert.ToBase64String(Encoding.UTF8.GetBytes("Password:"******"5.7.8 Authentication credentials invalid", 535);
							}
						} else {
							this._temporaryVariables.Remove("username");
							this._state = State.Default;
							this.SendMessage("5.7.8 Authentication credentials invalid", 535);
						}
						break;
					
					case State.AuthenticateLoginPassword:
						this._temporaryVariables["password"] = Encoding.UTF8.GetString(Convert.FromBase64String(e.Line)).Trim();
						this._state = State.Default;
						if (this._temporaryVariables["password"] != String.Empty) {
							string username = this._temporaryVariables["username"];
							string password = this._temporaryVariables["password"];
							this._temporaryVariables.Remove("username");
							this._temporaryVariables.Remove("password");

							if (this._user.RefreshByUsernamePassword(username, password) || this._user.RefreshByEMailPassword(username, password)) {
								this.SendMessage("2.7.0 Authentication Succeeded", 235);
							} else {
								this.SendMessage("5.7.8 Authentication credentials invalid", 535);
							}
						} else {
							this.SendMessage("5.7.8 Authentication credentials invalid", 535);
						}
						break;
					
					case State.AuthenticateCramMD5:
						List<string> wordsCramMD5 = this.GetWordsFromBase64EncodedLine(e.Line);
						
						this._state = State.Default;
						if (wordsCramMD5.Count == 1) {
							string[] splittedWords = wordsCramMD5[0].Split(new char[] {' '});
							if (splittedWords.Length == 2) {
								bool nameExists = User.NameExists(splittedWords[0]);
								bool eMailExists = User.EMailExists(splittedWords[0]);
								if (nameExists || eMailExists) {
									string userId = (nameExists) ? User.GetIdByName(splittedWords[0]) : User.GetIdByEMail(splittedWords[0]);
									User tmpUser = new User();
									if (tmpUser.RefreshById(userId)) {
										string calculatedDigest = this.CalculateCramMD5Digest(tmpUser.Password, this._currentCramMD5Challenge);
										if (calculatedDigest == splittedWords[1]) {
											this._user = tmpUser;
											this.SendMessage("2.7.0 Authentication Succeeded", 235);
										} else {
											this.SendMessage("5.7.8 Authentication credentials invalid", 535);
										}
									} else {
										this.SendMessage("4.7.0 Temporary authentication failure", 454);
									}
								} else {
									this.SendMessage("5.7.8 Authentication credentials invalid", 535);
								}
							} else {
								this.SendMessage("5.7.8 Authentication credentials invalid", 535);
							}
						} else {
							this.SendMessage("5.7.8 Authentication credentials invalid", 535);
						}
						
						this._currentCramMD5Challenge = String.Empty;
						break;

					case State.Default:
					default:
						if (!dataStarted) {
							if (e.Line.StartsWith("HELO ")) {
								mail.SetClientName(e.Line.Substring(5));
								this.SendMessage("OK", 250);
							} else if (e.Line.StartsWith("EHLO ")) {
								mail.SetClientName(e.Line.Substring(5));
								this.SendMessage("Hello " + mail.ClientName + " [" + this._remoteEndPoint.Address.ToString() + "]", "250-localhost");
								if (!this.SslIsActive) {
									string capabilities = "LOGIN";
									capabilities += " PLAIN CRAM-MD5";
									this.SendMessage(capabilities, "250-AUTH");
									this.SendMessage("STARTTLS", 250);
								} else {
									string capabilities = "AUTH LOGIN";
									capabilities += " PLAIN CRAM-MD5";
									this.SendMessage(capabilities, 250);
								}
							} else if (e.Line.StartsWith("AUTH ")) {
								Match authMatch = Regex.Match(e.Line, @"^AUTH\s+(PLAIN|CRAM-MD5|LOGIN)(.*)?", RegexOptions.IgnoreCase);
								if (authMatch.Success) {
									switch(authMatch.Groups[1].Value.ToUpper()) {
										case "PLAIN":
											List<string> words = new List<string>();
											try {
												words = this.GetWordsFromBase64EncodedLine(authMatch.Groups[2].Value);
											} catch(Exception) {

											}
						
											this._state = State.Default;
											if (words.Count == 2) {
												if (words[0] != String.Empty && words[1] != String.Empty) {
													if (this._user.RefreshByUsernamePassword(words[0], words[1]) || this._user.RefreshByEMailPassword(words[0], words[1])) {
														this.SendMessage("2.7.0 Authentication Succeeded", 235);
													} else {
														this.SendMessage("5.7.8 Authentication credentials invalid", 535);
													}
												} else {
													this.SendMessage("5.7.8 Authentication credentials invalid", 535);
												}
											} else {
												this.SendMessage("5.7.8 Authentication credentials invalid", 535);
											}
											break;
										
										case "LOGIN":
											this._state = State.AuthenticateLoginUsername;
											this.SendMessage(Convert.ToBase64String(Encoding.UTF8.GetBytes("Username:"******"CRAM-MD5":
											this._state = State.AuthenticateCramMD5;
											string base64EncodedCramMD5Challenge = this.CalculateOneTimeBase64Challenge("localhost.de");
											this._currentCramMD5Challenge = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedCramMD5Challenge));
											this.SendMessage(base64EncodedCramMD5Challenge, 334);
											break;

										default:
											this.SendMessage("Unrecognized authentication type", 504);
											break;
									}
								}
							} else if (e.Line.StartsWith("MAIL FROM:")) {
								string email = e.Line.Substring(10);
								try {
									mail.SetFrom(email);
									this.SendMessage("OK", 250);
								} catch(FormatException) {
									this.SendMessage("BAD <" + email + ">... Denied due to invalid email-format", 555);
								}
							} else if (e.Line.StartsWith("RCPT TO:")) {
								mail.SetRecipient(e.Line.Substring(8));
								this.SendMessage("OK", 250);
							} else if (e.Line.StartsWith("STARTTLS")) {
								if (e.Line.Trim() == "STARTTLS") {
									this.SendMessage("Ready to start TLS", 220);
									if (!this.StartTls()) {
										this.SendMessage("TLS not available due to temporary reason", 454);
									}
								} else {
									this.SendMessage("Syntax error (no parameters allowed)", 501);
								}
							} else if (e.Line == "DATA") {
								this.SendMessage("start mail input", 354);
								dataStarted = true;
							} else if (e.Line == "QUIT") {
								if (eMailServer.Options.Verbose) {
									logger.Debug("[{0}:{1}] to [{2}:{3}] quit connection", this._localEndPoint.Address.ToString(), this._localEndPoint.Port, this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port);
								}
								this.Close();
								return;
							} else {
								this.SendMessage("Syntax error, command unrecognized", 500);
								if (eMailServer.Options.Verbose) {
									logger.Debug("[{0}:{1}] to [{2}:{3}] unknown command: {2}", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, this._localEndPoint.Address.ToString(), this._localEndPoint.Port, e.Line);
								}
							}
						} else {
							if (e.Line == ".") {
								mailMessage = mailMessage.Trim();
								logger.Info("[{0}:{1}] to [{2}:{3}] eMail data received: {2}", this._remoteEndPoint.Address.ToString(), this._remoteEndPoint.Port, mailMessage, this._localEndPoint.Address.ToString(), this._localEndPoint.Port);
								dataStarted = false;

								mail.ParseData(mailMessage);
								if (mail.IsValid) {
									if (this._user.IsLoggedIn && User.EMailExists(mail.MailFrom)) {
										mail.SetUser(this._user);
										mail.SetFolder("SENT");
									} else {
										mail.SetFolder("INBOX");
									}

									mail.SaveToMongoDB();
								} else {
									logger.Error("received message is invalid for saving to database.");
								}

								this.SendMessage("OK", 250);
							} else {
								mailMessage += e.Line + "\r\n";
							}
						}
						break;
				}
			};
		}