使用 ABV.BG 拥有的 SMTP 服务器发送邮件返回超时异常?

问题描述

为什么示例不起作用?即使 SMTP server 也可以。

我设置 Outlook 只是为了测试 SMTP 服务器。一切都好。我可以发送和接收邮件
问题在于 C# 代码。它总是返回超时异常。即使我将超时设置为最大整数。

SMTP 服务器端口正常,因为使用 Outlook 客户端没有任何问题。只需检查顶部的链接即可查看 SMT 服务器的配置。相同的选项来自相同的网络。

public void SendAbvBg(string message)
{
    var Smtp = new SmtpClient("smtp.abv.bg",465); 
    Smtp.UseDefaultCredentials = false;          
    Smtp.EnableSsl = true;
    Smtp.Timeout = 80000;
    Smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
    Smtp.Credentials = new System.Net.NetworkCredential("mail@abv.bg","pass");
    
    MailMessage msg = new MailMessage();
    msg.Body = message;
    msg.From = new MailAddress("mail@abv.bg","name");
    msg.To.Add("mail@abv.bg");
    msg.HeadersEncoding = Encoding.UTF8;
    msg.BodyEncoding = Encoding.UTF8;
    msg.IsBodyHtml = true;
    msg.Subject = "Form";
    msg.SubjectEncoding = Encoding.UTF8;

    msg.Headers.Add("MIME-Version","1.0");
    msg.Headers.Add("Content-Language","en-us");

    try
    {
        Smtp.Send(message);               
    }
    catch(Exception e)
    {
        // timeout exception
    }   
}

这怎么可能?标准 Outlook pop/smtp 工作。代码 - 不!为什么? 除此之外,Outlook 配置超时为 2 分钟。

我已经尝试了 5 分钟 Timeout = 300000 并且仍然收到错误消息: Message = "操作超时。"

SMT 服务器归 ABV.BG 所有。它是一个非常古老的 UNIX smtp 服务器。 客户端代码和 Outlook 客户端在同一个办公公司网络中。网络不限制任何端口或策略。 Outlook 使用与代码示例相同的配置和选项完美运行。这证明问题与网络、防火墙或任何其他限制无关。这是代码中的错误

enter image description here

会不会是认 TargetName = "SMTPSVC/smtp.abv.bg" 的问题? https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient.targetname?view=net-5.0

GMAIL 没问题,它不是当前主题的一部分。

enter image description here

enter image description here

https://passport.abv.bg/app/profiles/sendtosupport/form?from=abv.bg

https://passport.abv.bg

来自 https://www.smtper.net/ 的测试

enter image description here

解决方法

我已经创建了一个类来测试 SMTP 访问。简单测试使用您的数据调用 SmtpSeverProbe(...) 方法。成功后您将收到电子邮件。将测试正常端口 25,465,587 将测试 SSL。

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;

namespace CommonCore.Tests
{
    // Based in:
    // https://gist.github.com/xamlmonkey/4737291

    //Normal port 25 text,465 SSL,587 text then STARTSSL


    /// <summary>
    /// For Testing access to SMTP server. 
    /// </summary>
    public static class SmtpTests
    {


        static int connectionTimeout = 1300;

