/// <summary> /// This method is called when asynchronous Connect method completes. /// </summary> /// <param name="ar">An IAsyncResult that stores state information and any user defined data for this asynchronous operation.</param> private void ConnectCallback(IAsyncResult ar) { try { m_pSmtpClient.EndConnect(ar); // Start TLS requested, start switching to SSL. if (m_pActiveTarget.SslMode == SslMode.TLS) { m_pSmtpClient.BeginStartTLS(StartTlsCallback, null); } // Authentication requested, start authenticating. else if (!string.IsNullOrEmpty(m_pActiveTarget.UserName)) { m_pSmtpClient.BeginAuthenticate(m_pActiveTarget.UserName, m_pActiveTarget.Password, AuthenticateCallback, null); } else { long messageSize = -1; try { messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; } catch { // Stream doesn't support seeking. } m_pSmtpClient.BeginMailFrom(From, messageSize, MailFromCallback, null); } } catch (Exception x) { try { // Release IP usage. m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; // Connect failed, if there are more target IPs, try next one. if (!IsDisposed && !IsConnected && m_pTargets.Count > 0) { BeginConnect(); } else { Dispose(x); } } catch (Exception xx) { Dispose(xx); } } }
/// <summary> /// Starts connecting to best target. /// </summary> private void BeginConnect() { // No tagets, abort relay. if (m_pTargets.Count == 0) { LogText("No relay target(s) for '" + m_pRelayItem.To + "', aborting."); Dispose(new Exception("No relay target(s) for '" + m_pRelayItem.To + "', aborting.")); return; } // If maximum connections to specified target exceeded and there are more targets, try to get limit free target. if (m_pServer.MaxConnectionsPerIP > 0) { // For DNS or load-balnced smart host relay, search free target if any. if (m_pServer.RelayMode == Relay_Mode.Dns || m_pServer.SmartHostsBalanceMode == BalanceMode.LoadBalance) { foreach (Relay_Target t in m_pTargets) { // We found free target, stop searching. if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) { m_pActiveTarget = t; m_pTargets.Remove(t); break; } } } // Smart host fail-over mode, just check if it's free. else { // Smart host IP limit not reached. if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } } } // Just get first target. else { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // If all targets has exeeded maximum allowed connection per IP address, end relay session, // next relay cycle will try to relay again. if (m_pActiveTarget == null) { LogText("All targets has exeeded maximum allowed connection per IP address, skip relay."); Dispose(new Exception("All targets has exeeded maximum allowed connection per IP address, skip relay.")); return; } m_pSmtpClient.BeginConnect(new IPEndPoint(m_pLocalBindInfo.IP, 0), m_pActiveTarget.Target, false, new AsyncCallback(this.ConnectCallback), null); }
/// <summary> /// Is called when EHLO/HELO command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void ConnectCompleted(TCP_Client.ConnectAsyncOP op) { if (op == null) { throw new ArgumentNullException("op"); } try { // Connect failed. if (op.Error != null) { try { // Release IP usage. m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; // Connect failed, if there are more target IPs, try next one. if (!this.IsDisposed && !this.IsConnected && m_pTargets.Count > 0) { BeginConnect(); } else { Dispose(op.Error); } } catch (Exception x1) { Dispose(x1); } } // Connect suceeded. else { // Do EHLO/HELO. string hostName = string.IsNullOrEmpty(m_pLocalBindInfo.HostName) ? Dns.GetHostName() : m_pLocalBindInfo.HostName; SMTP_Client.EhloHeloAsyncOP ehloOP = new SMTP_Client.EhloHeloAsyncOP(hostName); ehloOP.CompletedAsync += delegate(object s, EventArgs <SMTP_Client.EhloHeloAsyncOP> e) { EhloCommandCompleted(ehloOP); }; if (!m_pSmtpClient.EhloHeloAsync(ehloOP)) { EhloCommandCompleted(ehloOP); } } } catch (Exception x) { Dispose(x); } }
/// <summary> /// Completes relay session and does clean up. This method is thread-safe. /// </summary> /// <param name="exception">Exception happened or null if relay completed successfully.</param> public void Dispose(Exception exception) { try { lock (this) { if (m_IsDisposed) { return; } try { m_pServer.OnSessionCompleted(this, exception); } catch { } m_pServer.Sessions.Remove(this); m_IsDisposed = true; m_pLocalBindInfo = null; m_pRelayItem = null; m_pSmartHosts = null; if (m_pSmtpClient != null) { m_pSmtpClient.Dispose(); m_pSmtpClient = null; } m_pTargets = null; if (m_pActiveTarget != null) { m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; } m_pServer = null; } } catch (Exception x) { if (m_pServer != null) { m_pServer.OnError(x); } } }
/// <summary> /// Starts connecting to best target. /// </summary> private void BeginConnect() { // No tagets, abort relay. if (m_pTargets.Count == 0) { LogText("No relay target(s) for '" + m_pRelayItem.To + "', aborting."); Dispose(new Exception("No relay target(s) for '" + m_pRelayItem.To + "', aborting.")); return; } // If maximum connections to specified target exceeded and there are more targets, try to get limit free target. if (m_pServer.MaxConnectionsPerIP > 0) { // For DNS or load-balnced smart host relay, search free target if any. if (m_pServer.RelayMode == Relay_Mode.Dns || m_pServer.SmartHostsBalanceMode == BalanceMode.LoadBalance) { foreach (Relay_Target t in m_pTargets) { // We found free target, stop searching. if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) { m_pActiveTarget = t; m_pTargets.Remove(t); break; } } } // Smart host fail-over mode, just check if it's free. else { // Smart host IP limit not reached. if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } } } // Just get first target. else { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // If all targets has exeeded maximum allowed connection per IP address, end relay session, // next relay cycle will try to relay again. if (m_pActiveTarget == null) { LogText("All targets has exeeded maximum allowed connection per IP address, skip relay."); Dispose( new Exception( "All targets has exeeded maximum allowed connection per IP address, skip relay.")); return; } m_pSmtpClient.BeginConnect(new IPEndPoint(m_pLocalBindInfo.IP, 0), m_pActiveTarget.Target, false, ConnectCallback, null); }
/// <summary> /// Completes relay session and does clean up. This method is thread-safe. /// </summary> /// <param name="exception">Exception happened or null if relay completed successfully.</param> public void Dispose(Exception exception) { try { lock (this) { if (m_IsDisposed) { return; } try { m_pServer.OnSessionCompleted(this, exception); } catch {} m_pServer.Sessions.Remove(this); m_IsDisposed = true; m_pLocalBindInfo = null; m_pRelayItem = null; m_pSmartHosts = null; if (m_pSmtpClient != null) { m_pSmtpClient.Dispose(); m_pSmtpClient = null; } m_pTargets = null; if (m_pActiveTarget != null) { m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; } m_pServer = null; } } catch (Exception x) { if (m_pServer != null) { m_pServer.OnError(x); } } }
/// <summary> /// Starts connecting to best target. /// </summary> private void BeginConnect() { // No tagets, abort relay. if (m_pTargets.Count == 0) { LogText("No relay target(s) for '" + m_pRelayItem.To + "', aborting."); Dispose(new Exception("No relay target(s) for '" + m_pRelayItem.To + "', aborting.")); return; } // Maximum connections per IP limited. if (m_pServer.MaxConnectionsPerIP > 0) { // For DNS or load-balnced smart host relay, search free target if any. if (m_pServer.RelayMode == Relay_Mode.Dns || m_pServer.SmartHostsBalanceMode == BalanceMode.LoadBalance) { foreach (Relay_Target t in m_pTargets) { // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(t.Target.Address); // We have suitable local IP binding for the target. if (m_pLocalBindInfo != null) { // We found free target, stop searching. if (m_pServer.TryAddIpUsage(t.Target.Address)) { m_pActiveTarget = t; m_pTargets.Remove(t); break; } // Connection per IP limit reached. else { LogText("Skipping relay target (" + t.HostName + "->" + t.Target.Address + "), maximum connections to the specified IP has reached."); } } // No suitable local IP binding, try next target. else { LogText("Skipping relay target (" + t.HostName + "->" + t.Target.Address + "), no suitable local IPv4/IPv6 binding."); } } } // Smart host fail-over mode, just check if it's free. else { // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(m_pTargets[0].Target.Address); // We have suitable local IP binding for the target. if (m_pLocalBindInfo != null) { // Smart host IP limit not reached. if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // Connection per IP limit reached. else { LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), maximum connections to the specified IP has reached."); } } // No suitable local IP binding, try next target. else { LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), no suitable local IPv4/IPv6 binding."); } } } // Just get first target. else { // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(m_pTargets[0].Target.Address); // We have suitable local IP binding for the target. if (m_pLocalBindInfo != null) { m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // No suitable local IP binding, try next target. else { LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), no suitable local IPv4/IPv6 binding."); } } // We don't have suitable local IP end point for relay target. // This may heppen for example: if remote server supports only IPv6 and we don't have local IPv6 local end point. if (m_pLocalBindInfo == null) { LogText("No suitable IPv4/IPv6 local IP endpoint for relay target."); Dispose(new Exception("No suitable IPv4/IPv6 local IP endpoint for relay target.")); return; } // If all targets has exeeded maximum allowed connection per IP address, end relay session, // next relay cycle will try to relay again. if (m_pActiveTarget == null) { LogText("All targets has exeeded maximum allowed connection per IP address, skip relay."); Dispose(new Exception("All targets has exeeded maximum allowed connection per IP address, skip relay.")); return; } // Set SMTP host name. m_pSmtpClient.LocalHostName = m_pLocalBindInfo.HostName; // Start connecting to remote end point. TCP_Client.ConnectAsyncOP connectOP = new TCP_Client.ConnectAsyncOP(new IPEndPoint(m_pLocalBindInfo.IP, 0), m_pActiveTarget.Target, false, null); connectOP.CompletedAsync += delegate(object s, EventArgs <TCP_Client.ConnectAsyncOP> e){ ConnectCompleted(connectOP); }; if (!m_pSmtpClient.ConnectAsync(connectOP)) { ConnectCompleted(connectOP); } }
/// <summary> /// Is called when EHLO/HELO command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void ConnectCompleted(TCP_Client.ConnectAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ // Connect failed. if(op.Error != null){ try{ // Release IP usage. m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; // Connect failed, if there are more target IPs, try next one. if(!this.IsDisposed && !this.IsConnected && m_pTargets.Count > 0){ BeginConnect(); } else{ Dispose(op.Error); } } catch(Exception x1){ Dispose(x1); } } // Connect suceeded. else{ // Do EHLO/HELO. string hostName = string.IsNullOrEmpty(m_pLocalBindInfo.HostName) ? Dns.GetHostName() : m_pLocalBindInfo.HostName; SMTP_Client.EhloHeloAsyncOP ehloOP = new SMTP_Client.EhloHeloAsyncOP(hostName); ehloOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.EhloHeloAsyncOP> e){ EhloCommandCompleted(ehloOP); }; if(!m_pSmtpClient.EhloHeloAsync(ehloOP)){ EhloCommandCompleted(ehloOP); } } } catch(Exception x){ Dispose(x); } }
/// <summary> /// Starts connecting to best target. /// </summary> private void BeginConnect() { // No tagets, abort relay. if(m_pTargets.Count == 0){ LogText("No relay target(s) for '" + m_pRelayItem.To + "', aborting."); Dispose(new Exception("No relay target(s) for '" + m_pRelayItem.To + "', aborting.")); return; } // Maximum connections per IP limited. if(m_pServer.MaxConnectionsPerIP > 0){ // For DNS or load-balnced smart host relay, search free target if any. if(m_pServer.RelayMode == Relay_Mode.Dns || m_pServer.SmartHostsBalanceMode == BalanceMode.LoadBalance){ foreach(Relay_Target t in m_pTargets){ // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(t.Target.Address); // We have suitable local IP binding for the target. if(m_pLocalBindInfo != null){ // We found free target, stop searching. if(m_pServer.TryAddIpUsage(t.Target.Address)){ m_pActiveTarget = t; m_pTargets.Remove(t); break; } // Connection per IP limit reached. else{ LogText("Skipping relay target (" + t.HostName + "->" + t.Target.Address + "), maximum connections to the specified IP has reached."); } } // No suitable local IP binding, try next target. else{ LogText("Skipping relay target (" + t.HostName + "->" + t.Target.Address + "), no suitable local IPv4/IPv6 binding."); } } } // Smart host fail-over mode, just check if it's free. else{ // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(m_pTargets[0].Target.Address); // We have suitable local IP binding for the target. if(m_pLocalBindInfo != null){ // Smart host IP limit not reached. if(m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)){ m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // Connection per IP limit reached. else{ LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), maximum connections to the specified IP has reached."); } } // No suitable local IP binding, try next target. else{ LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), no suitable local IPv4/IPv6 binding."); } } } // Just get first target. else{ // Get local IP binding for remote IP. m_pLocalBindInfo = m_pServer.GetLocalBinding(m_pTargets[0].Target.Address); // We have suitable local IP binding for the target. if(m_pLocalBindInfo != null){ m_pActiveTarget = m_pTargets[0]; m_pTargets.RemoveAt(0); } // No suitable local IP binding, try next target. else{ LogText("Skipping relay target (" + m_pTargets[0].HostName + "->" + m_pTargets[0].Target.Address + "), no suitable local IPv4/IPv6 binding."); } } // We don't have suitable local IP end point for relay target. // This may heppen for example: if remote server supports only IPv6 and we don't have local IPv6 local end point. if(m_pLocalBindInfo == null){ LogText("No suitable IPv4/IPv6 local IP endpoint for relay target."); Dispose(new Exception("No suitable IPv4/IPv6 local IP endpoint for relay target.")); return; } // If all targets has exeeded maximum allowed connection per IP address, end relay session, // next relay cycle will try to relay again. if(m_pActiveTarget == null){ LogText("All targets has exeeded maximum allowed connection per IP address, skip relay."); Dispose(new Exception("All targets has exeeded maximum allowed connection per IP address, skip relay.")); return; } // Set SMTP host name. m_pSmtpClient.LocalHostName = m_pLocalBindInfo.HostName; // Start connecting to remote end point. TCP_Client.ConnectAsyncOP connectOP = new TCP_Client.ConnectAsyncOP(new IPEndPoint(m_pLocalBindInfo.IP,0),m_pActiveTarget.Target,false,null); connectOP.CompletedAsync += delegate(object s,EventArgs<TCP_Client.ConnectAsyncOP> e){ ConnectCompleted(connectOP); }; if(!m_pSmtpClient.ConnectAsync(connectOP)){ ConnectCompleted(connectOP); } }