frosty
frosty

Reputation: 5370

Testing SMTP server is running via C#

How can I test SMTP is up and running via C# without sending a message.

I could of course try:

try{
// send email to "[email protected]"
}
catch
{
// log "smtp is down"
}

There must be a more tidy way to do this.

Upvotes: 24

Views: 44065

Answers (5)

kazakov.nickolay
kazakov.nickolay

Reputation: 131

I use this method and classes to validate the credentials (link to 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;
    }

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);
}

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);
    }
}

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();
    }
}

Upvotes: 10

Sarah
Sarah

Reputation: 19

Here is a nice open source tool (does more than MX): http://www.codeproject.com/KB/IP/DNS_NET_Resolver.aspx

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1038890

You can try saying EHLO to your server and see if it responds with 250 OK. Of course this test doesn't guarantee you that you will succeed sending the mail later, but it is a good indication.

And here's a sample:

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
                }
            }
        }
    }
}

And here's the list of codes to expect.

Upvotes: 70

Justin Niessner
Justin Niessner

Reputation: 245429

Open a socket connection to the smtp server on port 25 and see if you get anything. If not, no smtp server.

Upvotes: 4

Daniel A. White
Daniel A. White

Reputation: 190941

You could open up the port (25) with a socket or TcpClient and see if it responds.

Upvotes: 4

Related Questions