context: // Copyright ?2004, 2011, Oracle and/or its affiliates. All rights reserved.
  //
  // MySQL Connector/NET is licensed under the terms of the GPLv2
  // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most 
  // MySQL Connectors. There are special exceptions to the terms and 
  // conditions of the GPLv2 as it is applied to this software, see the 
  // FLOSS License Exception
  // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  //
  // This program is free software; you can redistribute it and/or modify 
  // it under the terms of the GNU General Public License as published 
  // by the Free Software Foundation; version 2 of the License.
  //
  // This program is distributed in the hope that it will be useful, but 
  // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
  // for more details.
  //
  // You should have received a copy of the GNU General Public License along 
  // with this program; if not, write to the Free Software Foundation, Inc., 
  // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  
  using System;
  using System.Collections;
  using System.Diagnostics;
  using System.IO;
  using System.Security;
  using MySql.Data.Common;
  using MySql.Data.Types;
  using System.Security.Cryptography.X509Certificates;
  using MySql.Data.MySqlClient.Properties;
  using System.Text;
  using MySql.Data.MySqlClient.Authentication;
  #if !CF
  using System.Net.Security;
  using System.Security.Authentication;
  using System.Globalization;
  #endif
  
  namespace MySql.Data.MySqlClient
  {
    /// <summary>
    /// Summary description for Driver.
    /// </summary>
    internal class NativeDriver : IDriver
    {
      private DBVersion version;
      private int threadId;
      protected String encryptionSeed;
      internal string EncryptionSeed { get { return encryptionSeed; } }
      protected ServerStatusFlags serverStatus;
      protected MySqlStream stream;
      protected Stream baseStream;
      private BitArray nullMap;
      private MySqlPacket packet;
      private ClientFlags connectionFlags;
      private Driver owner;
      private int warnings;
      private MySqlAuthenticationPlugin authPlugin;
  
      // Windows authentication method string, used by the protocol.
      // Also known as "client plugin name".
      const string AuthenticationWindowsPlugin = "authentication_windows_client";
  
      // Predefined username for IntegratedSecurity
      const string AuthenticationWindowsUser = "auth_windows";
  
      public NativeDriver(Driver owner)
      {
        this.owner = owner;
        threadId = -1;
      }
  
      public ClientFlags Flags
      {
        get { return connectionFlags; }
      }
  
      public int ThreadId
      {
        get { return threadId; }
      }
  
      public DBVersion Version
      {
        get { return version; }
      }
  
      public ServerStatusFlags ServerStatus
      {
        get { return serverStatus; }
      }
  
      public int WarningCount
      {
        get { return warnings; }
      }
  
      public MySqlPacket Packet
      {
        get { return packet; }
      }
  
      internal MySqlConnectionStringBuilder Settings
      {
        get { return owner.Settings; }
      }
  
      internal Encoding Encoding
      {
        get { return owner.Encoding; }
      }
  
      private void HandleException(MySqlException ex)
      {
        if (ex.IsFatal)
          owner.Close();
      }
  
      internal void SendPacket(MySqlPacket p)
      {
        stream.SendPacket(p);
      }
  
      internal MySqlPacket ReadPacket()
      {
        return packet = stream.ReadPacket();
      }
  
      internal void ReadOk(bool read)
      {
        try
        {
          if (read)
            packet = stream.ReadPacket();
          var marker = (byte)packet.ReadByte();
          if (marker != 0)
          {
            throw new MySqlException("Out of sync with server", true, null);
          }
  
          packet.ReadFieldLength(); /* affected rows */
          packet.ReadFieldLength(); /* last insert id */
          if (packet.HasMoreData)
          {
            serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
            packet.ReadInteger(2);  /* warning count */
            if (packet.HasMoreData)
            {
              packet.ReadLenString();  /* message */
            }
          }
        }
        catch (MySqlException ex)
        {
          HandleException(ex);
          throw;
        }
      }
  
      /// <summary>
      /// Sets the current database for the this connection
      /// </summary>
      /// <param name="dbName"></param>
      public void SetDatabase(string dbName)
      {
        var dbNameBytes = Encoding.GetBytes(dbName);
  
        packet.Clear();
        packet.WriteByte((byte)DBCmd.INIT_DB);
        packet.Write(dbNameBytes);
        ExecutePacket(packet);
  
        ReadOk(true);
      }
  
      public void Configure()
      {
        stream.MaxPacketSize = (ulong)owner.MaxPacketSize;
        stream.Encoding = Encoding;
      }
  
      public void Open()
      {
        // connect to one of our specified hosts
        try
        {
  #if !CF
          if (Settings.ConnectionProtocol == MySqlConnectionProtocol.SharedMemory)
          {
            var str = new SharedMemoryStream(Settings.SharedMemoryName);
            str.Open(Settings.ConnectionTimeout);
            baseStream = str;
          }
          else
          {
  #endif
            var pipeName = Settings.PipeName;
            if (Settings.ConnectionProtocol != MySqlConnectionProtocol.NamedPipe)
              pipeName = null;
            var sc = new StreamCreator(Settings.Server, Settings.Port, pipeName,
                Settings.Keepalive, Version);
  #if !CF
            MySqlSecurityPermission.CreatePermissionSet(false).Assert();
  #endif
            baseStream = sc.GetStream(Settings.ConnectionTimeout);
  #if !CF
          }
  #endif
        }
        catch (SecurityException)
        {
          throw;
        }
        catch (Exception ex)
        {
          throw new MySqlException(Resources.UnableToConnectToHost,
              (int)MySqlErrorCode.UnableToConnectToHost, ex);
        }
        if (baseStream == null)
          throw new MySqlException(Resources.UnableToConnectToHost,
              (int)MySqlErrorCode.UnableToConnectToHost);
  
        var maxSinglePacket = 255 * 255 * 255;
        stream = new MySqlStream(baseStream, Encoding, false);
  
        stream.ResetTimeout((int)Settings.ConnectionTimeout * 1000);
  
        // read off the welcome packet and parse out it's values
        packet = stream.ReadPacket();
        int protocol = packet.ReadByte();
        var versionString = packet.ReadString();
        version = DBVersion.Parse(versionString);
        if (!version.isAtLeast(5, 0, 0))
          throw new NotSupportedException(Resources.ServerTooOld);
        threadId = packet.ReadInteger(4);
        encryptionSeed = packet.ReadString();
  
        maxSinglePacket = (256 * 256 * 256) - 1;
  
        // read in Server capabilities if they are provided
        ClientFlags serverCaps = 0;
        if (packet.HasMoreData)
          serverCaps = (ClientFlags)packet.ReadInteger(2);
  
        /* New protocol with 16 bytes to describe server characteristics */
        owner.ConnectionCharSetIndex = (int)packet.ReadByte();
  
        serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
  
        // Since 5.5, high bits of server caps are stored after status.
        // Previously, it was part of reserved always 0x00 13-byte filler.
        var serverCapsHigh = (uint)packet.ReadInteger(2);
        serverCaps |= (ClientFlags)(serverCapsHigh << 16);
  
        packet.Position += 11;
        var seedPart2 = packet.ReadString();
        encryptionSeed += seedPart2;
  
        var authenticationMethod = "";
        if ((serverCaps & ClientFlags.PLUGIN_AUTH) != 0)
        {
          authenticationMethod = packet.ReadString();
        }
        else
        {
          // Some MySql versions like 5.1, don't give name of plugin, default to native password.
          authenticationMethod = "mysql_native_password";
        }
  
        // based on our settings, set our connection flags
        SetConnectionFlags(serverCaps);
  
        packet.Clear();
        packet.WriteInteger((int)connectionFlags, 4);
  
  #if !CF
        if ((serverCaps & ClientFlags.SSL) == 0)
        {
          if ((Settings.SslMode != MySqlSslMode.None)
          && (Settings.SslMode != MySqlSslMode.Preferred))
          {
            // Client requires SSL connections.
            var message = String.Format(Resources.NoServerSSLSupport,
                Settings.Server);
            throw new MySqlException(message);
          }
        }
        else if (Settings.SslMode != MySqlSslMode.None)
        {
          stream.SendPacket(packet);
          StartSSL();
          packet.Clear();
          packet.WriteInteger((int)connectionFlags, 4);
        }
  #endif
  
        packet.WriteInteger(maxSinglePacket, 4);
        packet.WriteByte(8);
        packet.Write(new byte[23]);
  
        Authenticate(authenticationMethod, false);
  
        // if we are using compression, then we use our CompressedStream class
        // to hide the ugliness of managing the compression
        if ((connectionFlags & ClientFlags.COMPRESS) != 0)
          stream = new MySqlStream(baseStream, Encoding, true);
  
        // give our stream the server version we are connected to.  
        // We may have some fields that are read differently based 
        // on the version of the server we are connected to.
        packet.Version = version;
        stream.MaxBlockSize = maxSinglePacket;
      }
  
  #if !CF
  
      #region SSL
  
      /// <summary>
      /// Retrieve client SSL certificates. Dependent on connection string 
      /// settings we use either file or store based certificates.
      /// </summary>
      private X509CertificateCollection GetClientCertificates()
      {
        var certs = new X509CertificateCollection();
  
        // Check for file-based certificate
        if (Settings.CertificateFile != null)
        {
          if (!Version.isAtLeast(5, 1, 0))
            throw new MySqlException(Resources.FileBasedCertificateNotSupported);
  
          var clientCert = new X509Certificate2(Settings.CertificateFile,
              Settings.CertificatePassword);
          certs.Add(clientCert);
          return certs;
        }
  
        if (Settings.CertificateStoreLocation == MySqlCertificateStoreLocation.None)
          return certs;
  
        var location =
            (Settings.CertificateStoreLocation == MySqlCertificateStoreLocation.CurrentUser) ?
            StoreLocation.CurrentUser : StoreLocation.LocalMachine;
  
        // Check for store-based certificate
        var store = new X509Store(StoreName.My, location);
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
  
  
        if (Settings.CertificateThumbprint == null)
        {
          // Return all certificates from the store.
          certs.AddRange(store.Certificates);
          return certs;
        }
  
        // Find certificate with given thumbprint
        certs.AddRange(store.Certificates.Find(X509FindType.FindByThumbprint,
                  Settings.CertificateThumbprint, true));
  
        if (certs.Count == 0)
        {
          throw new MySqlException("Certificate with Thumbprint " +
             Settings.CertificateThumbprint + " not found");
        }
        return certs;
      }
  
  
      private void StartSSL()
      {
        var sslValidateCallback =
            new RemoteCertificateValidationCallback(ServerCheckValidation);
        var ss = new SslStream(baseStream, true, sslValidateCallback, null);
        var certs = GetClientCertificates();
        ss.AuthenticateAsClient(Settings.Server, certs, SslProtocols.Default, false);
        baseStream = ss;
        stream = new MySqlStream(ss, Encoding, false);
        stream.SequenceByte = 2;
  
      }
  
      private bool ServerCheckValidation(object sender, X509Certificate certificate,
                                                X509Chain chain, SslPolicyErrors sslPolicyErrors)
      {
        if (sslPolicyErrors == SslPolicyErrors.None)
          return true;
  
        if (Settings.SslMode == MySqlSslMode.Preferred ||
  
gt:           Settings.SslMode == MySqlSslMode.Required)