测试SMTP服务器正在通过C#运行

fro*_*sty 22 c# smtp

如何在不发送消息的情况下通过C#测试SMTP是否正常运行.

我当然可以尝试:

try{
// send email to "nonsense@example.com"
}
catch
{
// log "smtp is down"
}
Run Code Online (Sandbox Code Playgroud)

必须有一个更整洁的方法来做到这一点.

Dar*_*rov 64

您可以尝试对服务器EHLO,看看它是否响应250 OK.当然,这个测试并不能保证你以后会成功发送邮件,但这是一个很好的指示.

这是一个示例:

class Program
{
    static void Main(string[] args)
    {
        using (var client = new TcpClient())
        {
            var server = "smtp.gmail.com";
            var port = 465;
            client.Connect(server, port);
            // As GMail requires SSL we should use SslStream
            // If your SMTP server doesn't support SSL you can
            // work directly with the underlying stream
            using (var stream = client.GetStream())
            using (var sslStream = new SslStream(stream))
            {
                sslStream.AuthenticateAsClient(server);
                using (var writer = new StreamWriter(sslStream))
                using (var reader = new StreamReader(sslStream))
                {
                    writer.WriteLine("EHLO " + server);
                    writer.Flush();
                    Console.WriteLine(reader.ReadLine());
                    // GMail responds with: 220 mx.google.com ESMTP
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是预期的代码列表.

  • +1:非常优雅,正确使用协议. (2认同)
  • 这似乎不够.要正确测试SMTP设置,您至少应该使用一些外部电子邮件地址(例如`postmaster @ example.com`)进行"RCPT TO",但在发送"DATA"之前要挽救.这实际上将测试您是否具有中继权限,实际上验证了您的用户名/密码和/或客户端证书. (2认同)

kaz*_*lay 6

我使用此方法和类来验证凭据(链接到github):

public static bool ValidateCredentials(string login, string password, string server, int port, bool enableSsl) {
        SmtpConnectorBase connector;
        if (enableSsl) {
            connector = new SmtpConnectorWithSsl(server, port);
        } else {
            connector = new SmtpConnectorWithoutSsl(server, port);
        }

        if (!connector.CheckResponse(220)) {
            return false;
        }

        connector.SendData($"HELO {Dns.GetHostName()}{SmtpConnectorBase.EOF}");
        if (!connector.CheckResponse(250)) {
            return false;
        }

        connector.SendData($"AUTH LOGIN{SmtpConnectorBase.EOF}");
        if (!connector.CheckResponse(334)) {
            return false;
        }

        connector.SendData(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{login}")) + SmtpConnectorBase.EOF);
        if (!connector.CheckResponse(334)) {
            return false;
        }

        connector.SendData(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{password}")) + SmtpConnectorBase.EOF);
        if (!connector.CheckResponse(235)) {
            return false;
        }

        return true;
    }
Run Code Online (Sandbox Code Playgroud)

SmtpConnectorBase:

internal abstract class SmtpConnectorBase {
    protected string SmtpServerAddress { get; set; }
    protected int Port { get; set; }
    public const string EOF = "\r\n";

    protected SmtpConnectorBase(string smtpServerAddress, int port) {
        SmtpServerAddress = smtpServerAddress;
        Port = port;
    }

    public abstract bool CheckResponse(int expectedCode);
    public abstract void SendData(string data);
}
Run Code Online (Sandbox Code Playgroud)

SmtpConnectorWithoutSsl:

internal class SmtpConnectorWithoutSsl : SmtpConnectorBase {
    private Socket _socket = null;

    public SmtpConnectorWithoutSsl(string smtpServerAddress, int port) : base(smtpServerAddress, port) {
        IPHostEntry hostEntry = Dns.GetHostEntry(smtpServerAddress);
        IPEndPoint endPoint = new IPEndPoint(hostEntry.AddressList[0], port);
        _socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        //try to connect and test the rsponse for code 220 = success
        _socket.Connect(endPoint);

    }

    ~SmtpConnectorWithoutSsl() {
        try {
            if (_socket != null) {
                _socket.Close();
                _socket.Dispose();
                _socket = null;
            }
        } catch (Exception) {
            ;
        }

    }

    public override bool CheckResponse(int expectedCode) {
        while (_socket.Available == 0) {
            System.Threading.Thread.Sleep(100);
        }
        byte[] responseArray = new byte[1024];
        _socket.Receive(responseArray, 0, _socket.Available, SocketFlags.None);
        string responseData = Encoding.UTF8.GetString(responseArray);
        int responseCode = Convert.ToInt32(responseData.Substring(0, 3));
        if (responseCode == expectedCode) {
            return true;
        }
        return false;
    }

    public override void SendData(string data) {
        byte[] dataArray = Encoding.UTF8.GetBytes(data);
        _socket.Send(dataArray, 0, dataArray.Length, SocketFlags.None);
    }
}
Run Code Online (Sandbox Code Playgroud)

SmtpConnectorWithSsl:

internal class SmtpConnectorWithSsl : SmtpConnectorBase {
    private SslStream _sslStream = null;
    private TcpClient _client = null;

    public SmtpConnectorWithSsl(string smtpServerAddress, int port) : base(smtpServerAddress, port) {
        TcpClient client = new TcpClient(smtpServerAddress, port);

        _sslStream = new SslStream(
            client.GetStream(),
            false,
            new RemoteCertificateValidationCallback(ValidateServerCertificate),
            null
            );
        // The server name must match the name on the server certificate.
        try {
            _sslStream.AuthenticateAsClient(smtpServerAddress);
        } catch (AuthenticationException e) {
            _sslStream = null;
            Console.WriteLine("Exception: {0}", e.Message);
            if (e.InnerException != null) {
                Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
            }
            Console.WriteLine("Authentication failed - closing the connection.");
            client.Close();
        }
    }

    ~SmtpConnectorWithSsl() {
        try {
            if (_sslStream != null) {
                _sslStream.Close();
                _sslStream.Dispose();
                _sslStream = null;
            }
        } catch (Exception) {
            ;
        }

        try {
            if (_client != null) {
                _client.Close();
                _client = null;
            }
        } catch (Exception) {
            ;
        }
    }

    // The following method is invoked by the RemoteCertificateValidationDelegate.
    private static bool ValidateServerCertificate(
          object sender,
          X509Certificate certificate,
          X509Chain chain,
          SslPolicyErrors sslPolicyErrors) {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

        // Do not allow this client to communicate with unauthenticated servers.
        return false;
    }

    public override bool CheckResponse(int expectedCode) {
        if (_sslStream == null) {
            return false;
        }
        var message = ReadMessageFromStream(_sslStream);
        int responseCode = Convert.ToInt32(message.Substring(0, 3));
        if (responseCode == expectedCode) {
            return true;
        }
        return false;
    }

    public override void SendData(string data) {
        byte[] messsage = Encoding.UTF8.GetBytes(data);
        // Send hello message to the server. 
        _sslStream.Write(messsage);
        _sslStream.Flush();
    }

    private string ReadMessageFromStream(SslStream stream) {
        byte[] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;
        do {
            bytes = stream.Read(buffer, 0, buffer.Length);

            // Use Decoder class to convert from bytes to UTF8
            // in case a character spans two buffers.
            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);
            // Check for EOF.
            if (messageData.ToString().IndexOf(EOF) != -1) {
                break;
            }
        } while (bytes != 0);

        return messageData.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)