context: // Npgsql.NpgsqlClosedState.cs
  //
  // Authors:
  // 	Dave Joyner			<d4ljoyn@yahoo.com>
  //	Daniel Nauck		<dna(at)mono-project.de>
  //
  //	Copyright (C) 2002 The Npgsql Development Team
  //	npgsql-general@gborg.postgresql.org
  //	http://gborg.postgresql.org/project/npgsql/projdisplay.php
  //
  // Permission to use, copy, modify, and distribute this software and its
  // documentation for any purpose, without fee, and without a written
  // agreement is hereby granted, provided that the above copyright notice
  // and this paragraph and the following two paragraphs appear in all copies.
  // 
  // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
  // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
  // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
  // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
  // THE POSSIBILITY OF SUCH DAMAGE.
  // 
  // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
  // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
  // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  
  using System;
  using System.IO;
  using System.Net;
  using System.Net.Sockets;
  using System.Reflection;
  using System.Threading;
  using Mono.Security.Protocol.Tls;
  using SecurityProtocolType=Mono.Security.Protocol.Tls.SecurityProtocolType;
  using System.Security.Cryptography.X509Certificates;
  
  namespace Npgsql
  {
  	internal sealed class NpgsqlClosedState : NpgsqlState
  	{
  		private static readonly NpgsqlClosedState _instance = new NpgsqlClosedState();
  		private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name;
  
  
  		private NpgsqlClosedState()
  			: base()
  		{
  		}
  
  		public static NpgsqlClosedState Instance
  		{
  			get
  			{
  				NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Instance");
  				return _instance;
  			}
  		}
  
  
  		/// <summary>
  		/// Resolve a host name or IP address.
  		/// This is needed because if you call Dns.Resolve() with an IP address, it will attempt
  		/// to resolve it as a host name, when it should just convert it to an IP address.
  		/// </summary>
  		/// <param name="HostName"></param>
  		private static IPAddress[] ResolveIPHost(String HostName)
  		{
  			return Dns.GetHostAddresses(HostName);
  		}
  
  		public override void Open(NpgsqlConnector context)
  		{
  			try
  			{
  				NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
  
  				/*TcpClient tcpc = new TcpClient();
                  tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
                  Stream stream = tcpc.GetStream();*/
  
  				/*socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, context.ConnectionTimeout*1000);*/
  
  				//socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
  
  
  				/*Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
                  
                  IAsyncResult result = socket.BeginConnect(new IPEndPoint(ResolveIPHost(context.Host), context.Port), null, null);
  
                  if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout*1000, true))
                  {
                      socket.Close();
                      throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
                  }
  
                  try
                  {
                      socket.EndConnect(result);
                  }
                  catch (Exception)
                  {
                      socket.Close();
                      throw;
                  }
                  */
  
  				IPAddress[] ips = ResolveIPHost(context.Host);
  				Socket socket = null;
  
  				// try every ip address of the given hostname, use the first reachable one
  				foreach (IPAddress ip in ips)
  				{
  					NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ip);
  
  					IPEndPoint ep = new IPEndPoint(ip, context.Port);
  					socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  
  					try
  					{
  						IAsyncResult result = socket.BeginConnect(ep, null, null);
  
  						if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout*1000, true))
  						{
  							socket.Close();
  							throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
  						}
  
  						socket.EndConnect(result);
  
  						// connect was successful, leave the loop
  						break;
  					}
  					catch (Exception)
  					{
  						NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ip);
  						socket.Close();
  					}
  				}
  
  				if (socket == null || !socket.Connected)
  				{
  					throw new Exception(string.Format(resman.GetString("Exception_FailedConnection"), context.Host));
  				}
  
  				Stream stream = new NetworkStream(socket, true);
  
  
  				// If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
  				if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer))
  				{
  					PGUtil.WriteInt32(stream, 8);
  					PGUtil.WriteInt32(stream, 80877103);
  					// Receive response
  
  					Char response = (Char) stream.ReadByte();
  					if (response == 'S')
  					{
                          //create empty collection
                          X509CertificateCollection clientCertificates = new X509CertificateCollection();
                              
                          //trigger the callback to fetch some certificates
                          context.DefaultProvideClientCertificatesCallback(clientCertificates);
  
  						stream = new SslClientStream(
                              stream,
                              context.Host,
                              true,
                              SecurityProtocolType.Default,
                              clientCertificates);
  
  						((SslClientStream) stream).ClientCertSelectionDelegate =
  							new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
  						((SslClientStream) stream).ServerCertValidationDelegate =
  							new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
  						((SslClientStream) stream).PrivateKeyCertSelectionDelegate =
  							new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
  					}
  					else if (context.SslMode == SslMode.Require)
  					{
  						throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError"));
  					}
  				}
  
  				context.Stream = new BufferedStream(stream);
  				context.Socket = socket;
  
  
  				NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
  				ChangeState(context, NpgsqlConnectedState.Instance);
  			}
  				//FIXME: Exceptions that come from what we are handling should be wrapped - e.g. an error connecting to
  				//the server should definitely be presented to the uesr as an NpgsqlError. Exceptions from userland should
  				//be passed untouched - e.g. ThreadAbortException because the user started this in a thread they created and
  				//then aborted should be passed through.
  				//Are there any others that should be pass through? Alternatively, are there a finite number that should
  				//be wrapped?
  			catch (ThreadAbortException)
  			{
  				throw;
  			}
  			catch (Exception e)
  			{
  
gt: 				throw new NpgsqlException(e.Message, e);