public override void Open(NpgsqlConnector context, Int32 timeout)
{
try
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
IAsyncResult result;
// Keep track of time remaining; Even though there may be multiple timeout-able calls,
// this allows us to still respect the caller's timeout expectation.
DateTime attemptStart;
attemptStart = DateTime.Now;
result = Dns.BeginGetHostAddresses(context.Host, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout, true))
{
// Timeout was used up attempting the Dns lookup
throw new TimeoutException(resman.GetString("Exception_DnsLookupTimeout"));
}
timeout -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);
IPAddress[] ips = Dns.EndGetHostAddresses(result);
Socket socket = null;
Exception lastSocketException = null;
// try every ip address of the given hostname, use the first reachable one
// make sure not to exceed the caller's timeout expectation by splitting the
// time we have left between all the remaining ip's in the list.
for (int i = 0; i < ips.Length; i++)
{
NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ips[i]);
IPEndPoint ep = new IPEndPoint(ips[i], context.Port);
socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
attemptStart = DateTime.Now;
try
{
result = socket.BeginConnect(ep, null, null);
if (!result.AsyncWaitHandle.WaitOne(timeout / (ips.Length - i), true))
{
throw new TimeoutException(resman.GetString("Exception_ConnectionTimeout"));
}
socket.EndConnect(result);
// connect was successful, leave the loop
break;
}
catch (Exception e)
{
NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ips[i]);
timeout -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);
lastSocketException = e;
socket.Close();
socket = null;
}
}
if (socket == null)
{
throw lastSocketException;
}
NpgsqlNetworkStream baseStream = new NpgsqlNetworkStream(socket, true);
Stream sslStream = null;
// If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer))
{
baseStream
.WriteInt32(8)
.WriteInt32(80877103);
// Receive response
Char response = (Char)baseStream.ReadByte();
if (response == 'S')
{
//create empty collection
X509CertificateCollection clientCertificates = new X509CertificateCollection();
//trigger the callback to fetch some certificates
context.DefaultProvideClientCertificatesCallback(clientCertificates);
//if (context.UseMonoSsl)
if (!NpgsqlConnector.UseSslStream)
{
SslClientStream sslStreamPriv;
sslStreamPriv = new SslClientStream(
baseStream,
context.Host,
true,
SecurityProtocolType.Default,
clientCertificates);
sslStreamPriv.ClientCertSelectionDelegate =
new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
sslStreamPriv.ServerCertValidationDelegate =
new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
sslStreamPriv.PrivateKeyCertSelectionDelegate =
new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
sslStream = sslStreamPriv;
}
else
{
SslStream sslStreamPriv;
sslStreamPriv = new SslStream(baseStream, true, context.DefaultValidateRemoteCertificateCallback);
sslStreamPriv.AuthenticateAsClient(context.Host, clientCertificates, System.Security.Authentication.SslProtocols.Default, false);
sslStream = sslStreamPriv;
}
}
else if (context.SslMode == SslMode.Require)
{
throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError"));
}
}
context.Socket = socket;
context.BaseStream = baseStream;
context.Stream = new BufferedStream(sslStream == null ? baseStream : sslStream, 8192);
NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
ChangeState(context, NpgsqlConnectedState.Instance);
}
catch (Exception e)
{
throw new NpgsqlException(string.Format(resman.GetString("Exception_FailedConnection"), context.Host), e);
}
}