MqttNetworkChannel.cs 17 KB

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