MqttNetworkChannel.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. Copyright (c) 2013, 2014 Paolo Patierno
  3. All rights reserved. This program and the accompanying materials
  4. are made available under the terms of the Eclipse Public License v1.0
  5. and Eclipse Distribution License v1.0 which accompany this distribution.
  6. The Eclipse Public License is available at
  7. http://www.eclipse.org/legal/epl-v10.html
  8. and the Eclipse Distribution License is available at
  9. http://www.eclipse.org/org/documents/edl-v10.php.
  10. Contributors:
  11. Paolo Patierno - initial API and implementation and/or initial documentation
  12. ----------------------------------------------------------------------------
  13. Giovanni Paolo Vigano' - preprocessor directives for platform dependent compilation in Unity
  14. */
  15. #if !(!UNITY_EDITOR&&UNITY_WSA_10_0&&!ENABLE_IL2CPP)
  16. #if SSL
  17. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  18. using Microsoft.SPOT.Net.Security;
  19. #else
  20. using System.Net.Security;
  21. using System.Security.Authentication;
  22. #endif
  23. #endif
  24. using System.Net.Sockets;
  25. using System.Net;
  26. using System.Security.Cryptography.X509Certificates;
  27. using System;
  28. using System.Net.Security;
  29. using System.Security.Authentication;
  30. namespace uPLibrary.Networking.M2Mqtt
  31. {
  32. /// <summary>
  33. /// Channel to communicate over the network
  34. /// </summary>
  35. public class MqttNetworkChannel : IMqttNetworkChannel
  36. {
  37. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  38. private readonly RemoteCertificateValidationCallback userCertificateValidationCallback;
  39. private readonly LocalCertificateSelectionCallback userCertificateSelectionCallback;
  40. #endif
  41. // remote host information
  42. private string remoteHostName;
  43. private IPAddress remoteIpAddress;
  44. private int remotePort;
  45. // socket for communication
  46. private Socket socket;
  47. // using SSL
  48. private bool secure;
  49. // CA certificate (on client)
  50. private X509Certificate caCert;
  51. // Server certificate (on broker)
  52. private X509Certificate serverCert;
  53. // client certificate (on client)
  54. private X509Certificate clientCert;
  55. // SSL/TLS protocol version
  56. private MqttSslProtocols sslProtocol;
  57. /// <summary>
  58. /// Remote host name
  59. /// </summary>
  60. public string RemoteHostName { get { return this.remoteHostName; } }
  61. /// <summary>
  62. /// Remote IP address
  63. /// </summary>
  64. public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } }
  65. /// <summary>
  66. /// Remote port
  67. /// </summary>
  68. public int RemotePort { get { return this.remotePort; } }
  69. #if SSL
  70. // SSL stream
  71. private SslStream sslStream;
  72. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  73. private NetworkStream netStream;
  74. #endif
  75. #endif
  76. /// <summary>
  77. /// Data available on the channel
  78. /// </summary>
  79. public bool DataAvailable
  80. {
  81. get
  82. {
  83. #if SSL
  84. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  85. if (secure)
  86. return this.sslStream.DataAvailable;
  87. else
  88. return (this.socket.Available > 0);
  89. #else
  90. if (secure)
  91. return this.netStream.DataAvailable;
  92. else
  93. return (this.socket.Available > 0);
  94. #endif
  95. #else
  96. return (this.socket.Available > 0);
  97. #endif
  98. }
  99. }
  100. /// <summary>
  101. /// Constructor
  102. /// </summary>
  103. /// <param name="socket">Socket opened with the client</param>
  104. public MqttNetworkChannel(Socket socket)
  105. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  106. : this(socket, false, null, MqttSslProtocols.None, null, null)
  107. #else
  108. : this(socket, false, null, MqttSslProtocols.None)
  109. #endif
  110. {
  111. }
  112. /// <summary>
  113. /// Constructor
  114. /// </summary>
  115. /// <param name="socket">Socket opened with the client</param>
  116. /// <param name="secure">Secure connection (SSL/TLS)</param>
  117. /// <param name="serverCert">Server X509 certificate for secure connection</param>
  118. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  119. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  120. /// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  121. /// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  122. public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol,
  123. RemoteCertificateValidationCallback userCertificateValidationCallback,
  124. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  125. #else
  126. public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol)
  127. #endif
  128. {
  129. this.socket = socket;
  130. this.secure = secure;
  131. this.serverCert = serverCert;
  132. this.sslProtocol = sslProtocol;
  133. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  134. this.userCertificateValidationCallback = userCertificateValidationCallback;
  135. this.userCertificateSelectionCallback = userCertificateSelectionCallback;
  136. #endif
  137. }
  138. /// <summary>
  139. /// Constructor
  140. /// </summary>
  141. /// <param name="remoteHostName">Remote Host name</param>
  142. /// <param name="remotePort">Remote port</param>
  143. public MqttNetworkChannel(string remoteHostName, int remotePort)
  144. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  145. : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None, null, null)
  146. #else
  147. : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None)
  148. #endif
  149. {
  150. }
  151. /// <summary>
  152. /// Constructor
  153. /// </summary>
  154. /// <param name="remoteHostName">Remote Host name</param>
  155. /// <param name="remotePort">Remote port</param>
  156. /// <param name="secure">Using SSL</param>
  157. /// <param name="caCert">CA certificate</param>
  158. /// <param name="clientCert">Client certificate</param>
  159. /// <param name="sslProtocol">SSL/TLS protocol version</param>
  160. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  161. /// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  162. /// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  163. public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
  164. RemoteCertificateValidationCallback userCertificateValidationCallback,
  165. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  166. #else
  167. public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
  168. #endif
  169. {
  170. IPAddress remoteIpAddress = null;
  171. try
  172. {
  173. // check if remoteHostName is a valid IP address and get it
  174. remoteIpAddress = IPAddress.Parse(remoteHostName);
  175. }
  176. catch
  177. {
  178. }
  179. // in this case the parameter remoteHostName isn't a valid IP address
  180. if (remoteIpAddress == null)
  181. {
  182. IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName);
  183. if ((hostEntry != null) && (hostEntry.AddressList.Length > 0))
  184. {
  185. // check for the first address not null
  186. // it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null"
  187. int i = 0;
  188. while (hostEntry.AddressList[i] == null) i++;
  189. remoteIpAddress = hostEntry.AddressList[i];
  190. }
  191. else
  192. {
  193. throw new Exception("No address found for the remote host name");
  194. }
  195. }
  196. this.remoteHostName = remoteHostName;
  197. this.remoteIpAddress = remoteIpAddress;
  198. this.remotePort = remotePort;
  199. this.secure = secure;
  200. this.caCert = caCert;
  201. this.clientCert = clientCert;
  202. this.sslProtocol = sslProtocol;
  203. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  204. this.userCertificateValidationCallback = userCertificateValidationCallback;
  205. this.userCertificateSelectionCallback = userCertificateSelectionCallback;
  206. #endif
  207. }
  208. /// <summary>
  209. /// Connect to remote server
  210. /// </summary>
  211. public void Connect()
  212. {
  213. this.socket = new Socket(this.remoteIpAddress.GetAddressFamily(), SocketType.Stream, ProtocolType.Tcp);
  214. // try connection to the broker
  215. this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort));
  216. #if SSL
  217. // secure channel requested
  218. if (secure)
  219. {
  220. // create SSL stream
  221. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  222. this.sslStream = new SslStream(this.socket);
  223. #else
  224. this.netStream = new NetworkStream(this.socket);
  225. this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
  226. #endif
  227. // server authentication (SSL/TLS handshake)
  228. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  229. this.sslStream.AuthenticateAsClient(this.remoteHostName,
  230. this.clientCert,
  231. new X509Certificate[] { this.caCert },
  232. SslVerification.CertificateRequired,
  233. MqttSslUtility.ToSslPlatformEnum(this.sslProtocol));
  234. #else
  235. X509CertificateCollection clientCertificates = null;
  236. // check if there is a client certificate to add to the collection, otherwise it's null (as empty)
  237. if (this.clientCert != null)
  238. clientCertificates = new X509CertificateCollection(new X509Certificate[] { this.clientCert });
  239. this.sslStream.AuthenticateAsClient(this.remoteHostName,
  240. clientCertificates,
  241. MqttSslUtility.ToSslPlatformEnum(this.sslProtocol),
  242. false);
  243. #endif
  244. }
  245. #endif
  246. }
  247. /// <summary>
  248. /// Send data on the network channel
  249. /// </summary>
  250. /// <param name="buffer">Data buffer to send</param>
  251. /// <returns>Number of byte sent</returns>
  252. public int Send(byte[] buffer)
  253. {
  254. #if SSL
  255. if (this.secure)
  256. {
  257. this.sslStream.Write(buffer, 0, buffer.Length);
  258. this.sslStream.Flush();
  259. return buffer.Length;
  260. }
  261. else
  262. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  263. #else
  264. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  265. #endif
  266. }
  267. /// <summary>
  268. /// Receive data from the network
  269. /// </summary>
  270. /// <param name="buffer">Data buffer for receiving data</param>
  271. /// <returns>Number of bytes received</returns>
  272. public int Receive(byte[] buffer)
  273. {
  274. #if SSL
  275. if (this.secure)
  276. {
  277. // read all data needed (until fill buffer)
  278. int idx = 0, read = 0;
  279. while (idx < buffer.Length)
  280. {
  281. // fixed scenario with socket closed gracefully by peer/broker and
  282. // Read return 0. Avoid infinite loop.
  283. read = this.sslStream.Read(buffer, idx, buffer.Length - idx);
  284. if (read == 0)
  285. return 0;
  286. idx += read;
  287. }
  288. return buffer.Length;
  289. }
  290. else
  291. {
  292. // read all data needed (until fill buffer)
  293. int idx = 0, read = 0;
  294. while (idx < buffer.Length)
  295. {
  296. // fixed scenario with socket closed gracefully by peer/broker and
  297. // Read return 0. Avoid infinite loop.
  298. read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  299. if (read == 0)
  300. return 0;
  301. idx += read;
  302. }
  303. return buffer.Length;
  304. }
  305. #else
  306. // read all data needed (until fill buffer)
  307. int idx = 0, read = 0;
  308. while (idx < buffer.Length)
  309. {
  310. // fixed scenario with socket closed gracefully by peer/broker and
  311. // Read return 0. Avoid infinite loop.
  312. read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  313. if (read == 0)
  314. return 0;
  315. idx += read;
  316. }
  317. return buffer.Length;
  318. #endif
  319. }
  320. /// <summary>
  321. /// Receive data from the network channel with a specified timeout
  322. /// </summary>
  323. /// <param name="buffer">Data buffer for receiving data</param>
  324. /// <param name="timeout">Timeout on receiving (in milliseconds)</param>
  325. /// <returns>Number of bytes received</returns>
  326. public int Receive(byte[] buffer, int timeout)
  327. {
  328. // check data availability (timeout is in microseconds)
  329. if (this.socket.Poll(timeout * 1000, SelectMode.SelectRead))
  330. {
  331. return this.Receive(buffer);
  332. }
  333. else
  334. {
  335. return 0;
  336. }
  337. }
  338. /// <summary>
  339. /// Close the network channel
  340. /// </summary>
  341. public void Close()
  342. {
  343. #if SSL
  344. if (this.secure)
  345. {
  346. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  347. this.netStream.Close();
  348. #endif
  349. this.sslStream.Close();
  350. }
  351. this.socket.Close();
  352. #else
  353. this.socket.Close();
  354. #endif
  355. }
  356. /// <summary>
  357. /// Accept connection from a remote client
  358. /// </summary>
  359. public void Accept()
  360. {
  361. #if SSL
  362. // secure channel requested
  363. if (secure)
  364. {
  365. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  366. this.netStream = new NetworkStream(this.socket);
  367. this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
  368. this.sslStream.AuthenticateAsServer(this.serverCert, false, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), false);
  369. #endif
  370. }
  371. return;
  372. #else
  373. return;
  374. #endif
  375. }
  376. }
  377. /// <summary>
  378. /// IPAddress Utility class
  379. /// </summary>
  380. public static class IPAddressUtility
  381. {
  382. /// <summary>
  383. /// Return AddressFamily for the IP address
  384. /// </summary>
  385. /// <param name="ipAddress">IP address to check</param>
  386. /// <returns>Address family</returns>
  387. public static AddressFamily GetAddressFamily(this IPAddress ipAddress)
  388. {
  389. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  390. return ipAddress.AddressFamily;
  391. #else
  392. return (ipAddress.ToString().IndexOf(':') != -1) ?
  393. AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
  394. #endif
  395. }
  396. }
  397. /// <summary>
  398. /// MQTT SSL utility class
  399. /// </summary>
  400. public static class MqttSslUtility
  401. {
  402. #if (UNITY_EDITOR || !NET_4_6)
  403. public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
  404. {
  405. switch (mqttSslProtocol)
  406. {
  407. case MqttSslProtocols.None:
  408. return SslProtocols.None;
  409. //case MqttSslProtocols.SSLv3:
  410. //return SslProtocols.Ssl3;
  411. case MqttSslProtocols.TLSv1_0:
  412. return SslProtocols.Tls;
  413. case MqttSslProtocols.TLSv1_1:
  414. case MqttSslProtocols.TLSv1_2:
  415. default:
  416. throw new ArgumentException("SSL/TLS protocol version not supported");
  417. }
  418. }
  419. #elif (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK)
  420. public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
  421. {
  422. switch (mqttSslProtocol)
  423. {
  424. case MqttSslProtocols.None:
  425. return SslProtocols.None;
  426. case MqttSslProtocols.SSLv3:
  427. return SslProtocols.Ssl3;
  428. case MqttSslProtocols.TLSv1_0:
  429. return SslProtocols.Tls;
  430. case MqttSslProtocols.TLSv1_1:
  431. return SslProtocols.Tls11;
  432. case MqttSslProtocols.TLSv1_2:
  433. return SslProtocols.Tls12;
  434. default:
  435. throw new ArgumentException("SSL/TLS protocol version not supported");
  436. }
  437. }
  438. #elif (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  439. public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
  440. {
  441. switch (mqttSslProtocol)
  442. {
  443. case MqttSslProtocols.None:
  444. return SslProtocols.None;
  445. case MqttSslProtocols.SSLv3:
  446. return SslProtocols.SSLv3;
  447. case MqttSslProtocols.TLSv1_0:
  448. return SslProtocols.TLSv1;
  449. case MqttSslProtocols.TLSv1_1:
  450. case MqttSslProtocols.TLSv1_2:
  451. default:
  452. throw new ArgumentException("SSL/TLS protocol version not supported");
  453. }
  454. }
  455. #endif
  456. }
  457. }
  458. #endif