/// <summary> /// Establishes a connection to the specified SMTP or SMTP/S server. /// </summary> /// <remarks> /// <para>Establishes a connection to the specified SMTP or SMTP/S server.</para> /// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the /// <paramref name="options"/> parameter is used to determine the default port to /// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/> /// is <c>465</c>. All other values will use a default port of <c>25</c>.</para> /// <para>If the <paramref name="options"/> has a value of /// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used /// to determine the default security options. If the <paramref name="port"/> has a value /// of <c>465</c>, then the default options used will be /// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use /// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para> /// <para>Once a connection is established, properties such as /// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be /// populated.</para> /// <para>Note: The connection established by any of the /// <a href="Overload_MailKit_Net_Smtp_SmtpClient_Connect.htm">Connect</a> /// methods may be re-used if an application wishes to send multiple messages /// to the same SMTP server. Since connecting and authenticating can be expensive /// operations, re-using a connection can significantly improve performance when /// sending a large number of messages to the same SMTP server over a short /// period of time.</para> /// </remarks> /// <example> /// <code language="c#" source="Examples\SmtpExamples.cs" region="SendMessage"/> /// </example> /// <param name="host">The host name to connect to.</param> /// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param> /// <param name="options">The secure socket options to when connecting.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="host"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="host"/> is a zero-length string. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="SmtpClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="SmtpClient"/> is already connected. /// </exception> /// <exception cref="System.NotSupportedException"> /// <paramref name="options"/> was set to /// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/> /// and the SMTP server does not support the STARTTLS extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="SmtpCommandException"> /// An SMTP command failed. /// </exception> /// <exception cref="SmtpProtocolException"> /// An SMTP protocol error occurred. /// </exception> public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) { if (host == null) throw new ArgumentNullException ("host"); if (host.Length == 0) throw new ArgumentException ("The host name cannot be empty.", "host"); if (port < 0 || port > 65535) throw new ArgumentOutOfRangeException ("port"); CheckDisposed (); if (IsConnected) throw new InvalidOperationException ("The SmtpClient is already connected."); capabilities = SmtpCapabilities.None; AuthenticationMechanisms.Clear (); MaxSize = 0; SmtpResponse response; Stream stream; bool starttls; Uri uri; ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); #if !NETFX_CORE var ipAddresses = Dns.GetHostAddresses (host); Socket socket = null; for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); try { cancellationToken.ThrowIfCancellationRequested (); if (LocalEndPoint != null) socket.Bind (LocalEndPoint); socket.Connect (ipAddresses[i], port); break; } catch (OperationCanceledException) { socket.Dispose (); socket = null; throw; } catch { socket.Dispose (); socket = null; if (i + 1 == ipAddresses.Length) throw; } } if (socket == null) throw new IOException (string.Format ("Failed to resolve host: {0}", host)); this.host = host; if (options == SecureSocketOptions.SslOnConnect) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } #else var protection = options == SecureSocketOptions.SslOnConnect ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket; var socket = new StreamSocket (); try { cancellationToken.ThrowIfCancellationRequested (); socket.ConnectAsync (new HostName (host), port.ToString (), protection) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); } catch { socket.Dispose (); throw; } stream = new DuplexStream (socket.InputStream.AsStreamForRead (0), socket.OutputStream.AsStreamForWrite (0)); #endif if (stream.CanTimeout) { stream.WriteTimeout = timeout; stream.ReadTimeout = timeout; } ProtocolLogger.LogConnect (uri); Stream = new SmtpStream (stream, socket, ProtocolLogger); try { // read the greeting response = Stream.ReadResponse (cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); // Send EHLO and get a list of supported extensions Ehlo (cancellationToken); if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0) throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension."); if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) { response = SendCommand ("STARTTLS", cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); #if !NETFX_CORE var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true); Stream.Stream = tls; #else socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (host)) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); #endif // Send EHLO again and get the new list of supported extensions Ehlo (cancellationToken); } connected = true; } catch { Stream.Dispose (); Stream = null; throw; } OnConnected (); }
/// <summary> /// Establish a connection to the specified SMTP or SMTP/S server using the provided socket. /// </summary> /// <remarks> /// <para>Establishes a connection to the specified SMTP or SMTP/S server.</para> /// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the /// <paramref name="options"/> parameter is used to determine the default port to /// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/> /// is <c>465</c>. All other values will use a default port of <c>25</c>.</para> /// <para>If the <paramref name="options"/> has a value of /// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used /// to determine the default security options. If the <paramref name="port"/> has a value /// of <c>465</c>, then the default options used will be /// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use /// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para> /// <para>Once a connection is established, properties such as /// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be /// populated.</para> /// <para>Note: The connection established by any of the /// <a href="Overload_MailKit_Net_Smtp_SmtpClient_Connect.htm">Connect</a> /// methods may be re-used if an application wishes to send multiple messages /// to the same SMTP server. Since connecting and authenticating can be expensive /// operations, re-using a connection can significantly improve performance when /// sending a large number of messages to the same SMTP server over a short /// period of time.</para> /// </remarks> /// <param name="socket">The socket to use for the connection.</param> /// <param name="host">The host name to connect to.</param> /// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param> /// <param name="options">The secure socket options to when connecting.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="socket"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="host"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="socket"/> is not connected.</para> /// <para>-or-</para> /// The <paramref name="host"/> is a zero-length string. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="SmtpClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="SmtpClient"/> is already connected. /// </exception> /// <exception cref="System.NotSupportedException"> /// <paramref name="options"/> was set to /// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/> /// and the SMTP server does not support the STARTTLS extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="SmtpCommandException"> /// An SMTP command failed. /// </exception> /// <exception cref="SmtpProtocolException"> /// An SMTP protocol error occurred. /// </exception> public void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) { if (socket == null) throw new ArgumentNullException ("socket"); if (!socket.Connected) throw new ArgumentException ("The socket is not connected.", "socket"); if (host == null) throw new ArgumentNullException ("host"); if (host.Length == 0) throw new ArgumentException ("The host name cannot be empty.", "host"); if (port < 0 || port > 65535) throw new ArgumentOutOfRangeException ("port"); CheckDisposed (); if (IsConnected) throw new InvalidOperationException ("The SmtpClient is already connected."); capabilities = SmtpCapabilities.None; AuthenticationMechanisms.Clear (); MaxSize = 0; SmtpResponse response; Stream stream; bool starttls; Uri uri; ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); this.host = host; if (options == SecureSocketOptions.SslOnConnect) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } if (stream.CanTimeout) { stream.WriteTimeout = timeout; stream.ReadTimeout = timeout; } ProtocolLogger.LogConnect (uri); Stream = new SmtpStream (stream, socket, ProtocolLogger); try { // read the greeting response = Stream.ReadResponse (cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); // Send EHLO and get a list of supported extensions Ehlo (cancellationToken); if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0) throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension."); if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) { response = SendCommand ("STARTTLS", cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true); Stream.Stream = tls; // Send EHLO again and get the new list of supported extensions Ehlo (cancellationToken); } connected = true; } catch { Stream.Dispose (); Stream = null; throw; } OnConnected (); }
internal void ReplayConnect (string hostName, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) { CheckDisposed (); if (hostName == null) throw new ArgumentNullException ("hostName"); if (replayStream == null) throw new ArgumentNullException ("replayStream"); Stream = new SmtpStream (replayStream, null, ProtocolLogger); capabilities = SmtpCapabilities.None; AuthenticationMechanisms.Clear (); host = hostName; MaxSize = 0; try { // read the greeting var response = Stream.ReadResponse (cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); // Send EHLO and get a list of supported extensions Ehlo (cancellationToken); connected = true; } catch { Stream.Dispose (); Stream = null; throw; } OnConnected (); }