add Hue EntertainmentAPI + Forwarder & other Fixes (#592)

* whitespaces + typo fixes

* JS / LGTM fixes

* SSDP Handler crash fix

* MessageForwarder handlePriorityChanges Slave fixes

* use aboutToQuit Signal

* complete rewriten Hue Entertainment API structure
combined Philips Hue and Entertainment API
with new MbedTLS based SSL UDP Provider

* add required cross-compile submodules

* logical rebuild fn: initLeds, setLights + new logs
-more detailed checks and error handling inside iniLeds and setLights
- logical script procedure before ProviderUdpSSL init
- first steps for multiple ProviderUdpSSL usage
- better fallback support to old RestAPI, if entertainment api is not supported
- just 4 u LordGrey: new log fn for cosmetic config outputs ;)

* add OSX CompileHowTo - undo from CrossCompileHowTo

* whitespace fixes

* lightID toString fix

* fix unsigned int E-API + debug output

* bugfixes, reworked black signal detection, wizard:
- change device config field light-ids from int to string -> real unsigned int fix
- add signal detection brightness minimum threshold value
0.0 for 0% brightness - 1.0 for 100% brightness to count for blacklight signal detection
reason: input may not 100% black, like mine - i have a deep dark gray input signal
-> my threshold value is set to 0.005 for 0.5% minimum brightness = 1 (from max 255) to count as black
- wizard optimations, with fallback without entertainment support (beta state)
- whitespace fixes

* cleanup + minor fixes

* change fixed Hue UPD SSL config to _devConfig paras

* Hotfix SSL Connection, new light models, wizard:
- Fix UPD SSL Connection failed Problems
- add new supported gamut C light models: LCG002, LCA001, LCA002, LCA003
- wizard: extend fallback support to classic mode + hints

* whitespace, typo fix

* uncheck useEntertainmentAPI, if noAPISupport detected + hint

* coredump fix -> add _blackLightsTimer nullptr init

* code cleanup / remove old debugs + whitespacefixes

* add gamut C LCP001, LCP002

* SSL UDP config made more flexible + remove qDebug
-> switch to hyerion.ng _log
-> replace logCommand with verbose
-> code cleanups etc...

* extended mbedtls debugging infos

* add adjustable ssl timeout settings

* error handling

* streamdebugger bugfixes

* UPDSSL psk / psk_identity bugfixes! + hue wizard fn typo fix +
- verbose option available without dependencies
- whitespace fixes

* Philips Hue Assistant now recognizes non-original bridges better...
+ Added note if no clientkey is set when using the entertainment API
+ User creation (+ clientkey) for non-original bridges can now also be used
+ Minor changes and bug fixes

* CMAKE mbedTLS  detection

* minor bug fixes + code cleanups

* FindMbedTLS.cmake remove Path-Hints + wizard.js: ajax timeout handling
Test - content_grabber.js: run relevant code only, if V4L2_AVAIL is true:
conf_grabber don't displays other devices, if V4L2 is not available

* compile mbedtls via cmake as static lib

* remove libmbedtls-dev from compileHowto / scripts

* Fix Windows build

* Fix windows build (part 2)

* removed unnecessary osx x11 include directory path

* QTimer Shutdown bugfix

* cmake win32 fix + minor bugfixes

* cmake debug msg used mbedtls libs

* Bugfix: noSignalDetection wasn't switchedOn again
if no signal was previously detected

* Some code fixes based on alerts from lgtm.com

Co-authored-by: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com>
This commit is contained in:
SJunkies
2020-05-22 19:40:50 +02:00
committed by GitHub
parent 4aebd55715
commit 259becea04
37 changed files with 3832 additions and 1302 deletions

View File

