/*
Copyright (c) 2013, 2014 Paolo Patierno
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
using System.Threading;
namespace uPLibrary.Networking.M2Mqtt
{
public class MqttNetworkChannel : IMqttNetworkChannel
{
// stream socket for communication
private StreamSocket socket;
// remote host information
private HostName remoteHostName;
private int remotePort;
// using SSL
private bool secure;
// SSL/TLS protocol version
private MqttSslProtocols sslProtocol;
///
/// Constructor
///
/// Socket opened with the client
public MqttNetworkChannel(StreamSocket socket)
{
this.socket = socket;
this.sslProtocol = MqttSslProtocols.None;
}
///
/// Constructor
///
/// Remote Host name
/// Remote port
/// Using SSL
/// SSL/TLS protocol version
public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, MqttSslProtocols sslProtocol)
{
this.remoteHostName = new HostName(remoteHostName);
this.remotePort = remotePort;
this.secure = secure;
this.sslProtocol = sslProtocol;
if (secure && (sslProtocol == MqttSslProtocols.None))
throw new ArgumentException("For secure connection, an SSL/TLS protocol version is needed");
}
public bool DataAvailable
{
get { return true; }
}
public int Receive(byte[] buffer)
{
IBuffer result;
// read all data needed (until fill buffer)
int idx = 0;
while (idx < buffer.Length)
{
// fixed scenario with socket closed gracefully by peer/broker and
// Read return 0. Avoid infinite loop.
// read is executed synchronously
result = this.socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask().Result;
if (result.Length == 0)
return 0;
idx += (int)result.Length;
}
return buffer.Length;
}
public int Receive(byte[] buffer, int timeout)
{
CancellationTokenSource cts = new CancellationTokenSource(timeout);
try
{
IBuffer result;
// read all data needed (until fill buffer)
int idx = 0;
while (idx < buffer.Length)
{
// fixed scenario with socket closed gracefully by peer/broker and
// Read return 0. Avoid infinite loop.
// read is executed synchronously
result = this.socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None).AsTask(cts.Token).Result;
if (result.Length == 0)
return 0;
idx += (int)result.Length;
}
return buffer.Length;
}
catch (TaskCanceledException)
{
return 0;
}
}
public int Send(byte[] buffer)
{
// send is executed synchronously
return (int)this.socket.OutputStream.WriteAsync(buffer.AsBuffer()).AsTask().Result;
}
public void Close()
{
this.socket.Dispose();
}
public void Connect()
{
this.socket = new StreamSocket();
// connection is executed synchronously
this.socket.ConnectAsync(this.remoteHostName,
this.remotePort.ToString(),
MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)).AsTask().Wait();
}
public void Accept()
{
// TODO : SSL support with StreamSocket / StreamSocketListener seems to be NOT supported
return;
}
}
///
/// MQTT SSL utility class
///
public static class MqttSslUtility
{
public static SocketProtectionLevel ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
{
switch (mqttSslProtocol)
{
case MqttSslProtocols.None:
return SocketProtectionLevel.PlainSocket;
case MqttSslProtocols.SSLv3:
return SocketProtectionLevel.SslAllowNullEncryption;
case MqttSslProtocols.TLSv1_0:
return SocketProtectionLevel.Tls10;
case MqttSslProtocols.TLSv1_1:
return SocketProtectionLevel.Tls11;
case MqttSslProtocols.TLSv1_2:
return SocketProtectionLevel.Tls12;
default:
throw new ArgumentException("SSL/TLS protocol version not supported");
}
}
}
}