        /// <summary>
        /// Tests RELAY,Try to send email port 25 with and without StartSSl,port 465 with SSL,Port 587 with StartSSL.
        /// Returns a resume Log,but you have a 'out string log' variable with details.
        /// </summary>
        /// <param name="login"></param>
        /// <param name="password"></param>
        /// <param name="server">To test SSL use a name that exists in the Server Certificate,For SSL avoid a IP</param>
        /// <param name="to">Accepts all formats: ...@... or  "......." &lt;....@....&gt;</param>
        /// <param name="msg">Message in the body</param>
        /// <param name="log">very detailed Log</param>
        /// <param name="error">Only Errors Log</param>
        /// <param name="from"></param>
        /// <returns></returns>
        public static string SmtpServerProbe(string login,string password,string server,string to,string msg,out string log,out string error,string from = null)
        {
            //Normal port 25 text,587 text then STARTSSL

            string resume = "";
            error = "";
            log = Environment.NewLine;
            string _resume = "";
            string _error = "";

            int port;
            bool forceSsl;

            from = from ?? "\"Test Email\" <test@email.com>";

            bool _sendok = false;

            System.Net.Mail.MailAddress addr = null;
            try
            {
                addr = new System.Net.Mail.MailAddress(from);
            }
            catch
            {
                error = "'from' email address is incorrect.";
                log += "!! Error : " + error;
                resume = Environment.NewLine + $"[FAIL] 'from' incorrect format > '{from}'" + Environment.NewLine;
                return resume;
            }

            try
            {
                addr = new System.Net.Mail.MailAddress(to);
            }
            catch
            {
                error = "'to' email address is incorrect.";
                log += "!! Error : " + error;
                resume = Environment.NewLine + $"[FAIL] 'to' incorrect format > '{to}'" + Environment.NewLine;
                return resume;
            }

            //Test Relay
            port = 25;
            forceSsl = false;
            log += "----------------------------------" + Environment.NewLine +
                "Testing RELAY." + Environment.NewLine +
                "(user is not authenticated,email is sento to 'test@gmail.com')" + Environment.NewLine;
            _sendok = Tests.SmtpTests.ScanSMTP("","",server,port,forceSsl,"\"Test Email\" <test@email.com>","test@gmail.com","Relay Test",out string _log,out _resume,out _error);
            if (_sendok)
            {
                resume += "SERVER IS ACCEPTING RELAY THAT'S VERY BAD IDEIA";
                resume += Environment.NewLine + $" (maybe it has an exception to this Host or IP '{System.Net.Dns.GetHostName()}')";
                log += _log + Environment.NewLine;
                log += "----------------------------------" + Environment.NewLine + " SERVER IS ACCEPTING RELAY THAT'S VERY BAD IDEIA" + Environment.NewLine + Environment.NewLine;
            }
            else
            {
                log += "----------------------------------" + Environment.NewLine + " SERVER is not accepting Relay,that's good." + Environment.NewLine + Environment.NewLine;
            }

            //TEST SMTPS
            port = 465;
            forceSsl = true;
            log += Environment.NewLine + "__________________________________" + Environment.NewLine +
                $"Testing port {port} / force SSL={forceSsl}";
            _sendok = Tests.SmtpTests.ScanSMTP(login,password,from,to,msg,out _log,out _error);
            log += _log;
            error += Environment.NewLine + _error;
            resume += _resume;

            //TEST SMTP with STARTSSL
            port = 25;
            forceSsl = false;
            log += Environment.NewLine + "__________________________________" + Environment.NewLine +
                $"Testing port {port} / try STARTSSL=true";
            _sendok = Tests.SmtpTests.ScanSMTP(login,out _error);
            log += _log;
            error += Environment.NewLine + _error;
            resume += _resume;

            //TEST SMTP no STARTSSL
            port = 25;
            forceSsl = false;
            log += Environment.NewLine + "__________________________________" + Environment.NewLine +
                $"Testing port {port} / try STARTSSL=false";
            _sendok = Tests.SmtpTests.ScanSMTP(login,out _error,true,false);
            log += _log;
            error += Environment.NewLine + _error;
            resume += _resume;

            //Test port 587 with STARTSSL
            port = 587;
            forceSsl = false;
            log += Environment.NewLine + "__________________________________" + Environment.NewLine +
                $"Testing port {port} / try STARTSSL=true";
            _sendok = Tests.SmtpTests.ScanSMTP(login,out _error);
            log += _log;
            error += Environment.NewLine + _error;
            resume += _resume;

            return resume;

        }