@@ -428,8 +428,6 @@ QJsonDocument LedDeviceNanoleaf::handleReply(QNetworkReply* const &reply )
return jsonDoc;
}
int LedDeviceNanoleaf::write(const std::vector<ColorRgb> & ledValues)
{

File diff suppressed because it is too large Load Diff

View File

@@ -2,16 +2,38 @@
// STL includes
#include <set>
#include <string>
#include <stdarg.h>
// Qt includes
#include <QNetworkAccessManager>
#include <QTimer>
#include <QEventLoop>
#include <QNetworkReply>
#include <QtCore/qmath.h>
#include <QStringList>
// Leddevice includes
#include <leddevice/LedDevice.h>
#include "ProviderUdpSSL.h"
// Forward declaration
struct CiColorTriangle;
/**
* A XY color point in the color space of the hue system without brightness.
*/
struct XYColor
{
/// X component.
double x;
/// Y component.
double y;
};
/**
* Color triangle to define an available color space for the hue lamps.
*/
struct CiColorTriangle
{
XYColor red, green, blue;
};
/**
* A color point in the color space of the hue system.
@@ -37,14 +59,14 @@ struct CiColor
///
/// @return color point
///
static CiColor rgbToCiColor(double red, double green, double blue, CiColorTriangle colorSpace);
static CiColor rgbToCiColor(double red, double green, double blue, const CiColorTriangle &colorSpace);
///
/// @param p the color point to check
///
/// @return true if the color point is covered by the lamp color space
///
static bool isPointInLampsReach(CiColor p, CiColorTriangle colorSpace);
static bool isPointInLampsReach(CiColor p, const CiColorTriangle &colorSpace);
///
/// @param p1 point one
@@ -53,7 +75,7 @@ struct CiColor
///
/// @return the cross product between p1 and p2
///
static double crossProduct(CiColor p1, CiColor p2);
static double crossProduct(XYColor p1, XYColor p2);
///
/// @param a reference point one
@@ -64,7 +86,7 @@ struct CiColor
///
/// @return the closest color point of p to a and b
///
static CiColor getClosestPointToPoint(CiColor a, CiColor b, CiColor p);
static XYColor getClosestPointToPoint(XYColor a, XYColor b, CiColor p);
///
/// @param p1 point one
@@ -73,20 +95,12 @@ struct CiColor
///
/// @return the distance between the two points
///
static double getDistanceBetweenTwoPoints(CiColor p1, CiColor p2);
static double getDistanceBetweenTwoPoints(CiColor p1, XYColor p2);
};
bool operator==(const CiColor& p1, const CiColor& p2);
bool operator!=(const CiColor& p1, const CiColor& p2);
/**
* Color triangle to define an available color space for the hue lamps.
*/
struct CiColorTriangle
{
CiColor red, green, blue;
};
/**
* Simple class to hold the id, the latest color, the color space and the original state.
*/
@@ -120,13 +134,12 @@ public:
///
/// @param transitionTime the transition time between colors in multiples of 100 ms
///
void setTransitionTime(unsigned int _transitionTime);
void setTransitionTime(unsigned int transitionTime);
///
/// @param color the color to set
///
void setColor(const CiColor& _color);
void setColor(const CiColor& color);
unsigned int getId() const;
@@ -138,9 +151,7 @@ public:
/// @return the color space of the light determined by the model id reported by the bridge.
CiColorTriangle getColorSpace() const;
QString getOriginalState();
QString getOriginalState() const;
private:
@@ -167,7 +178,7 @@ private:
CiColor _originalColor;
};
class LedDevicePhilipsHueBridge : public LedDevice
class LedDevicePhilipsHueBridge : public ProviderUdpSSL
{
Q_OBJECT
@@ -183,24 +194,22 @@ public:
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
///
/// @param route the route of the POST request.
///
/// @param content the content of the POST request.
///
void post(const QString& route, const QString& content);
QJsonDocument post(const QString& route, const QString& content);
void setLightState(unsigned int lightId = 0, QString state = "");
const QMap<quint16,QJsonObject>& getLightMap();
// /// Set device in error state
// ///
// /// @param errorMsg The error message to be logged
// ///
// virtual void setInError( const QString& errorMsg) override;
const QMap<quint16,QJsonObject>& getGroupMap();
QString getGroupName(unsigned int groupId = 0);
QJsonArray getGroupLights(unsigned int groupId = 0);
public slots:
///
@@ -209,11 +218,6 @@ public slots:
virtual int open(void) override;
virtual int open( const QString& hostname, const QString& port, const QString& username );
//signals:
// ///
// /// Emits with a QMap of current bridge light/value pairs
// ///
// void newLights(QMap<quint16,QJsonObject> map);
protected:
/// Ip address of the bridge
@@ -222,6 +226,18 @@ protected:
/// User name for the API ("newdeveloper")
QString _username;
bool _useHueEntertainmentAPI;
QJsonDocument getGroupState( unsigned int groupId );
QJsonDocument setGroupState( unsigned int groupId, bool state);
bool isStreamOwner(const QString streamOwner);
bool initMaps();
void log(const char* msg, const char* type, ...);
const int * getCiphersuites() override;
private:
///
@@ -267,6 +283,11 @@ private:
///
QJsonDocument handleReply(QNetworkReply* const &reply );
QJsonDocument getAllBridgeInfos();
void setBridgeConfig( QJsonDocument doc );
void setLightsMap( QJsonDocument doc );
void setGroupMap( QJsonDocument doc );
/// QNetworkAccessManager for sending requests.
QNetworkAccessManager* _networkmanager;
@@ -282,10 +303,9 @@ private:
bool _isHueEntertainmentReady;
QMap<quint16,QJsonObject> _lightsMap;
QMap<quint16,QJsonObject> _groupsMap;
};
/**
* Implementation for the Philips Hue system.
*
@@ -296,7 +316,6 @@ private:
*/
class LedDevicePhilipsHue: public LedDevicePhilipsHueBridge
{
Q_OBJECT
public:
@@ -337,10 +356,16 @@ public:
unsigned int getLightsCount() const { return _lightsCount; }
void setLightsCount( unsigned int lightsCount);
bool initStream();
bool getStreamGroupState();
bool setStreamGroupState(bool state);
bool startStream();
bool stopStream();
void setOnOffState(PhilipsHueLight& light, bool on);
void setTransitionTime(PhilipsHueLight& light, unsigned int transitionTime);
void setColor(PhilipsHueLight& light, const CiColor& color, double brightnessFactor);
void setState(PhilipsHueLight& light, bool on, const CiColor& color, double brightnessFactor, unsigned int transitionTime);
void setTransitionTime(PhilipsHueLight& light);
void setColor(PhilipsHueLight& light, CiColor& color);
void setState(PhilipsHueLight& light, bool on, const CiColor& color);
void restoreOriginalState();
@@ -357,7 +382,9 @@ private slots:
///
/// @param map Map of lightid/value pairs of bridge
///
void updateLights(QMap<quint16, QJsonObject> map);
bool updateLights(QMap<quint16, QJsonObject> map);
void noSignalTimeout();
protected:
@@ -374,6 +401,7 @@ protected:
/// @return True, if Nanoleaf device capabilities fit configuration
///
bool initLeds();
bool reinitLeds();
///
/// Writes the RGB-Color values to the leds.
@@ -386,8 +414,18 @@ protected:
private:
bool setLights();
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
void writeStream();
bool noSignalDetection();
void stopBlackTimeoutTimer();
QByteArray prepareStreamData();
///
bool _switchOffOnBlack;
/// The brightness factor to multiply on color change.
@@ -397,6 +435,8 @@ private:
unsigned int _transitionTime;
bool _isRestoreOrigState;
bool _lightStatesRestored;
bool _isInitLeds;
/// Array of the light ids.
std::vector<unsigned int> _lightIds;
@@ -404,6 +444,26 @@ private:
std::vector<PhilipsHueLight> _lights;
unsigned int _lightsCount;
unsigned int _groupId;
double _brightnessMin;
double _brightnessMax;
bool _allLightsBlack;
QTimer* _blackLightsTimer;
unsigned int _blackLightsTimeout;
double _brightnessThreshold;
int _handshake_timeout_min;
int _handshake_timeout_max;
int _ssl_read_timeout;
bool _stopConnection;
QString _groupName;
QString _streamOwner;
int start_retry_left;
int stop_retry_left;
};

