MqttNetworkChannel.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. return buffer.Length;
  211. }
  212. else
  213. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  214. #else
  215. return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
  216. #endif
  217. }
  218. /// <summary>
  219. /// Receive data from the network
  220. /// </summary>
  221. /// <param name="buffer">Data buffer for receiving data</param>
  222. /// <returns>Number of bytes received</returns>
  223. public int Receive(byte[] buffer)
  224. {
  225. #if SSL
  226. if (this.secure)
  227. {
  228. // read all data needed (until fill buffer)
  229. int idx = 0;
  230. while (idx < buffer.Length)
  231. {
  232. idx += this.sslStream.Read(buffer, idx, buffer.Length - idx);
  233. }
  234. return buffer.Length;
  235. }
  236. else
  237. {
  238. // read all data needed (until fill buffer)
  239. int idx = 0;
  240. while (idx < buffer.Length)
  241. {
  242. idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  243. }
  244. return buffer.Length;
  245. }
  246. #else
  247. // read all data needed (until fill buffer)
  248. int idx = 0;
  249. while (idx < buffer.Length)
  250. {
  251. idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
  252. }
  253. return buffer.Length;
  254. #endif
  255. }
  256. /// <summary>
  257. /// Close the network channel
  258. /// </summary>
  259. public void Close()
  260. {
  261. #if SSL
  262. if (this.secure)
  263. {
  264. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  265. this.netStream.Close();
  266. #endif
  267. this.sslStream.Close();
  268. }
  269. this.socket.Close();
  270. #else
  271. this.socket.Close();
  272. #endif
  273. }
  274. }
  275. /// <summary>
  276. /// IPAddress Utility class
  277. /// </summary>
  278. public static class IPAddressUtility
  279. {
  280. /// <summary>
  281. /// Return AddressFamily for the IP address
  282. /// </summary>
  283. /// <param name="ipAddress">IP address to check</param>
  284. /// <returns>Address family</returns>
  285. public static AddressFamily GetAddressFamily(this IPAddress ipAddress)
  286. {
  287. #if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
  288. return ipAddress.AddressFamily;
  289. #else
  290. return (ipAddress.ToString().IndexOf(':') != -1) ?
  291. AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
  292. #endif
  293. }
  294. }
  295. }