MqttNetworkChannel.cs 17 KB

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