Skip to content

Commit

Permalink
Refactor proxy usage and support. Refactor TLS parameter usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
chkr1011 committed Jul 26, 2018
1 parent 65e2396 commit 60bc4eb
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 87 deletions.
91 changes: 34 additions & 57 deletions Source/MQTTnet/Client/MqttClientOptionsBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using MQTTnet.Serializer;

namespace MQTTnet.Client
{
public class MqttClientOptionsBuilder
{
private readonly MqttClientOptions _options = new MqttClientOptions();

private MqttClientTcpOptions _tcpOptions;
private MqttClientWebSocketOptions _webSocketOptions;

private MqttClientTlsOptions _tlsOptions;
private MqttClientOptionsBuilderTlsParameters _tlsParameters;

public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
{
Expand Down Expand Up @@ -88,7 +85,7 @@ public MqttClientOptionsBuilder WithProxy(string address, string username = null
throw new InvalidOperationException("A WebSocket channel must be set if MqttClientWebSocketProxy is configured.");
}

_webSocketOptions.MqttClientWebSocketProxy = new MqttClientWebSocketProxyOptions
_webSocketOptions.ProxyOptions = new MqttClientWebSocketProxyOptions
{
Address = address,
Username = username,
Expand All @@ -112,86 +109,66 @@ public MqttClientOptionsBuilder WithWebSocketServer(string uri)
return this;
}


public MqttClientOptionsBuilder WithTls()
{
return WithTls(null);
}

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)
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
{
return WithTls(false, ignoreCertificateRevocationErrors, certificates, sslProtocol, certificateValidationCallback);
_tlsParameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
return this;
}

public MqttClientOptionsBuilder WithTls(bool ignoreCertificateChainErrors,
bool ignoreCertificateRevocationErrors = false,
byte[][] certificates = null,
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
public MqttClientOptionsBuilder WithTls()
{
return WithTls(false, ignoreCertificateChainErrors, ignoreCertificateRevocationErrors, certificates, sslProtocol, certificateValidationCallback);
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
}

[Obsolete("Use method _WithTlps_ which accepts the _MqttClientOptionsBuilderTlsParameters_.")]
public MqttClientOptionsBuilder WithTls(
bool allowUntrustedCertificates,
bool allowUntrustedCertificates = false,
bool ignoreCertificateChainErrors = false,
bool ignoreCertificateRevocationErrors = false,
byte[][] certificates = null,
SslProtocols sslProtocol = SslProtocols.Tls12,
Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> certificateValidationCallback = null)
params byte[][] certificates)
{
_tlsOptions = new MqttClientTlsOptions
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
AllowUntrustedCertificates = allowUntrustedCertificates,
IgnoreCertificateChainErrors = ignoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors,
Certificates = certificates?.ToList(),
SslProtocol = sslProtocol,
CertificateValidationCallback = certificateValidationCallback
Certificates = certificates?.ToList()
};

return this;
}


public IMqttClientOptions Build()
{
if (_tlsOptions != null)
if (_tlsParameters != null)
{
if (_tcpOptions == null && _webSocketOptions == null)
{
throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured.");
}

if (_tcpOptions != null)
{
_tcpOptions.TlsOptions = _tlsOptions;
}
else if (_webSocketOptions != null)
if (_tlsParameters?.UseTls == true)
{
_webSocketOptions.TlsOptions = _tlsOptions;
var tlsOptions = new MqttClientTlsOptions
{
UseTls = true,
SslProtocol = _tlsParameters.SslProtocol,
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;
}
}
}

Expand Down
29 changes: 29 additions & 0 deletions Source/MQTTnet/Client/MqttClientOptionsBuilderTlsParameters.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
4 changes: 1 addition & 3 deletions Source/MQTTnet/Client/MqttClientWebSocketOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ public class MqttClientWebSocketOptions : IMqttClientChannelOptions

public CookieContainer CookieContainer { get; set; }

#if NET452 || NET461
public MqttClientWebSocketProxyOptions MqttClientWebSocketProxy { get; set; } = new MqttClientWebSocketProxyOptions();
#endif
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }

public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();

Expand Down
8 changes: 2 additions & 6 deletions Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#if NET452 || NET461

namespace MQTTnet.Client
namespace MQTTnet.Client
{
public class MqttClientWebSocketProxyOptions
{
Expand All @@ -16,6 +14,4 @@ public class MqttClientWebSocketProxyOptions

public string[] BypassList { get; set; }
}
}

#endif
}
50 changes: 29 additions & 21 deletions Source/MQTTnet/Implementations/MqttWebSocketChannel.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System;
using System.Net;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Client;

#if NET452 || NET461
using System.Net;
#endif

namespace MQTTnet.Implementations
{
public class MqttWebSocketChannel : IMqttChannel
Expand Down Expand Up @@ -49,25 +46,10 @@ public async Task ConnectAsync(CancellationToken cancellationToken)

var clientWebSocket = new ClientWebSocket();

#if NET452 || NET461
// use proxy if we have an address
if (string.IsNullOrEmpty(_options.MqttClientWebSocketProxy.Address) == false)
if (_options.ProxyOptions != null)
{
var proxyUri = new Uri(_options.MqttClientWebSocketProxy.Address);

// 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);
}
clientWebSocket.Options.Proxy = CreateProxy();
}
#endif

if (_options.RequestHeaders != null)
{
Expand Down Expand Up @@ -156,5 +138,31 @@ public void Dispose()
_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
}
}
}

0 comments on commit 60bc4eb

Please sign in to comment.