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.
 
 
 
 

158 lines
5.4 KiB

  1. using System;
  2. using System.Net.Security;
  3. using System.Net.Sockets;
  4. using System.Security.Authentication;
  5. using System.Security.Cryptography.X509Certificates;
  6. using System.Threading.Tasks;
  7. using MQTTnet.Core.Channel;
  8. using MQTTnet.Core.Client;
  9. using System.IO;
  10. using System.Linq;
  11. namespace MQTTnet.Implementations
  12. {
  13. public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
  14. {
  15. private readonly MqttClientTcpOptions _options;
  16. //todo: this can be used with min dependency NetStandard1.6
  17. #if NET45
  18. // ReSharper disable once MemberCanBePrivate.Global
  19. // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
  20. public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
  21. #endif
  22. private Socket _socket;
  23. private SslStream _sslStream;
  24. /// <summary>
  25. /// called on client sockets are created in connect
  26. /// </summary>
  27. public MqttTcpChannel(MqttClientTcpOptions options)
  28. {
  29. _options = options ?? throw new ArgumentNullException(nameof(options));
  30. }
  31. /// <summary>
  32. /// called on server, sockets are passed in
  33. /// connect will not be called
  34. /// </summary>
  35. public MqttTcpChannel(Socket socket, SslStream sslStream)
  36. {
  37. _socket = socket ?? throw new ArgumentNullException(nameof(socket));
  38. _sslStream = sslStream;
  39. CreateStreams(socket, sslStream);
  40. }
  41. public Stream SendStream { get; private set; }
  42. public Stream ReceiveStream { get; private set; }
  43. public Stream RawReceiveStream { 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 NET45
  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(IMqttClientOptions 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. RawReceiveStream = sslStream ?? new NetworkStream(socket);
  118. //cannot use this as default buffering prevents from receiving the first connect message
  119. //need two streams otherwise read and write have to be synchronized
  120. //todo: if branch can be used with min dependency NetStandard1.6
  121. #if NET45
  122. SendStream = new BufferedStream(RawReceiveStream, BufferSize);
  123. ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize);
  124. #else
  125. SendStream = RawReceiveStream;
  126. ReceiveStream = RawReceiveStream;
  127. #endif
  128. }
  129. }
  130. }