View File

@@ -77,7 +77,7 @@ bool ProviderUdp::initNetwork()
{
bool isInitOK = false;
_udpSocket =new QUdpSocket(this);
_udpSocket = new QUdpSocket(this);
if ( _udpSocket != nullptr)
{

View File

@@ -0,0 +1,801 @@
// STL includes
#include <cstdio>
#include <exception>
// Linux includes
#include <fcntl.h>
#ifndef _WIN32
#include <sys/ioctl.h>
#endif
// Local Hyperion includes
#include "ProviderUdpSSL.h"
ProviderUdpSSL::ProviderUdpSSL()
: LedDevice()
, client_fd()
, entropy()
, ssl()
, conf()
, ctr_drbg()
, timer()
, _transport_type("DTLS")
, _custom("dtls_client")
, _address("127.0.0.1")
, _defaultHost("127.0.0.1")
, _port(1)
, _ssl_port(1)
, _server_name()
, _psk()
, _psk_identity()
, _read_timeout(0)
, _handshake_timeout_min(400)
, _handshake_timeout_max(1000)
, _handshake_attempts(5)
, _retry_left(MAX_RETRY)
, _stopConnection(true)
, _debugStreamer(false)
, _debugLevel(0)
{
_deviceReady = false;
_latchTime_ms = 1;
}
ProviderUdpSSL::~ProviderUdpSSL()
{
}
bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
{
bool isInitOK = LedDevice::init(deviceConfig);
_debugStreamer = deviceConfig["debugStreamer"].toBool(false);
_debugLevel = deviceConfig["debugLevel"].toString().toInt(0);
//PSK Pre Shared Key
_psk = deviceConfig["psk"].toString();
_psk_identity = deviceConfig["psk_identity"].toString();
_port = deviceConfig["sslport"].toInt(2100);
_server_name = deviceConfig["servername"].toString();
if( deviceConfig.contains("transport_type") ) _transport_type = deviceConfig["transport_type"].toString("DTLS");
if( deviceConfig.contains("seed_custom") ) _custom = deviceConfig["seed_custom"].toString("dtls_client");
if( deviceConfig.contains("retry_left") ) _retry_left = deviceConfig["retry_left"].toInt(MAX_RETRY);
if( deviceConfig.contains("read_timeout") ) _read_timeout = deviceConfig["read_timeout"].toInt(0);
if( deviceConfig.contains("hs_timeout_min") ) _handshake_timeout_min = deviceConfig["hs_timeout_min"].toInt(400);
if( deviceConfig.contains("hs_timeout_max") ) _handshake_timeout_max = deviceConfig["hs_timeout_max"].toInt(1000);
if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
QString host = deviceConfig["host"].toString(_defaultHost);
QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
configLog( "SSL DebugLevel", "[%d] %s", _debugLevel, QSTRING_CSTR( debugLevels[ _debugLevel ]) );
configLog( "SSL Servername", "%s", QSTRING_CSTR( _server_name ) );
configLog( "SSL Host", "%s", QSTRING_CSTR( host ) );
configLog( "SSL Port", "%d", _port );
configLog( "PSK", "%s", QSTRING_CSTR( _psk ) );
configLog( "PSK-Identity", "%s", QSTRING_CSTR( _psk_identity ) );
configLog( "SSL Transport Type", "%s", QSTRING_CSTR( _transport_type ) );
configLog( "SSL Seed Custom", "%s", QSTRING_CSTR( _custom ) );
configLog( "SSL Retry Left", "%d", _retry_left );
configLog( "SSL Read Timeout", "%d", _read_timeout );
configLog( "SSL Handshake Timeout min", "%d", _handshake_timeout_min );
configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
if ( _address.setAddress(host) )
{
Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
}
else
{
Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
QHostInfo info = QHostInfo::fromName(host);
if ( info.addresses().isEmpty() )
{
Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
QString errortext = QString("Invalid target address [%1]!").arg(host);
this->setInError( errortext );
isInitOK = false;
}
else
{
Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
_address = info.addresses().first();
}
}
int config_port = deviceConfig["sslport"].toInt(_port);
if ( config_port <= 0 || config_port > MAX_PORT_SSL )
{
QString errortext = QString ("Invalid target port [%1]!").arg(config_port);
this->setInError( errortext );
isInitOK = false;
}
else
{
_ssl_port = config_port;
Debug( _log, "UDP SSL using %s:%u", QSTRING_CSTR( _address.toString() ), _ssl_port );
}
return isInitOK;
}
int ProviderUdpSSL::open()
{
int retval = -1;
QString errortext;
_deviceReady = false;
if ( init(_devConfig) )
{
if ( !initNetwork() )
{
this->setInError( "UDP SSL Network error!" );
}
else
{
// Everything is OK -> enable device
_deviceReady = true;
setEnable(true);
retval = 0;
}
}
return retval;
}
void ProviderUdpSSL::close()
{
LedDevice::close();
closeSSLConnection();
}
void ProviderUdpSSL::closeSSLConnection()
{
if( _deviceReady && !_stopConnection )
{
closeSSLNotify();
freeSSLConnection();
}
}
const int *ProviderUdpSSL::getCiphersuites()
{
return mbedtls_ssl_list_ciphersuites();
}
void ProviderUdpSSL::configLog(const char* msg, const char* type, ...)
{
if( _debugStreamer )
{
const size_t max_val_length = 1024;
char val[max_val_length];
va_list args;
va_start(args, type);
vsnprintf(val, max_val_length, type, args);
va_end(args);
std::string s = msg;
int max = 30;
s.append(max - s.length(), ' ');
Debug( _log, "%s: %s", s.c_str(), val );
}
}
void ProviderUdpSSL::sslLog(const QString &msg, const char* errorType)
{
sslLog( QSTRING_CSTR( msg ), errorType );
}
void ProviderUdpSSL::sslLog(const char* msg, const char* errorType)
{
if( strcmp("fatal", errorType) == 0 ) Error( _log, "%s", msg );
if( _debugStreamer )
{
if( strcmp("debug", errorType) == 0 ) Debug( _log, "%s", msg );
if( strcmp("warning", errorType) == 0 ) Warning( _log, "%s", msg );
if( strcmp("error", errorType) == 0 ) Error( _log, "%s", msg );
}
}
bool ProviderUdpSSL::initNetwork()
{
sslLog( "init SSL Network..." );
QMutexLocker locker(&_hueMutex);
if (!initConnection()) return false;
sslLog( "init SSL Network...ok" );
_stopConnection = false;
return true;
}
bool ProviderUdpSSL::initConnection()
{
sslLog( "init SSL Network -> initConnection" );
mbedtls_net_init(&client_fd);
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&ctr_drbg);
if(!seedingRNG()) return false;
return setupStructure();
}
bool ProviderUdpSSL::seedingRNG()
{
int ret = 0;
sslLog( "Seeding the random number generator..." );
mbedtls_entropy_init(&entropy);
sslLog( "Set mbedtls_ctr_drbg_seed..." );
const char* custom = QSTRING_CSTR( _custom );
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, reinterpret_cast<const unsigned char *>(custom), strlen(custom))) != 0)
{
sslLog( QString("mbedtls_ctr_drbg_seed FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
sslLog( "Seeding the random number generator...ok" );
return true;
}
bool ProviderUdpSSL::setupStructure()
{
int ret = 0;
sslLog( QString( "Setting up the %1 structure").arg( _transport_type ) );
//TLS MBEDTLS_SSL_TRANSPORT_STREAM
//DTLS MBEDTLS_SSL_TRANSPORT_DATAGRAM
int transport = ( _transport_type == "DTLS" ) ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM;
if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, transport, MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
sslLog( QString("mbedtls_ssl_config_defaults FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
const int * ciphersuites = getCiphersuites();
if( _debugStreamer )
{
int s = ( sizeof( ciphersuites ) ) / sizeof( int );
QString cipher_values;
for(int i=0; i<s; i++)
{
if(i > 0) cipher_values.append(", ");
cipher_values.append(QString::number(ciphersuites[i]));
}
sslLog( ( QString("used ciphersuites value: %1").arg( cipher_values ) ) );
}
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
//mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
//mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
mbedtls_ssl_conf_ciphersuites(&conf, ciphersuites);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
if ( _debugLevel > 0)
{
mbedtls_ssl_conf_verify(&conf, ProviderUdpSSLVerify, NULL);
mbedtls_ssl_conf_dbg(&conf, ProviderUdpSSLDebug, NULL);
mbedtls_debug_set_threshold( _debugLevel );
}
if( _read_timeout > 0 ) mbedtls_ssl_conf_read_timeout(&conf, _read_timeout);
mbedtls_ssl_conf_handshake_timeout(&conf, _handshake_timeout_min, _handshake_timeout_max);
if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0)
{
sslLog( QString("mbedtls_ssl_setup FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
if ((ret = mbedtls_ssl_set_hostname(&ssl, QSTRING_CSTR( _server_name ))) != 0)
{
sslLog( QString("mbedtls_ssl_set_hostname FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
sslLog( QString( "Setting up the %1 structure...ok").arg( _transport_type ) );
return startUPDConnection();
}
bool ProviderUdpSSL::startUPDConnection()
{
sslLog( "init SSL Network -> startUPDConnection" );
int ret = 0;
mbedtls_ssl_session_reset(&ssl);
if(!setupPSK()) return false;
sslLog( QString("Connecting to udp %1:%2").arg( _address.toString() ).arg( _ssl_port ) );
if ((ret = mbedtls_net_connect( &client_fd, _address.toString().toUtf8(), std::to_string(_ssl_port).c_str(), MBEDTLS_NET_PROTO_UDP)) != 0)
{
sslLog( QString("mbedtls_net_connect FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
sslLog( "Connecting...ok" );
return startSSLHandshake();
}
bool ProviderUdpSSL::setupPSK()
{
int ret;
QByteArray pskArray = _psk.toUtf8();
QByteArray pskRawArray = QByteArray::fromHex(pskArray);
QByteArray pskIdArray = _psk_identity.toUtf8();
QByteArray pskIdRawArray = pskIdArray;
if (0 != (ret = mbedtls_ssl_conf_psk( &conf, ( const unsigned char* ) pskRawArray.data(), pskRawArray.length() * sizeof(char), reinterpret_cast<const unsigned char *> ( pskIdRawArray.data() ), pskIdRawArray.length() * sizeof(char) ) ) )
{
sslLog( QString("mbedtls_ssl_conf_psk FAILED %1").arg( errorMsg( ret ) ), "error" );
return false;
}
return true;
}
bool ProviderUdpSSL::startSSLHandshake()
{
sslLog( "init SSL Network -> startSSLHandshake" );
int ret = 0;
sslLog( QString( "Performing the SSL/%1 handshake...").arg( _transport_type ) );
for (unsigned int attempt = 1; attempt <= _handshake_attempts; ++attempt)
{
sslLog( QString("handshake attempt %1/%2").arg( attempt ).arg( _handshake_attempts ) );
do
{
ret = mbedtls_ssl_handshake(&ssl);
}
while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
if (ret == 0)
{
break;
}
else
{
sslLog( QString("mbedtls_ssl_handshake attempt %1/%2 FAILED %3").arg( attempt ).arg( _handshake_attempts ).arg( errorMsg( ret ) ) );
}
QThread::msleep(200);
}
if (ret != 0)
{
sslLog( QString("mbedtls_ssl_handshake FAILED %1").arg( errorMsg( ret ) ), "error" );
handleReturn(ret);
sslLog( "UDP SSL Connection failed!", "fatal" );
return false;
}
else
{
if( ( mbedtls_ssl_get_verify_result( &ssl ) ) != 0 ) {
sslLog( "SSL certificate verification failed!", "fatal" );
return false;
}
}
sslLog( QString( "Performing the SSL/%1 handshake...ok").arg( _transport_type ) );
return true;
}
void ProviderUdpSSL::freeSSLConnection()
{
sslLog( "SSL Connection cleanup..." );
_stopConnection = true;
try
{
mbedtls_ssl_session_reset(&ssl);
mbedtls_net_free(&client_fd);
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
mbedtls_x509_crt_free(&cacert);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
sslLog( "SSL Connection cleanup...ok" );
}
catch (std::exception &e)
{
sslLog( QString("SSL Connection cleanup Error: %s").arg( e.what() ) );
}
catch (...)
{
sslLog( "SSL Connection cleanup Error: <unknown>" );
}
}
void ProviderUdpSSL::writeBytes(const unsigned size, const unsigned char * data)
{
if( _stopConnection ) return;
QMutexLocker locker(&_hueMutex);
int ret = 0;
do
{
ret = mbedtls_ssl_write(&ssl, data, size);
}
while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
if (ret <= 0)
{
handleReturn(ret);
}
}
void ProviderUdpSSL::handleReturn(int ret)
{
bool closeNotify = false;
bool gotoExit = false;
switch (ret)
{
case MBEDTLS_ERR_SSL_TIMEOUT:
sslLog( errorMsg( ret ), "warning" );
if ( _retry_left-- > 0 ) return;
gotoExit = true;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
sslLog( "SSL Connection was closed gracefully", "warning" );
ret = 0;
closeNotify = true;
break;
default:
sslLog( QString("mbedtls_ssl_read returned %1").arg( errorMsg( ret ) ), "warning" );
gotoExit = true;
}
if (closeNotify)
{
closeSSLNotify();
gotoExit = true;
}
if (gotoExit)
{
sslLog( "Exit SSL connection" );
_stopConnection = true;
}
}
QString ProviderUdpSSL::errorMsg(int ret) {
QString msg;
#ifdef MBEDTLS_ERROR_C
char error_buf[1024];
mbedtls_strerror(ret, error_buf, 1024);
msg = QString("Last error was: %1 - %2").arg( ret ).arg( error_buf );
#else
switch (ret)
{
#if defined(MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE)
case MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE:
msg = "The requested feature is not available. - MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE -0x7080";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_INPUT_DATA)
case MBEDTLS_ERR_SSL_BAD_INPUT_DATA:
msg = "Bad input parameters to function. - MBEDTLS_ERR_SSL_BAD_INPUT_DATA -0x7100";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_INVALID_MAC)
case MBEDTLS_ERR_SSL_INVALID_MAC:
msg = "Verification of the message MAC failed. - MBEDTLS_ERR_SSL_INVALID_MAC -0x7180";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_INVALID_RECORD)
case MBEDTLS_ERR_SSL_INVALID_RECORD:
msg = "An invalid SSL record was received. - MBEDTLS_ERR_SSL_INVALID_RECORD -0x7200";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CONN_EOF)
case MBEDTLS_ERR_SSL_CONN_EOF:
msg = "The connection indicated an EOF. - MBEDTLS_ERR_SSL_CONN_EOF -0x7280";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_UNKNOWN_CIPHER)
case MBEDTLS_ERR_SSL_UNKNOWN_CIPHER:
msg = "An unknown cipher was received. - MBEDTLS_ERR_SSL_UNKNOWN_CIPHER -0x7300";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN)
case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN:
msg = "The server has no ciphersuites in common with the client. - MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN -0x7380";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_NO_RNG)
case MBEDTLS_ERR_SSL_NO_RNG:
msg = "No RNG was provided to the SSL module. - MBEDTLS_ERR_SSL_NO_RNG -0x7400";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE)
case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE:
msg = "No client certification received from the client, but required by the authentication mode. - MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE -0x7480";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE)
case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE:
msg = "Our own certificate(s) is/are too large to send in an SSL message. - MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE -0x7500";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED)
case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED:
msg = "The own certificate is not set, but needed by the server. - MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED -0x7580";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED)
case MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED:
msg = "The own private key or pre-shared key is not set, but needed. - MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED -0x7600";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED)
case MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED:
msg = "No CA Chain is set, but required to operate. - MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED -0x7680";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE)
case MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE:
msg = "An unexpected message was received from our peer. - MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE -0x7700";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)
case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
msg = "A fatal alert message was received from our peer. - MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE -0x7780";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED)
case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED:
msg = "Verification of our peer failed. - MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED -0x7800";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
msg = "The peer notified us that the connection is going to be closed. - MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY -0x7880";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO)
case MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO:
msg = "Processing of the ClientHello handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO -0x7900";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO)
case MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO:
msg = "Processing of the ServerHello handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO -0x7980";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE)
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE:
msg = "Processing of the Certificate handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE -0x7A00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST)
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST:
msg = "Processing of the CertificateRequest handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST -0x7A80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE)
case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
msg = "Processing of the ServerKeyExchange handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE -0x7B00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE)
case MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE:
msg = "Processing of the ServerHelloDone handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE -0x7B80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE)
case MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE:
msg = "Processing of the ClientKeyExchange handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE -0x7C00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP)
case MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP:
msg = "Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public. - MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP -0x7C80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS)
case MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS:
msg = "Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret. - MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS -0x7D00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY)
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY:
msg = "Processing of the CertificateVerify handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY -0x7D80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC)
case MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC:
msg = "Processing of the ChangeCipherSpec handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC -0x7E00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_FINISHED)
case MBEDTLS_ERR_SSL_BAD_HS_FINISHED:
msg = "Processing of the Finished handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_FINISHED -0x7E80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_ALLOC_FAILED)
case MBEDTLS_ERR_SSL_ALLOC_FAILED:
msg = "Memory allocation failed. - MBEDTLS_ERR_SSL_ALLOC_FAILED -0x7F00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED)
case MBEDTLS_ERR_SSL_HW_ACCEL_FAILED:
msg = "Hardware acceleration function returned with error. - MBEDTLS_ERR_SSL_HW_ACCEL_FAILED -0x7F80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH)
case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH:
msg = "Hardware acceleration function skipped / left alone data. - MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_COMPRESSION_FAILED)
case MBEDTLS_ERR_SSL_COMPRESSION_FAILED:
msg = "Processing of the compression / decompression failed. - MBEDTLS_ERR_SSL_COMPRESSION_FAILED -0x6F00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION)
case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION:
msg = "Handshake protocol not within min/max boundaries. - MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION -0x6E80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET)
case MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET:
msg = "Processing of the NewSessionTicket handshake message failed. - MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET -0x6E00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED)
case MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED:
msg = "Session ticket has expired. - MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED -0x6D80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH)
case MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH:
msg = "Public key type mismatch (eg, asked for RSA key exchange and presented EC key) - MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH -0x6D00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY)
case MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY:
msg = "Unknown identity received (eg, PSK identity) - MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY -0x6C80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_INTERNAL_ERROR)
case MBEDTLS_ERR_SSL_INTERNAL_ERROR:
msg = "Internal error (eg, unexpected failure in lower-level module) - MBEDTLS_ERR_SSL_INTERNAL_ERROR -0x6C00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_COUNTER_WRAPPING)
case MBEDTLS_ERR_SSL_COUNTER_WRAPPING:
msg = "A counter would wrap (eg, too many messages exchanged). - MBEDTLS_ERR_SSL_COUNTER_WRAPPING -0x6B80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO)
case MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO:
msg = "Unexpected message at ServerHello in renegotiation. - MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO -0x6B00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED)
case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED:
msg = "DTLS client must retry for hello verification. - MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL)
case MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL:
msg = "A buffer is too small to receive or write a message. - MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE)
case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE:
msg = "None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). - MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_WANT_READ)
case MBEDTLS_ERR_SSL_WANT_READ:
msg = "No data of requested type currently available on underlying transport. - MBEDTLS_ERR_SSL_WANT_READ -0x6900";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_WANT_WRITE)
case MBEDTLS_ERR_SSL_WANT_WRITE:
msg = "Connection requires a write call. - MBEDTLS_ERR_SSL_WANT_WRITE -0x6880";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_TIMEOUT)
case MBEDTLS_ERR_SSL_TIMEOUT:
msg = "The operation timed out. - MBEDTLS_ERR_SSL_TIMEOUT -0x6800";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CLIENT_RECONNECT)
case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
msg = "The client initiated a reconnect from the same port. - MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD)
case MBEDTLS_ERR_SSL_UNEXPECTED_RECORD:
msg = "Record header looks valid but is not expected. - MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_NON_FATAL)
case MBEDTLS_ERR_SSL_NON_FATAL:
msg = "The alert message received indicates a non-fatal error. - MBEDTLS_ERR_SSL_NON_FATAL -0x6680";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH)
case MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH:
msg = "Couldn't set the hash for verifying CertificateVerify. - MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING)
case MBEDTLS_ERR_SSL_CONTINUE_PROCESSING:
msg = "Internal-only message signaling that further message-processing should be done. - MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS)
case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
msg = "The asynchronous operation is not completed yet. - MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS -0x6500";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_EARLY_MESSAGE)
case MBEDTLS_ERR_SSL_EARLY_MESSAGE:
msg = "Internal-only message signaling that a message arrived early. - MBEDTLS_ERR_SSL_EARLY_MESSAGE -0x6480";
break;
#endif
#if defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)
case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
msg = "A cryptographic operation is in progress. - MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS -0x7000";
break;
#endif
default:
msg.append("Last error was: ").append( QString::number(ret) );
}
#endif
return msg;
}
void ProviderUdpSSL::closeSSLNotify()
{
int ret = 0;
sslLog( "Closing SSL connection..." );
/* No error checking, the connection might be closed already */
do
{
ret = mbedtls_ssl_close_notify(&ssl);
}
while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
sslLog( "SSL Connection successful closed" );
}

