/*
M2Mqtt - MQTT Client Library for .Net
Copyright (c) 2014, Paolo Patierno, All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
*/
#if SSL
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
using Microsoft.SPOT.Net.Security;
#else
using System.Net.Security;
using System.Security.Authentication;
#endif
#endif
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography.X509Certificates;
namespace uPLibrary.Networking.M2Mqtt
{
///
/// Channel to communicate over the network
///
public class MqttNetworkChannel : IMqttNetworkChannel
{
// remote host information
private string remoteHostName;
private IPAddress remoteIpAddress;
private int remotePort;
// socket for communication
private Socket socket;
// using SSL
private bool secure;
// CA certificate
private X509Certificate caCert;
///
/// Remote host name
///
public string RemoteHostName { get { return this.remoteHostName; } }
///
/// Remote IP address
///
public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } }
///
/// Remote port
///
public int RemotePort { get { return this.remotePort; } }
#if SSL
// SSL stream
private SslStream sslStream;
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
private NetworkStream netStream;
#endif
#endif
///
/// Data available on the channel
///
public bool DataAvailable
{
get
{
#if SSL
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
if (secure)
return this.sslStream.DataAvailable;
else
return (this.socket.Available > 0);
#else
if (secure)
return this.netStream.DataAvailable;
else
return (this.socket.Available > 0);
#endif
#else
return (this.socket.Available > 0);
#endif
}
}
///
/// Constructor
///
/// Socket opened with the client
public MqttNetworkChannel(Socket socket)
{
this.socket = socket;
}
///
/// Constructor
///
/// Remote Host name
/// Remote IP address
/// Remote port
public MqttNetworkChannel(string remoteHostName, IPAddress remoteIpAddress, int remotePort) :
this(remoteHostName, remoteIpAddress, remotePort, false, null)
{
}
///
/// Constructor
///
/// Remote Host name
/// Remote IP address
/// Remote port
/// Using SSL
/// CA certificate
public MqttNetworkChannel(string remoteHostName, IPAddress remoteIpAddress, int remotePort, bool secure, X509Certificate caCert)
{
this.remoteHostName = remoteHostName;
this.remoteIpAddress = remoteIpAddress;
this.remotePort = remotePort;
this.secure = secure;
this.caCert = caCert;
}
///
/// Connect to remote server
///
public void Connect()
{
this.socket = new Socket(IPAddressUtility.GetAddressFamily(this.remoteIpAddress), SocketType.Stream, ProtocolType.Tcp);
// try connection to the broker
this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort));
#if SSL
// secure channel requested
if (secure)
{
// create SSL stream
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
this.sslStream = new SslStream(this.socket);
#else
this.netStream = new NetworkStream(this.socket);
this.sslStream = new SslStream(this.netStream);
#endif
// server authentication (SSL/TLS handshake)
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
this.sslStream.AuthenticateAsClient(this.remoteHostName,
null,
new X509Certificate[] { this.caCert },
SslVerification.CertificateRequired,
SslProtocols.TLSv1);
#else
this.sslStream.AuthenticateAsClient(this.remoteHostName,
new X509CertificateCollection(new X509Certificate[] { this.caCert }),
SslProtocols.Tls, false);
#endif
}
#endif
}
///
/// Send data on the network channel
///
/// Data buffer to send
/// Number of byte sent
public int Send(byte[] buffer)
{
#if SSL
if (this.secure)
{
this.sslStream.Write(buffer, 0, buffer.Length);
return buffer.Length;
}
else
return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
#else
return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
#endif
}
///
/// Receive data from the network
///
/// Data buffer for receiving data
/// Number of bytes received
public int Receive(byte[] buffer)
{
#if SSL
if (this.secure)
{
// read all data needed (until fill buffer)
int idx = 0;
while (idx < buffer.Length)
{
idx += this.sslStream.Read(buffer, idx, buffer.Length - idx);
}
return buffer.Length;
}
else
{
// read all data needed (until fill buffer)
int idx = 0;
while (idx < buffer.Length)
{
idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
}
return buffer.Length;
}
#else
// read all data needed (until fill buffer)
int idx = 0;
while (idx < buffer.Length)
{
idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
}
return buffer.Length;
#endif
}
///
/// Close the network channel
///
public void Close()
{
#if SSL
if (this.secure)
{
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
this.netStream.Close();
#endif
this.sslStream.Close();
}
this.socket.Close();
#else
this.socket.Close();
#endif
}
}
///
/// IPAddress Utility class
///
public static class IPAddressUtility
{
///
/// Return AddressFamily for the IP address
///
/// IP address to check
/// Address family
public static AddressFamily GetAddressFamily(IPAddress ipAddress)
{
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
return ipAddress.AddressFamily;
#else
return (ipAddress.ToString().IndexOf(':') != -1) ?
AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
#endif
}
}
}