        /// <summary>
        /// Tests the SMTP server and try to send a test Email
        /// </summary>
        /// <param name="login"></param>
        /// <param name="password"></param>
        /// <param name="server">Server DNS name</param>
        /// <param name="port"></param>
        /// <param name="forceSsl"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="msg">Message to send to the body</param>
        /// <param name="log">Detailed Log</param>
        /// <param name="resume">Resumed Log</param>
        /// <param name="error">Erro Log</param>
        /// <param name="checkcertificate">Check if SSL Certificate in the Server is acceptable</param>
        /// <param name="trystartssl">try to send the command STARTTLS</param>
        /// <returns></returns>
        public static bool ScanSMTP(string login,int port,bool forceSsl,string from,out string resume,bool checkcertificate = true,bool trystartssl = true)
        {

            #region check parameters

            log = Environment.NewLine;
            error = "";
            resume = "";
            string _fromaddrstr = "";
            string _toaddrstr = "";

            string _errorResume = $"[FAIL] Port={port},Force SSL={forceSsl},try STARTSSL={trystartssl}";

            System.Net.Mail.MailAddress addr = null;

            if (!String.IsNullOrEmpty(login))
            {
                if (String.IsNullOrEmpty(password))
                {
                    error = "If login is not empty than the password cannot be empty";
                    log += "!! Error : " + error;
                    resume = Environment.NewLine + $"[FAIL] 'password' is missing" + Environment.NewLine;
                    return false;
                }
            }

            try
            {
                addr = new System.Net.Mail.MailAddress(from);
                _fromaddrstr = addr.Address;
            }
            catch
            {
                error = "'from' email address is incorrect.";
                log += "!! Error : " + error;
                resume = Environment.NewLine + $"[FAIL] 'from' incorrect format > '{from}'" + Environment.NewLine;
                return false;
            }

            try
            {
                addr = new System.Net.Mail.MailAddress(to);
                _toaddrstr = addr.Address;
            }
            catch
            {
                error = "'to' email address is incorrect.";
                log += "!! Error : " + error;
                resume = Environment.NewLine + $"[FAIL] 'to' incorrect format > '{to}'" + Environment.NewLine;
                return false;
            }

            

            #endregion

            try
            {
                #region support variables

                string _response = "";
                string _msgfotter = "";

                StreamReader clearTextReader = null;
                StreamWriter clearTextWriter = null;

                StreamReader sslreader = null;
                StreamWriter sslwriter = null;

                SslStream sslStream = null;

                // Depending on checkcertificate value stream may ignore Certificate Errors
                // like: Hostname not in the certificate and Certificate Chain Errors 
                SslStream GetSslStream(Stream innerstream)
                {
                    if (checkcertificate)
                        return new SslStream(innerstream);
                    else
                        return new SslStream(
                            innerstream,false,new RemoteCertificateValidationCallback((s,c,h,e) => true),null
                    );
                }

                #endregion

                #region support internal methods

                void ExitError(string str,ref string _log,ref string _error)
                {
                    _error = str;
                    _log += "!! Error : " + str + Environment.NewLine + Environment.NewLine;
                    if (sslStream == null)
                    {
                        clearTextWriter.WriteLine("QUIT");
                    }
                    else
                    {
                        sslwriter.WriteLine("QUIT");
                    }


                }

                string AskReceive(string commandline,ref string _log)
                {
                    if (sslStream == null)
                    {
                        _log += "> " + commandline + Environment.NewLine;
                        clearTextWriter.WriteLine(commandline);

                        string _str;

                        System.Threading.Thread.Sleep(200);

                        //while (!clearTextReader.EndOfStream)
                        //while (!String.IsNullOrEmpty(_str))
                        //    {
                        //    System.Threading.Thread.Sleep(200);
                        //    //yield return _str;
                        //    _str = clearTextReader.ReadLine();
                        //    _log += "< " + _str + Environment.NewLine;
                        //}
                        _str = clearTextReader.ReadLine();
                        _log += "< " + _str + Environment.NewLine;

                        return _str;
                    }
                    else
                    {
                        _log += "> " + commandline + Environment.NewLine;
                        sslwriter.WriteLine(commandline);
                        System.Threading.Thread.Sleep(200);
                        string _str = sslreader.ReadLine();
                        _log += "< " + _str + Environment.NewLine;
                        return _str;
                    }
                }

                bool Login(ref string _log,ref string _error)
                {
                    if (!(_response = AskReceive($"AUTH LOGIN",ref _log)).StartsWith("334"))
                    {
                        ExitError(_response,ref _log,ref _error);
                        return false;
                    }

                    if (!(_response = AskReceive(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{login}")),ref _error);
                        return false;
                    }

                    if (!(_response = AskReceive(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{password}")),ref _log)).StartsWith("235"))
                    {
                        ExitError(_response,ref _error);
                        return false;
                    }

                    return true;
                }

                bool SendMsg(ref string _log,ref string _error)
                {

                    // HELO JakesDominoApp
                    // MAIL FROM: jake@jakehowlett.com
                    // RCPT To: jhowlett@EITS
                    // DATA
                    // From: My Self <me@you.com>
                    // To: A secret list <you@me.com>
                    // Subject: A simple test
                    // Mime-Version: 1.0;
                    // Content-Type: text/html; charset="ISO-8859-1";
                    // Content-Transfer-Encoding: 7bit;
                    //
                    // <html>
                    // <body>
                    // <h2>An important link to look at!</h2>
                    // Here's an <a href="http://www.codestore.net">important link</a>
                    // </body>
                    // </html>
                    // .
                    // QUIT

                    if (!String.IsNullOrEmpty(login))
                        if (!Login(ref _log,ref _error))
                            return false;


                    if (!(_response = AskReceive($"MAIL FROM: <{_fromaddrstr}>",ref _log)).StartsWith("250"))
                    {
                        ExitError(_response,ref _error);
                        return false;
                    }

                    if (!(_response = AskReceive($"RCPT TO: <{_toaddrstr}>",ref _error);
                        return false;
                    }

                    string _return = AskReceive($"DATA",ref _log);

                    if (_return.Substring(0,1) == "5")
                    {
                        ExitError(_return,ref _log);
                        return false;
                    }

                    string _details = $"Server:'{server}' | Login:'{login}' | port:{port} | try STARTTSL:{trystartssl} | Force SSL:{forceSsl}";

                    if (!AskReceive(
                        $"From: {from}" + Environment.NewLine +
                        $"To: {to}" + Environment.NewLine +
                        $"Subject: " + _details +
                        Environment.NewLine + Environment.NewLine +
                        msg + Environment.NewLine + Environment.NewLine +
                        "__________________________________________________" + Environment.NewLine + Environment.NewLine +
                        _details + " " + _msgfotter + Environment.NewLine +
                        Environment.NewLine + Environment.NewLine + ".",ref _log).StartsWith("250"))
                    {
                        ExitError(_response,ref _error);
                        return false;
                    }

                    AskReceive($"QUIT",ref _log);
                    return true;

                }

                #endregion

                #region method body

                TcpClient client = new TcpClient();

                //Make the connection with timeout
                if (!client.ConnectAsync(server,port).Wait(connectionTimeout))
                {
                    //log = ex.ExceptionToString();
                    error = $"Could not connect '{server}' at port '{port}'";
                    log += Environment.NewLine + error + Environment.NewLine;
                    resume = Environment.NewLine + $"[FAIL] Port={port}. Could not connect '{server}' at port '{port}'" + Environment.NewLine;
                    return false;
                }

                using (client)
                {

                    if (forceSsl)
                    {
                        using (NetworkStream stream = client.GetStream())
                        using (sslStream = GetSslStream(stream)) // new SslStream(stream))
                        {
                            sslStream.AuthenticateAsClient(server);

                            using (sslreader = new StreamReader(sslStream))
                            using (sslwriter = new StreamWriter(sslStream) { AutoFlush = true })
                            {
                                log += Environment.NewLine + Environment.NewLine + "## SSL connection (safe)" + Environment.NewLine;
                                string connectResponse = sslreader.ReadLine();
                                log += "< " + connectResponse + Environment.NewLine;
                                if (!connectResponse.StartsWith("220"))
                                {
                                    ExitError(_response,ref log,ref error);
                                    resume = Environment.NewLine + $"[FAIL] Port={port},Force SSL={forceSsl} (encrypted)";
                                    return false;
                                }

                                if (!(_response = AskReceive($"HELO {Dns.GetHostName()}",ref log)).StartsWith("250"))
                                {
                                    ExitError(_response,Force SSL={forceSsl} (encrypted)";
                                    return false;
                                }

                                _msgfotter = $"Encrypted MSG using SMTP/S on port: {port}";

                                if (SendMsg(ref log,ref error))
                                { resume = Environment.NewLine + $"[SUCCESS] Port={port},Force SSL={forceSsl} (encrypted)"; return true; }
                                else
                                { resume = Environment.NewLine + $"[FAIL] Port={port},Force SSL={forceSsl} (encrypted)"; return false; }
                            }
                        }
                    }
                    else //Not SMTP/S (SSL)
                    {
                        using (NetworkStream stream = client.GetStream())
                        using (clearTextReader = new StreamReader(stream))
                        using (clearTextWriter = new StreamWriter(stream) { AutoFlush = true })

                        {
                            log += Environment.NewLine + Environment.NewLine + "## Plain text connection (UNSAFE)" + Environment.NewLine;
                            string connectResponse = clearTextReader.ReadLine();
                            log += "< " + connectResponse + Environment.NewLine;
                            if (!connectResponse.StartsWith("220"))
                            {
                                ExitError(connectResponse,ref error);
                                resume = Environment.NewLine + $"[FAIL] Port={port},Force SSL={forceSsl} (unsafe,plain text)";
                                return false;
                            }

                            if (!(_response = AskReceive($"HELO {Dns.GetHostName()}",ref log)).StartsWith("250"))
                            {
                                ExitError(_response,plain text)";
                                return false;
                            }

                            if (trystartssl)
                            {
                                if ((_response = AskReceive("STARTTLS",ref log)).StartsWith("220"))
                                {
                                    clearTextReader = null;
                                    clearTextWriter = null;
                                    using (sslStream = GetSslStream(stream)) // new SslStream(stream))
                                    {

                                        //TLS Start
                                        sslStream.AuthenticateAsClient(server);
                                        log += Environment.NewLine + Environment.NewLine + "## TLS connection (safe)" + Environment.NewLine;
                                        using (sslreader = new StreamReader(sslStream))
                                        using (sslwriter = new StreamWriter(sslStream) { AutoFlush = true })
                                        {
                                            if (!AskReceive($"HELO {Dns.GetHostName()}",ref log).StartsWith("250"))
                                            { ExitError(_response,ref error); resume = $"[FAIL] Port={port},Force SSL={forceSsl} (encrypted)"; return false; }

                                            _msgfotter = "Encrypted MSG using STARTSSL on port: " + port.ToString();
                                            if (SendMsg(ref log,ref error))
                                            {
                                                resume = Environment.NewLine +
                                                    $"[SUCCESS] Port={port},try STARTSSL={trystartssl} (encrypted)";
                                                return true;
                                            }
                                            else
                                            {
                                                resume = Environment.NewLine +
                                                    $"[FAIL] Port={port},try STARTSSL={trystartssl} (encrypted)";
                                                return false;
                                            }
                                        }
                                    }
                                }
                            }

                            if ((trystartssl && !_response.StartsWith("220")) || !trystartssl)
                            {

                                if (trystartssl)
                                    log += "## Does not accept StartSSL" + Environment.NewLine;

                                _msgfotter = "Unsafe MSG using plain text on port: " + port.ToString();

                                if (SendMsg(ref log,ref error))
                                {
                                    resume = Environment.NewLine +
                                        $"[SUCCESS] Port={port},try STARTSSL={trystartssl} (unsafe,plain text)";
                                    return true;
                                }
                                else
                                {
                                    resume = Environment.NewLine +
                                        $"[FAIL] Port={port},plain text)";
                                    return false;
                                }
                            }

                            return false;

                        }
                    }

                }

                #endregion

            }
            catch (Exception ex)
            {
                if (ex.Message == @"The remote certificate is invalid according to the validation procedure.")
                {
                    log += $"The host name '{server}' must exist in the server SSL certificate. Don't use IP or host names not in the Certificate" + Environment.NewLine;
                }
                error = ex.Message;
                log += "!! Error : " + ex.Message + Environment.NewLine;
                resume += Environment.NewLine + _errorResume;

                return false;
            }

            
        }


    }
}