View File

@@ -0,0 +1,210 @@
#pragma once
#include <leddevice/LedDevice.h>
#include <utils/Logger.h>
// Qt includes
#include <QMutex>
#include <QMutexLocker>
#include <QHostInfo>
#include <QThread>
//----------- mbedtls
#if !defined(MBEDTLS_CONFIG_FILE)
#include <mbedtls/config.h>
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include <mbedtls/platform.h>
#else
#include <stdio.h>
#include <stdlib.h>
#define mbedtls_time time
#define mbedtls_time_t time_t
#define mbedtls_printf printf
#define mbedtls_fprintf fprintf
#define mbedtls_snprintf snprintf
#define mbedtls_calloc calloc
#define mbedtls_free free
#define mbedtls_exit exit
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif
#include <string.h>
#include <cstring>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl_ciphersuites.h>
#include <mbedtls/entropy.h>
#include <mbedtls/timing.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/error.h>
#include <mbedtls/debug.h>
#define READ_TIMEOUT_MS 1000
#define MAX_RETRY 5
//----------- END mbedtls
const ushort MAX_PORT_SSL = 65535;
class ProviderUdpSSL : public LedDevice
{
Q_OBJECT
public:
///
/// Constructs specific LedDevice
///
ProviderUdpSSL();
///
/// Destructor of the LedDevice; closes the output device if it is open
///
virtual ~ProviderUdpSSL() override;
///
/// Sets configuration
///
/// @param deviceConfig the json device config
/// @return true if success
virtual bool init(const QJsonObject &deviceConfig) override;
public slots:
///
/// Closes the output device.
/// Includes switching-off the device and stopping refreshes
///
virtual void close() override;
protected:
///
/// Initialise device's network details
///
/// @return True if success
///
bool initNetwork();
///
/// Opens and configures the output device
///
/// @return Zero on succes else negative
///
int open() override;
///
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
/// values are latched.
///
/// @param[in] size The length of the data
/// @param[in] data The data
///
void writeBytes(const unsigned size, const uint8_t *data);
///
/// get ciphersuites list from mbedtls_ssl_list_ciphersuites
///
/// @return const int * array
///
virtual const int * getCiphersuites();
void sslLog(const QString &msg, const char* errorType = "debug");
void sslLog(const char* msg, const char* errorType = "debug");
void configLog(const char* msg, const char* type, ...);
/**
* Debug callback for mbed TLS
* Just prints on the USB serial port
*/
static void ProviderUdpSSLDebug(void *ctx, int level, const char *file, int line, const char *str)
{
const char *p, *basename;
(void) ctx;
/* Extract basename from file */
for(p = basename = file; *p != '\0'; p++)
{
if(*p == '/' || *p == '\\')
{
basename = p + 1;
}
}
mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
}
/**
* Certificate verification callback for mbed TLS
* Here we only use it to display information on each cert in the chain
*/
static int ProviderUdpSSLVerify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
const uint32_t buf_size = 1024;
char *buf = new char[buf_size];
(void) data;
mbedtls_printf("\nVerifying certificate at depth %d:\n", depth);
mbedtls_x509_crt_info(buf, buf_size - 1, " ", crt);
mbedtls_printf("%s", buf);
if (*flags == 0)
mbedtls_printf("No verification issue for this certificate\n");
else
{
mbedtls_x509_crt_verify_info(buf, buf_size, " ! ", *flags);
mbedtls_printf("%s\n", buf);
}
delete[] buf;
return 0;
}
///
/// closeSSLNotify and freeSSLConnection
///
void closeSSLConnection();
private:
bool buildConnection();
bool initConnection();
bool seedingRNG();
bool setupStructure();
bool startUPDConnection();
bool setupPSK();
bool startSSLHandshake();
void handleReturn(int ret);
QString errorMsg(int ret);
void closeSSLNotify();
void freeSSLConnection();
mbedtls_net_context client_fd;
mbedtls_entropy_context entropy;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_timing_delay_context timer;
QMutex _hueMutex;
QString _transport_type;
QString _custom;
QHostAddress _address;
QString _defaultHost;
int _port;
int _ssl_port;
QString _server_name;
QString _psk;
QString _psk_identity;
uint32_t _read_timeout;
uint32_t _handshake_timeout_min;
uint32_t _handshake_timeout_max;
unsigned int _handshake_attempts;
int _retry_left;
bool _stopConnection;
bool _debugStreamer;
int _debugLevel;
};