diff --git a/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs index 985cda7c..ed8ae03d 100644 --- a/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs +++ b/Source/MQTTnet/Client/MqttClientOptionsBuilder.cs @@ -1,5 +1,8 @@ using System; using System.Linq; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; using MQTTnet.Serializer; namespace MQTTnet.Client @@ -76,6 +79,29 @@ namespace MQTTnet.Client return this; } +#if NET452 || NET461 + + public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null) + { + if (_webSocketOptions == null) + { + throw new InvalidOperationException("A WebSocket channel must be set if MqttClientWebSocketProxy is configured."); + } + + _webSocketOptions.MqttClientWebSocketProxy = new MqttClientWebSocketProxyOptions + { + Address = address, + Username = username, + Password = password, + Domain = domain, + BypassOnLocal = bypassOnLocal, + BypassList = bypassList + }; + + return this; + } +#endif + public MqttClientOptionsBuilder WithWebSocketServer(string uri) { _webSocketOptions = new MqttClientWebSocketOptions @@ -86,11 +112,54 @@ namespace MQTTnet.Client return this; } + + public MqttClientOptionsBuilder WithTls() + { + return WithTls(null); + } + + public MqttClientOptionsBuilder WithTls(Func certificateValidationCallback) + { + return WithTls(SslProtocols.None, certificateValidationCallback); + } + + public MqttClientOptionsBuilder WithTls(SslProtocols sslProtocol, + Func certificateValidationCallback = null) + { + return WithTls(new byte[][] { }, sslProtocol, certificateValidationCallback); + } + + public MqttClientOptionsBuilder WithTls(byte[][] certificates, + SslProtocols sslProtocol = SslProtocols.Tls12, + Func certificateValidationCallback = null) + { + return WithTls(false, certificates, sslProtocol, certificateValidationCallback); + } + + public MqttClientOptionsBuilder WithTls(bool ignoreCertificateRevocationErrors, + byte[][] certificates = null, + SslProtocols sslProtocol = SslProtocols.Tls12, + Func 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 certificateValidationCallback = null) + { + return WithTls(false, ignoreCertificateChainErrors, ignoreCertificateRevocationErrors, certificates, sslProtocol, certificateValidationCallback); + } + public MqttClientOptionsBuilder WithTls( - bool allowUntrustedCertificates = false, + bool allowUntrustedCertificates, bool ignoreCertificateChainErrors = false, bool ignoreCertificateRevocationErrors = false, - params byte[][] certificates) + byte[][] certificates = null, + SslProtocols sslProtocol = SslProtocols.Tls12, + Func certificateValidationCallback = null) { _tlsOptions = new MqttClientTlsOptions { @@ -98,21 +167,14 @@ namespace MQTTnet.Client AllowUntrustedCertificates = allowUntrustedCertificates, IgnoreCertificateChainErrors = ignoreCertificateChainErrors, IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors, - Certificates = certificates?.ToList() + Certificates = certificates?.ToList(), + SslProtocol = sslProtocol, + CertificateValidationCallback = certificateValidationCallback }; return this; } - public MqttClientOptionsBuilder WithTls() - { - _tlsOptions = new MqttClientTlsOptions - { - UseTls = true - }; - - return this; - } public IMqttClientOptions Build() { diff --git a/Source/MQTTnet/Client/MqttClientTlsOptions.cs b/Source/MQTTnet/Client/MqttClientTlsOptions.cs index 2536b231..73d65cbc 100644 --- a/Source/MQTTnet/Client/MqttClientTlsOptions.cs +++ b/Source/MQTTnet/Client/MqttClientTlsOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net.Security; using System.Security.Cryptography.X509Certificates; +using System.Security.Authentication; namespace MQTTnet.Client { @@ -17,6 +18,8 @@ namespace MQTTnet.Client public List Certificates { get; set; } + public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12; + public Func CertificateValidationCallback { get; set; } } } diff --git a/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs b/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs index a4dd0d5c..2cce4310 100644 --- a/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs +++ b/Source/MQTTnet/Client/MqttClientWebSocketOptions.cs @@ -13,6 +13,10 @@ namespace MQTTnet.Client public CookieContainer CookieContainer { get; set; } +#if NET452 || NET461 + public MqttClientWebSocketProxyOptions MqttClientWebSocketProxy { get; set; } = new MqttClientWebSocketProxyOptions(); +#endif + public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions(); public override string ToString() diff --git a/Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs b/Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs new file mode 100644 index 00000000..81f48da8 --- /dev/null +++ b/Source/MQTTnet/Client/MqttClientWebSocketProxyOptions.cs @@ -0,0 +1,21 @@ +#if NET452 || NET461 + +namespace MQTTnet.Client +{ + public class MqttClientWebSocketProxyOptions + { + public string Address { get; set; } + + public string Username { get; set; } + + public string Password { get; set; } + + public string Domain { get; set; } + + public bool BypassOnLocal { get; set; } = true; + + public string[] BypassList { get; set; } + } +} + +#endif \ No newline at end of file diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index c48a06f6..d7c55a77 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -63,7 +63,7 @@ namespace MQTTnet.Implementations if (_options.TlsOptions.UseTls) { sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); - await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); + await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); } CreateStream(sslStream); diff --git a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs index 3243cd76..cf8c183a 100644 --- a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs +++ b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs @@ -6,6 +6,10 @@ using System.Threading.Tasks; using MQTTnet.Channel; using MQTTnet.Client; +#if NET452 || NET461 + using System.Net; +#endif + namespace MQTTnet.Implementations { public class MqttWebSocketChannel : IMqttChannel @@ -14,7 +18,7 @@ namespace MQTTnet.Implementations private readonly MqttClientWebSocketOptions _options; private WebSocket _webSocket; - + public MqttWebSocketChannel(MqttClientWebSocketOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); @@ -45,6 +49,26 @@ namespace MQTTnet.Implementations var clientWebSocket = new ClientWebSocket(); +#if NET452 || NET461 + // use proxy if we have an address + if (string.IsNullOrEmpty(_options.MqttClientWebSocketProxy.Address) == false) + { + 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); + } + } +#endif + if (_options.RequestHeaders != null) { foreach (var requestHeader in _options.RequestHeaders)