You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

155 lines
5.2 KiB

  1. #if NET452 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
  2. using System;
  3. using System.Net.Security;
  4. using System.Net.Sockets;
  5. using System.Security.Authentication;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Threading.Tasks;
  8. using System.IO;
  9. using System.Linq;
  10. using MQTTnet.Channel;
  11. using MQTTnet.Client;
  12. namespace MQTTnet.Implementations
  13. {
  14. public sealed class MqttTcpChannel : IMqttChannel, IDisposable
  15. {
  16. private readonly MqttClientTcpOptions _options;
  17. //todo: this can be used with min dependency NetStandard1.6
  18. #if NET452 || NET461
  19. // ReSharper disable once MemberCanBePrivate.Global
  20. // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
  21. public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
  22. #endif
  23. private Socket _socket;
  24. private SslStream _sslStream;
  25. /// <summary>
  26. /// called on client sockets are created in connect
  27. /// </summary>
  28. public MqttTcpChannel(MqttClientTcpOptions options)
  29. {
  30. _options = options ?? throw new ArgumentNullException(nameof(options));
  31. }
  32. /// <summary>
  33. /// called on server, sockets are passed in
  34. /// connect will not be called
  35. /// </summary>
  36. public MqttTcpChannel(Socket socket, SslStream sslStream)
  37. {
  38. _socket = socket ?? throw new ArgumentNullException(nameof(socket));
  39. _sslStream = sslStream;
  40. CreateStreams(socket, sslStream);
  41. }
  42. public Stream SendStream { get; private set; }
  43. public Stream ReceiveStream { get; private set; }
  44. public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; }
  45. public async Task ConnectAsync()
  46. {
  47. if (_socket == null)
  48. {
  49. _socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
  50. }
  51. //todo: else brach can be used with min dependency NET46
  52. #if NET452 || NET461
  53. await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
  54. #else
  55. await _socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
  56. #endif
  57. if (_options.TlsOptions.UseTls)
  58. {
  59. _sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
  60. await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
  61. }
  62. CreateStreams(_socket, _sslStream);
  63. }
  64. public Task DisconnectAsync()
  65. {
  66. Dispose();
  67. return Task.FromResult(0);
  68. }
  69. public void Dispose()
  70. {
  71. _socket?.Dispose();
  72. _socket = null;
  73. _sslStream?.Dispose();
  74. _sslStream = null;
  75. }
  76. private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
  77. {
  78. if (CustomCertificateValidationCallback != null)
  79. {
  80. return CustomCertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _options);
  81. }
  82. if (sslPolicyErrors == SslPolicyErrors.None)
  83. {
  84. return true;
  85. }
  86. if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown))
  87. {
  88. if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
  89. {
  90. return false;
  91. }
  92. }
  93. if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
  94. {
  95. if (!_options.TlsOptions.IgnoreCertificateChainErrors)
  96. {
  97. return false;
  98. }
  99. }
  100. return _options.TlsOptions.AllowUntrustedCertificates;
  101. }
  102. private static X509CertificateCollection LoadCertificates(MqttClientTcpOptions options)
  103. {
  104. var certificates = new X509CertificateCollection();
  105. if (options.TlsOptions.Certificates == null)
  106. {
  107. return certificates;
  108. }
  109. foreach (var certificate in options.TlsOptions.Certificates)
  110. {
  111. certificates.Add(new X509Certificate(certificate));
  112. }
  113. return certificates;
  114. }
  115. private void CreateStreams(Socket socket, Stream sslStream)
  116. {
  117. var stream = sslStream ?? new NetworkStream(socket);
  118. //todo: if branch can be used with min dependency NetStandard1.6
  119. #if NET452 || NET461
  120. SendStream = new BufferedStream(stream, BufferSize);
  121. ReceiveStream = new BufferedStream(stream, BufferSize);
  122. #else
  123. SendStream = stream;
  124. ReceiveStream = stream;
  125. #endif
  126. }
  127. }
  128. }
  129. #endif