MqttNetworkChannel.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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
  45. private X509Certificate caCert;
  46. /// <summary>
  47. /// Remote host name
  48. /// </summary>
  49. public string RemoteHostName { get { return this.remoteHostName; } }
  50. /// <summary>
  51. /// Remote IP address
  52. /// </summary>
  53. public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } }
  54. /// <summary>
  55. /// Remote port
  56. /// </summary>
  57. public int RemotePort { get { return this.remotePort; } }
  58. #if SSL
  59. // SSL stream
  60. private SslStream sslStream;
  61. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  62. private NetworkStream netStream;
  63. #endif
  64. #endif
  65. /// <summary>
  66. /// Data available on the channel
  67. /// </summary>
  68. public bool DataAvailable
  69. {
  70. get
  71. {
  72. #if SSL
  73. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  74. if (secure)
  75. return this.sslStream.DataAvailable;
  76. else
  77. return (this.socket.Available > 0);
  78. #else
  79. if (secure)
  80. return this.netStream.DataAvailable;
  81. else
  82. return (this.socket.Available > 0);
  83. #endif
  84. #else
  85. return (this.socket.Available > 0);
  86. #endif
  87. }
  88. }
  89. /// <summary>
  90. /// Constructor
  91. /// </summary>
  92. /// <param name="socket">Socket opened with the client</param>
  93. public MqttNetworkChannel(Socket socket)
  94. {
  95. this.socket = socket;
  96. }
  97. /// <summary>
  98. /// Constructor
  99. /// </summary>
  100. /// <param name="remoteHostName">Remote Host name</param>
  101. /// <param name="remotePort">Remote port</param>
  102. public MqttNetworkChannel(string remoteHostName, int remotePort)
  103. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  104. : this(remoteHostName, remotePort, false, null, null, null)
  105. #else
  106. : this(remoteHostName, remotePort, false, null)
  107. #endif
  108. {
  109. }
  110. /// <summary>
  111. /// Constructor
  112. /// </summary>
  113. /// <param name="remoteHostName">Remote Host name</param>
  114. /// <param name="remotePort">Remote port</param>
  115. /// <param name="secure">Using SSL</param>
  116. /// <param name="caCert">CA certificate</param>
  117. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  118. /// <param name="userCertificateSelectionCallback">A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party</param>
  119. /// <param name="userCertificateValidationCallback">A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication</param>
  120. public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert,
  121. RemoteCertificateValidationCallback userCertificateValidationCallback,
  122. LocalCertificateSelectionCallback userCertificateSelectionCallback)
  123. #else
  124. public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert)
  125. #endif
  126. {
  127. IPAddress remoteIpAddress = null;
  128. try
  129. {
  130. // check if remoteHostName is a valid IP address and get it
  131. remoteIpAddress = IPAddress.Parse(remoteHostName);
  132. }
  133. catch
  134. {
  135. }
  136. // in this case the parameter remoteHostName isn't a valid IP address
  137. if (remoteIpAddress == null)
  138. {
  139. IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName);
  140. if ((hostEntry != null) && (hostEntry.AddressList.Length > 0))
  141. {
  142. // check for the first address not null
  143. // it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null"
  144. int i = 0;
  145. while (hostEntry.AddressList[i] == null) i++;
  146. remoteIpAddress = hostEntry.AddressList[i];
  147. }
  148. else
  149. {
  150. throw new Exception("No address found for the remote host name");
  151. }
  152. }
  153. this.remoteHostName = remoteHostName;
  154. this.remoteIpAddress = remoteIpAddress;
  155. this.remotePort = remotePort;
  156. this.secure = secure;
  157. this.caCert = caCert;
  158. #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
  159. this.userCertificateValidationCallback = userCertificateValidationCallback;
  160. this.userCertificateSelectionCallback = userCertificateSelectionCallback;
  161. #endif
  162. }
  163. /// <summary>
  164. /// Connect to remote server
  165. /// </summary>
  166. public void Connect()
  167. {
  168. this.socket = new Socket(this.remoteIpAddress.GetAddressFamily(), SocketType.Stream, ProtocolType.Tcp);
  169. // try connection to the broker
  170. this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort));
  171. #if SSL
  172. // secure channel requested
  173. if (secure)
  174. {
  175. // create SSL stream
  176. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  177. this.sslStream = new SslStream(this.socket);
  178. #else
  179. this.netStream = new NetworkStream(this.socket);
  180. this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
  181. #endif
  182. // server authentication (SSL/TLS handshake)
  183. #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
  184. this.sslStream.AuthenticateAsClient(this.remoteHostName,
  185. null,
  186. new X509Certificate[] { this.caCert },
  187. SslVerification.CertificateRequired,
  188. SslProtocols.TLSv1);
  189. #else
  190. this.sslStream.AuthenticateAsClient(
  191. this.remoteHostName,
  192. null,
  193. SslProtocols.Tls,
  194. false);
  195. #endif
  196. }
  197. #endif
  198. }
  199. /// <summary>
  200. /// Send data on the network channel
  201. /// </summary>
  202. /// <param name="buffer">Data buffer to send</param>
  203. /// <returns>Number of byte sent</returns>
  204. public int Send(byte[] buffer)
  205. {
  206. #if SSL
  207. if (this.secure)
  208. {
  209. this.sslStream.Write(buffer, 0, buffer.Length);
  210. this.sslStream.Flush();
  211. return buffer.Length;
  212. }
  213. else
  214. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  215. #else
  216. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  217. #endif
  218. }
  219. /// <summary>
  220. /// Receive data from the network
  221. /// </summary>
  222. /// <param name="buffer">Data buffer for receiving data</param>
  223. /// <returns>Number of bytes received</returns>
  224. public int Receive(byte[] buffer)
  225. {
  226. #if SSL
  227. if (this.secure)
  228. {
  229. // read all data needed (until fill buffer)
  230. int idx = 0, read = 0;
  231. while (idx < buffer.Length)
  232. {
  233. // fixed scenario with socket closed gracefully by peer/broker and
  234. // Read return 0. Avoid infinite loop.
  235. read = this.sslStream.Read(buffer, idx, buffer.Length - idx);
  236. if (read == 0)
  237. return 0;
  238. idx += read;
  239. }
  240. return buffer.Length;
  241. }
  242. else
  243. {
  244. // read all data needed (until fill buffer)
  245. int idx = 0, read = 0;
  246. while (idx < buffer.Length)
  247. {
  248. // fixed scenario with socket closed gracefully by peer/broker and
  249. // Read return 0. Avoid infinite loop.
  250. read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  251. if (read == 0)
  252. return 0;
  253. idx += read;
  254. }
  255. return buffer.Length;
  256. }
  257. #else
  258. // read all data needed (until fill buffer)
  259. int idx = 0, read = 0;
  260. while (idx < buffer.Length)
  261. {
  262. // fixed scenario with socket closed gracefully by peer/broker and
  263. // Read return 0. Avoid infinite loop.
  264. read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  265. if (read == 0)
  266. return 0;
  267. idx += read;
  268. }
  269. return buffer.Length;
  270. #endif
  271. }
  272. /// <summary>
  273. /// Receive data from the network channel with a specified timeout
  274. /// </summary>
  275. /// <param name="buffer">Data buffer for receiving data</param>
  276. /// <param name="timeout">Timeout on receiving (in milliseconds)</param>
  277. /// <returns>Number of bytes received</returns>
  278. public int Receive(byte[] buffer, int timeout)
  279. {
  280. // check data availability (timeout is in microseconds)
  281. if (this.socket.Poll(timeout * 1000, SelectMode.SelectRead))
  282. {
  283. return this.Receive(buffer);
  284. }
  285. else
  286. {
  287. return 0;
  288. }
  289. }
  290. /// <summary>
  291. /// Close the network channel
  292. /// </summary>
  293. public void Close()
  294. {
  295. #if SSL
  296. if (this.secure)
  297. {
  298. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  299. this.netStream.Close();
  300. #endif
  301. this.sslStream.Close();
  302. }
  303. this.socket.Close();
  304. #else
  305. this.socket.Close();
  306. #endif
  307. }
  308. }
  309. /// <summary>
  310. /// IPAddress Utility class
  311. /// </summary>
  312. public static class IPAddressUtility
  313. {
  314. /// <summary>
  315. /// Return AddressFamily for the IP address
  316. /// </summary>
  317. /// <param name="ipAddress">IP address to check</param>
  318. /// <returns>Address family</returns>
  319. public static AddressFamily GetAddressFamily(this IPAddress ipAddress)
  320. {
  321. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  322. return ipAddress.AddressFamily;
  323. #else
  324. return (ipAddress.ToString().IndexOf(':') != -1) ?
  325. AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
  326. #endif
  327. }
  328. }
  329. }