MqttTcpChannel.Uwp.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #if WINDOWS_UWP
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.InteropServices.WindowsRuntime;
  7. using System.Security.Authentication;
  8. using System.Security.Cryptography.X509Certificates;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using Windows.Networking;
  12. using Windows.Networking.Sockets;
  13. using Windows.Security.Cryptography.Certificates;
  14. using MQTTnet.Channel;
  15. using MQTTnet.Client.Options;
  16. using MQTTnet.Server;
  17. namespace MQTTnet.Implementations
  18. {
  19. public class MqttTcpChannel : IMqttChannel
  20. {
  21. private readonly MqttClientTcpOptions _options;
  22. private readonly int _bufferSize;
  23. private StreamSocket _socket;
  24. private Stream _readStream;
  25. private Stream _writeStream;
  26. public MqttTcpChannel(IMqttClientOptions clientOptions)
  27. {
  28. _options = (MqttClientTcpOptions)clientOptions.ChannelOptions;
  29. _bufferSize = _options.BufferSize;
  30. }
  31. public MqttTcpChannel(StreamSocket socket, X509Certificate2 clientCertificate, IMqttServerOptions serverOptions)
  32. {
  33. _socket = socket ?? throw new ArgumentNullException(nameof(socket));
  34. _bufferSize = serverOptions.DefaultEndpointOptions.BufferSize;
  35. CreateStreams();
  36. IsSecureConnection = socket.Information.ProtectionLevel >= SocketProtectionLevel.Tls12;
  37. ClientCertificate = clientCertificate;
  38. Endpoint = _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
  39. }
  40. public static Func<MqttClientTcpOptions, IEnumerable<ChainValidationResult>> CustomIgnorableServerCertificateErrorsResolver { get; set; }
  41. public string Endpoint { get; private set; }
  42. public bool IsSecureConnection { get; }
  43. public X509Certificate2 ClientCertificate { get; }
  44. public async Task ConnectAsync(CancellationToken cancellationToken)
  45. {
  46. if (_socket == null)
  47. {
  48. _socket = new StreamSocket();
  49. _socket.Control.NoDelay = _options.NoDelay;
  50. _socket.Control.KeepAlive = true;
  51. }
  52. if (_options.TlsOptions?.UseTls != true)
  53. {
  54. await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString()).AsTask().ConfigureAwait(false);
  55. }
  56. else
  57. {
  58. _socket.Control.ClientCertificate = LoadCertificate(_options);
  59. foreach (var ignorableChainValidationResult in ResolveIgnorableServerCertificateErrors())
  60. {
  61. _socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
  62. }
  63. var socketProtectionLevel = SocketProtectionLevel.Tls12;
  64. if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
  65. {
  66. socketProtectionLevel = SocketProtectionLevel.Tls11;
  67. }
  68. else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
  69. {
  70. socketProtectionLevel = SocketProtectionLevel.Tls10;
  71. }
  72. await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), socketProtectionLevel).AsTask().ConfigureAwait(false);
  73. }
  74. Endpoint = _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
  75. CreateStreams();
  76. }
  77. public Task DisconnectAsync(CancellationToken cancellationToken)
  78. {
  79. Dispose();
  80. return Task.FromResult(0);
  81. }
  82. public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  83. {
  84. return _readStream.ReadAsync(buffer, offset, count, cancellationToken);
  85. }
  86. public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  87. {
  88. // In the write method only the internal buffer will be filled. So here is no
  89. // async/await required. The real network transmit is done when calling the
  90. // Flush method.
  91. _writeStream.Write(buffer, offset, count);
  92. return _writeStream.FlushAsync(cancellationToken);
  93. }
  94. public void Dispose()
  95. {
  96. TryDispose(_readStream, () => _readStream = null);
  97. TryDispose(_writeStream, () => _writeStream = null);
  98. TryDispose(_socket, () => _socket = null);
  99. }
  100. private static Certificate LoadCertificate(IMqttClientChannelOptions options)
  101. {
  102. if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
  103. {
  104. return null;
  105. }
  106. if (options.TlsOptions.Certificates.Count > 1)
  107. {
  108. throw new NotSupportedException("Only one client certificate is supported for UWP.");
  109. }
  110. return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
  111. }
  112. private IEnumerable<ChainValidationResult> ResolveIgnorableServerCertificateErrors()
  113. {
  114. if (CustomIgnorableServerCertificateErrorsResolver != null)
  115. {
  116. return CustomIgnorableServerCertificateErrorsResolver(_options);
  117. }
  118. var result = new List<ChainValidationResult>();
  119. if (_options.TlsOptions.IgnoreCertificateRevocationErrors)
  120. {
  121. result.Add(ChainValidationResult.RevocationInformationMissing);
  122. //_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); Not supported.
  123. result.Add(ChainValidationResult.RevocationFailure);
  124. }
  125. if (_options.TlsOptions.IgnoreCertificateChainErrors)
  126. {
  127. result.Add(ChainValidationResult.IncompleteChain);
  128. }
  129. if (_options.TlsOptions.AllowUntrustedCertificates)
  130. {
  131. result.Add(ChainValidationResult.Untrusted);
  132. }
  133. return result;
  134. }
  135. private void CreateStreams()
  136. {
  137. // Attention! Do not set the buffer for the read method. This will
  138. // limit the internal buffer and the read operation will hang forever
  139. // if more data than the buffer size was received.
  140. _readStream = _socket.InputStream.AsStreamForRead();
  141. _writeStream = _socket.OutputStream.AsStreamForWrite(_bufferSize);
  142. }
  143. private static void TryDispose(IDisposable disposable, Action afterDispose)
  144. {
  145. try
  146. {
  147. disposable?.Dispose();
  148. }
  149. catch (ObjectDisposedException)
  150. {
  151. }
  152. catch (NullReferenceException)
  153. {
  154. }
  155. finally
  156. {
  157. afterDispose();
  158. }
  159. }
  160. }
  161. }
  162. #endif