blob: 2f0c5e9e7c8911e009c08e49657bff714f372a51 [file] [log] [blame]
using DotPulsar.Exceptions;
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace DotPulsar.Internal
{
public sealed class Connector
{
private readonly X509Certificate2 _trustedCertificateAuthority;
private readonly bool _verifyCertificateAuthority;
private readonly bool _verifyCertificateName;
public Connector(X509Certificate2 trustedCertificateAuthority, bool verifyCertificateAuthority, bool verifyCertificateName)
{
_trustedCertificateAuthority = trustedCertificateAuthority;
_verifyCertificateAuthority = verifyCertificateAuthority;
_verifyCertificateName = verifyCertificateName;
}
public async Task<Stream> Connect(Uri serviceUrl)
{
var scheme = serviceUrl.Scheme;
var host = serviceUrl.Host;
var port = serviceUrl.Port;
var encrypt = scheme == Constants.PulsarSslScheme;
if (port == -1)
port = encrypt ? Constants.DefaultPulsarSSLPort : Constants.DefaultPulsarPort;
var stream = await GetStream(host, port);
if (encrypt)
stream = await EncryptStream(stream, host);
return stream;
}
private async Task<Stream> GetStream(string host, int port)
{
var tcpClient = new TcpClient();
try
{
var type = Uri.CheckHostName(host);
if (type == UriHostNameType.IPv4 || type == UriHostNameType.IPv6)
await tcpClient.ConnectAsync(IPAddress.Parse(host), port);
else
await tcpClient.ConnectAsync(host, port);
return tcpClient.GetStream();
}
catch
{
tcpClient.Dispose();
throw;
}
}
private async Task<Stream> EncryptStream(Stream stream, string host)
{
SslStream sslStream = null;
try
{
sslStream = new SslStream(stream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
await sslStream.AuthenticateAsClientAsync(host);
return sslStream;
}
catch (System.Security.Authentication.AuthenticationException exception)
{
if (sslStream == null)
stream.Dispose();
else
sslStream.Dispose();
throw new AuthenticationException("Got an authentication exception while trying to establish an encrypted connection. See inner exception for details.", exception);
}
}
private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable))
return false;
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) && _verifyCertificateName)
return false;
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) && _verifyCertificateAuthority)
{
if (_trustedCertificateAuthority is null)
return false;
chain.ChainPolicy.ExtraStore.Add(_trustedCertificateAuthority);
_ = chain.Build((X509Certificate2)certificate);
for (var i = 0; i < chain.ChainElements.Count; i++)
{
if (chain.ChainElements[i].Certificate.Thumbprint == _trustedCertificateAuthority.Thumbprint)
return true;
}
return false;
}
return true;
}
}
}