Refactor proxy usage and support. Refactor TLS parameter usage.

This commit is contained in:
Christian Kratky
2018-07-26 10:35:52 +02:00
parent 65e239652d
commit 60bc4ebcee
5 changed files with 96 additions and 88 deletions
@@ -1,8 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using MQTTnet.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Client namespace MQTTnet.Client
@@ -10,10 +7,10 @@ namespace MQTTnet.Client
public class MqttClientOptionsBuilder public class MqttClientOptionsBuilder
{ {
private readonly MqttClientOptions _options = new MqttClientOptions(); private readonly MqttClientOptions _options = new MqttClientOptions();
private MqttClientTcpOptions _tcpOptions; private MqttClientTcpOptions _tcpOptions;
private MqttClientWebSocketOptions _webSocketOptions; private MqttClientWebSocketOptions _webSocketOptions;
private MqttClientOptionsBuilderTlsParameters _tlsParameters;
private MqttClientTlsOptions _tlsOptions;
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value) public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
{ {
@@ -88,7 +85,7 @@ namespace MQTTnet.Client
throw new InvalidOperationException("A WebSocket channel must be set if MqttClientWebSocketProxy is configured."); throw new InvalidOperationException("A WebSocket channel must be set if MqttClientWebSocketProxy is configured.");
} }
_webSocketOptions.MqttClientWebSocketProxy = new MqttClientWebSocketProxyOptions _webSocketOptions.ProxyOptions = new MqttClientWebSocketProxyOptions
{ {
Address = address, Address = address,
Username = username, Username = username,
@@ -112,86 +109,66 @@ namespace MQTTnet.Client
return this; return this;
} }
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
{
_tlsParameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
return this;
}
public MqttClientOptionsBuilder WithTls() public MqttClientOptionsBuilder WithTls()
{ {
return WithTls(null); return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
}
public MqttClientOptionsBuilder WithTls(Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback)
{
return WithTls(SslProtocols.None, certificateValidationCallback);
}
public MqttClientOptionsBuilder WithTls(SslProtocols sslProtocol,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
{
return WithTls(new byte[][] { }, sslProtocol, certificateValidationCallback);
}
public MqttClientOptionsBuilder WithTls(byte[][] certificates,
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
{
return WithTls(false, certificates, sslProtocol, certificateValidationCallback);
}
public MqttClientOptionsBuilder WithTls(bool ignoreCertificateRevocationErrors,
byte[][] certificates = null,
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
{
return WithTls(false, ignoreCertificateRevocationErrors, certificates, sslProtocol, certificateValidationCallback);
}
public MqttClientOptionsBuilder WithTls(bool ignoreCertificateChainErrors,
bool ignoreCertificateRevocationErrors = false,
byte[][] certificates = null,
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
{
return WithTls(false, ignoreCertificateChainErrors, ignoreCertificateRevocationErrors, certificates, sslProtocol, certificateValidationCallback);
} }
[Obsolete("Use method _WithTlps_ which accepts the _MqttClientOptionsBuilderTlsParameters_.")]
public MqttClientOptionsBuilder WithTls( public MqttClientOptionsBuilder WithTls(
bool allowUntrustedCertificates, bool allowUntrustedCertificates = false,
bool ignoreCertificateChainErrors = false, bool ignoreCertificateChainErrors = false,
bool ignoreCertificateRevocationErrors = false, bool ignoreCertificateRevocationErrors = false,
byte[][] certificates = null, params byte[][] certificates)
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
{ {
_tlsOptions = new MqttClientTlsOptions _tlsParameters = new MqttClientOptionsBuilderTlsParameters
{ {
UseTls = true, UseTls = true,
AllowUntrustedCertificates = allowUntrustedCertificates, AllowUntrustedCertificates = allowUntrustedCertificates,
IgnoreCertificateChainErrors = ignoreCertificateChainErrors, IgnoreCertificateChainErrors = ignoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors, IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors,
Certificates = certificates?.ToList(), Certificates = certificates?.ToList()
SslProtocol = sslProtocol,
CertificateValidationCallback = certificateValidationCallback
}; };
return this; return this;
} }
public IMqttClientOptions Build() public IMqttClientOptions Build()
{ {
if (_tlsOptions != null) if (_tlsParameters != null)
{ {
if (_tcpOptions == null && _webSocketOptions == null) if (_tcpOptions == null && _webSocketOptions == null)
{ {
throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured."); throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured.");
} }
if (_tcpOptions != null) if (_tlsParameters?.UseTls == true)
{ {
_tcpOptions.TlsOptions = _tlsOptions; var tlsOptions = new MqttClientTlsOptions
} {
else if (_webSocketOptions != null) UseTls = true,
{ SslProtocol = _tlsParameters.SslProtocol,
_webSocketOptions.TlsOptions = _tlsOptions; AllowUntrustedCertificates = _tlsParameters.AllowUntrustedCertificates,
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
};
if (_tcpOptions != null)
{
_tcpOptions.TlsOptions = tlsOptions;
}
else if (_webSocketOptions != null)
{
_webSocketOptions.TlsOptions = tlsOptions;
}
} }
} }
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Client
{
public class MqttClientOptionsBuilderTlsParameters
{
public bool UseTls { get; set; }
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback
{
get;
set;
}
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
public bool AllowUntrustedCertificates { get; set; }
public bool IgnoreCertificateChainErrors { get; set; }
public bool IgnoreCertificateRevocationErrors { get; set; }
}
}
@@ -13,9 +13,7 @@ namespace MQTTnet.Client
public CookieContainer CookieContainer { get; set; } public CookieContainer CookieContainer { get; set; }
#if NET452 || NET461 public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
public MqttClientWebSocketProxyOptions MqttClientWebSocketProxy { get; set; } = new MqttClientWebSocketProxyOptions();
#endif
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions(); public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
@@ -1,6 +1,4 @@
#if NET452 || NET461 namespace MQTTnet.Client
namespace MQTTnet.Client
{ {
public class MqttClientWebSocketProxyOptions public class MqttClientWebSocketProxyOptions
{ {
@@ -16,6 +14,4 @@ namespace MQTTnet.Client
public string[] BypassList { get; set; } public string[] BypassList { get; set; }
} }
} }
#endif
@@ -1,4 +1,5 @@
using System; using System;
using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
@@ -6,10 +7,6 @@ using System.Threading.Tasks;
using MQTTnet.Channel; using MQTTnet.Channel;
using MQTTnet.Client; using MQTTnet.Client;
#if NET452 || NET461
using System.Net;
#endif
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public class MqttWebSocketChannel : IMqttChannel public class MqttWebSocketChannel : IMqttChannel
@@ -49,25 +46,10 @@ namespace MQTTnet.Implementations
var clientWebSocket = new ClientWebSocket(); var clientWebSocket = new ClientWebSocket();
#if NET452 || NET461 if (_options.ProxyOptions != null)
// use proxy if we have an address
if (string.IsNullOrEmpty(_options.MqttClientWebSocketProxy.Address) == false)
{ {
var proxyUri = new Uri(_options.MqttClientWebSocketProxy.Address); clientWebSocket.Options.Proxy = CreateProxy();
// use proxy credentials if we have them
if (string.IsNullOrEmpty(_options.MqttClientWebSocketProxy.Username) == false && string.IsNullOrEmpty(_options.MqttClientWebSocketProxy.Password) == false)
{
var credentials = new NetworkCredential(_options.MqttClientWebSocketProxy.Username, _options.MqttClientWebSocketProxy.Password, _options.MqttClientWebSocketProxy.Domain);
clientWebSocket.Options.Proxy = new WebProxy(proxyUri, _options.MqttClientWebSocketProxy.BypassOnLocal, _options.MqttClientWebSocketProxy.BypassList, credentials);
}
else
{
// use proxy without credentials
clientWebSocket.Options.Proxy = new WebProxy(proxyUri, _options.MqttClientWebSocketProxy.BypassOnLocal, _options.MqttClientWebSocketProxy.BypassList);
}
} }
#endif
if (_options.RequestHeaders != null) if (_options.RequestHeaders != null)
{ {
@@ -156,5 +138,31 @@ namespace MQTTnet.Implementations
_webSocket = null; _webSocket = null;
} }
} }
private IWebProxy CreateProxy()
{
if (string.IsNullOrEmpty(_options.ProxyOptions?.Address))
{
return null;
}
#if WINDOWS_UWP
throw new NotSupportedException("Proxies are not supported in UWP.");
#elif NETSTANDARD1_3
throw new NotSupportedException("Proxies are not supported in netstandard 1.3.");
#else
var proxyUri = new Uri(_options.ProxyOptions.Address);
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
{
var credentials =
new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
}
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
#endif
}
} }
} }