SslStream под Mono выдаёт "Couldn't complete EndRead".
От: RekoD  
Дата: 06.09.11 11:16
Оценка:
Под .NET такой код отлично работает:

using System;

using System.Collections;

using System.Net;

using System.Net.Security;

using System.Net.Sockets;

using System.Security.Authentication;

using System.Text;

using System.Text.RegularExpressions;

using System.IO;



using Mono.Security.Protocol.Tls;

using System.Security.Cryptography.X509Certificates;



namespace Pop3Test

{

    class Pop3Auth

    {

        private const int readtimeout = 10000;

        private const int writetimeout = 10000;

        private static Hashtable certificateErrors = new Hashtable ();



        // The following method is invoked by the RemoteCertificateValidationDelegate.

        private static bool ValidateServerCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)

        {

            return ((sslPolicyErrors == SslPolicyErrors.None) ? true : false);

        }



        public static bool Auth (string server, string user, string pass)

        {

            // Получаем host и port

            string[] hostport = server.Split (':');

            string host = hostport[0];

            int port = 110;

            

            try {

                port = System.Convert.ToInt32 (hostport[1]);

            } catch {

                // port = 110 by default

            }

            

            // Собственно аутентификация

            if (port == 995) {

                // Соединяемся

                TcpClient client = new TcpClient (host, port);

                

                // Открываем SSL stream

                SslStream sslStream = new SslStream (client.GetStream (), false, new RemoteCertificateValidationCallback (ValidateServerCertificate), null);

                

                // Настраиваем SSL stream

                sslStream.ReadTimeout = readtimeout;

                sslStream.WriteTimeout = writetimeout;

                

                // SSL аутентификация

                try {

                    sslStream.AuthenticateAsClient(host); // тут выдаёт "Couldn't complete EndRead"

                } catch(Exception e) {

                    Log.Print("POP3AUTH:" + e.Message);

                    client.Close ();

                    throw new Exception ("E_POP3_CONNECT");

                }

                

                string request = "";

                string response = "";

                

                // Читаем "банер" сервера

                response = ReadFrom (sslStream);

                

                // Пишем USER

                request = "USER " + user + "\r\n";

                WriteTo (sslStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (sslStream);

                

                if ((response.Length < 3) || (response.Substring (0, 3) != "+OK")) {

                    client.Close ();

                    throw new Exception ("E_POP3_LOGIN");

                }

                

                // Пишем PASS

                request = "PASS " + pass + "\r\n";

                WriteTo (sslStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (sslStream);

                

                if ((response.Length < 3) || (response.Substring (0, 3) != "+OK")) {

                    client.Close ();

                    throw new Exception ("E_POP3_PASSWORD");

                }

                

                // Пишем QUIT

                request = "QUIT\r\n";

                WriteTo (sslStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (sslStream);

                

                client.Close ();

            } else if (port == 110) {

                // Соединяемся

                TcpClient client = new TcpClient (host, port);

                

                // Открываем TCP stream

                NetworkStream tcpStream = client.GetStream ();

                

                // Настраиваем SSL stream

                tcpStream.ReadTimeout = readtimeout;

                tcpStream.WriteTimeout = writetimeout;

                

                string request = "";

                string response = "";

                

                // Читаем "банер" сервера

                response = ReadFrom (tcpStream);

                

                // Пишем USER

                request = "USER " + user + "\r\n";

                WriteTo (tcpStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (tcpStream);

                

                if ((response.Length < 3) || (response.Substring (0, 3) != "+OK")) {

                    client.Close ();

                    throw new Exception ("E_POP3_LOGIN");

                }

                

                // Пишем PASS

                request = "PASS " + pass + "\r\n";

                WriteTo (tcpStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (tcpStream);

                

                if ((response.Length < 3) || (response.Substring (0, 3) != "+OK")) {

                    client.Close ();

                    throw new Exception ("E_POP3_PASSWORD");

                }

                

                // Пишем QUIT

                request = "QUIT\r\n";

                WriteTo (tcpStream, request);

                

                // Читаем ответ сервера

                response = ReadFrom (tcpStream);

                

                client.Close ();

            } else {

                throw new Exception ("E_POP3_CONNECT");

            }

            

            return true;

        }



        static string ReadFrom (SslStream stream)

        {

            int bytes = -1;

            StringBuilder result = new StringBuilder ();

            byte[] buffer = new byte[2048];

            

            do {

                bytes = stream.Read (buffer, 0, buffer.Length);

                

                // Декодируем и добавляем в основной буфер

                Decoder decoder = Encoding.UTF8.GetDecoder ();

                char[] chars = new char[decoder.GetCharCount (buffer, 0, bytes)];

                decoder.GetChars (buffer, 0, bytes, chars, 0);

                result.Append (chars);

                

                // Проверка, что мы получили строчку

                if (result.ToString ().IndexOf ("\r\n") != -1)

                    break;

            } while (bytes != 0);

            

            return result.ToString ();

        }



        static void WriteTo (SslStream stream, string message)

        {

            stream.Write (Encoding.UTF8.GetBytes (message));

            stream.Flush ();

        }



        static string ReadFrom (NetworkStream stream)

        {

            int bytes = -1;

            StringBuilder result = new StringBuilder ();

            byte[] buffer = new byte[2048];

            

            do {

                bytes = stream.Read (buffer, 0, buffer.Length);

                

                // Декодируем и добавляем в основной буфер

                Decoder decoder = Encoding.UTF8.GetDecoder ();

                char[] chars = new char[decoder.GetCharCount (buffer, 0, bytes)];

                decoder.GetChars (buffer, 0, bytes, chars, 0);

                result.Append (chars);

                

                // Проверка, что мы получили строчку

                if (result.ToString ().IndexOf ("\r\n") != -1)

                    break;

            } while (bytes != 0);

            

            return result.ToString ();

        }



        static void WriteTo (NetworkStream stream, string message)

        {

            byte[] buffer = Encoding.UTF8.GetBytes (message);

            stream.Write (buffer, 0, buffer.Length);

            stream.Flush ();

        }

    }

}


Изначально за основу взят пример: http://msdn.microsoft.com/en-us/library/system.net.security.sslstream.aspx#Y5130

А вот под Mono при вызове sslStream.AuthenticateAsClient(host) выдаёт Exception: "Couldn't complete EndRead".
Я знаю, что EndRead — это асинхронный I/O.
Т.е. sslStream.AuthenticateAsClient() — всегда асинхронный?
Что это за зверь и как с ним бороться?
Есть ли у кого готовый пример, работающий под Mono?

P.S. Код является просто тестом, "написан на коленке", под .NET 100% работает, поэтому просьба не придираться особо по поводу "корявости"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.