| /* |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| namespace DotPulsar.Internal |
| { |
| using System; |
| using System.IO; |
| using System.Net; |
| using System.Net.Security; |
| using System.Net.Sockets; |
| using System.Security.Authentication; |
| using System.Security.Cryptography.X509Certificates; |
| using System.Threading.Tasks; |
| |
| public sealed class Connector |
| { |
| private readonly X509Certificate2Collection _clientCertificates; |
| private readonly X509Certificate2? _trustedCertificateAuthority; |
| private readonly bool _verifyCertificateAuthority; |
| private readonly bool _verifyCertificateName; |
| |
| public Connector( |
| X509Certificate2Collection clientCertificates, |
| X509Certificate2? trustedCertificateAuthority, |
| bool verifyCertificateAuthority, |
| bool verifyCertificateName) |
| { |
| _clientCertificates = clientCertificates; |
| _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).ConfigureAwait(false); |
| |
| if (encrypt) |
| stream = await EncryptStream(stream, host).ConfigureAwait(false); |
| |
| 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).ConfigureAwait(false); |
| else |
| await tcpClient.ConnectAsync(host, port).ConfigureAwait(false); |
| |
| 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, ValidateServerCertificate, null); |
| await sslStream.AuthenticateAsClientAsync(host, _clientCertificates, SslProtocols.None, true).ConfigureAwait(false); |
| return sslStream; |
| } |
| catch |
| { |
| #if NETSTANDARD2_0 |
| if (sslStream is null) |
| stream.Dispose(); |
| else |
| sslStream.Dispose(); |
| #else |
| if (sslStream is null) |
| await stream.DisposeAsync().ConfigureAwait(false); |
| else |
| await sslStream.DisposeAsync().ConfigureAwait(false); |
| #endif |
| |
| throw; |
| } |
| } |
| |
| 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 || chain is null || certificate 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; |
| } |
| } |
| } |