added cloud connection
This commit is contained in:
parent
211cb3f637
commit
275e3b3921
3
debian/control
vendored
3
debian/control
vendored
@ -23,7 +23,8 @@ Build-Depends: debhelper (>= 9.0.0),
|
||||
libavahi-client-dev,
|
||||
libavahi-common-dev,
|
||||
libssl-dev,
|
||||
|
||||
libmbedtls-dev,
|
||||
libaws-iot-device-sdk-cpp,
|
||||
|
||||
Package: guh
|
||||
Architecture: any
|
||||
|
||||
348
libguh-core/MbedTLS/MbedTLSConnection.cpp
Normal file
348
libguh-core/MbedTLS/MbedTLSConnection.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MbedTLSConnection.hpp
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <util/memory/stl/Vector.hpp>
|
||||
|
||||
#include "MbedTLSConnection.hpp"
|
||||
#include "util/logging/LogMacros.hpp"
|
||||
|
||||
#define MBEDTLS_WRAPPER_LOG_TAG "[MbedTLS Wrapper]"
|
||||
#define MAX_CHARS_IN_PORT_NUMBER 6
|
||||
|
||||
namespace awsiotsdk {
|
||||
namespace network {
|
||||
MbedTLSConnection::MbedTLSConnection(util::String endpoint,
|
||||
uint16_t endpoint_port,
|
||||
util::String root_ca_location,
|
||||
util::String device_cert_location,
|
||||
util::String device_private_key_location,
|
||||
std::chrono::milliseconds tls_handshake_timeout,
|
||||
std::chrono::milliseconds tls_read_timeout,
|
||||
std::chrono::milliseconds tls_write_timeout,
|
||||
bool server_verification_flag) {
|
||||
endpoint_ = endpoint;
|
||||
endpoint_port_ = endpoint_port;
|
||||
root_ca_location_ = root_ca_location;
|
||||
device_cert_location_ = device_cert_location;
|
||||
device_private_key_location_ = device_private_key_location;
|
||||
server_verification_flag_ = server_verification_flag;
|
||||
tls_handshake_timeout_ = tls_handshake_timeout;
|
||||
tls_read_timeout_ = tls_read_timeout;
|
||||
tls_write_timeout_ = tls_write_timeout;
|
||||
flags_ = 0;
|
||||
|
||||
is_connected_ = false;
|
||||
requires_free_ = false;
|
||||
}
|
||||
|
||||
bool MbedTLSConnection::IsPhysicalLayerConnected() {
|
||||
// Use this to add implementation which can check for physical layer disconnect
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MbedTLSConnection::IsConnected() {
|
||||
return is_connected_;
|
||||
}
|
||||
|
||||
int MbedTLSConnection::VerifyCertificate(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
|
||||
char buf[1024];
|
||||
((void) data);
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "Verify requested for (Depth %d):", depth);
|
||||
mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "%s", buf);
|
||||
|
||||
if ((*flags) == 0) {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "This certificate has no flags");
|
||||
} else {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, buf, sizeof(buf), " ! ", *flags);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "%s\n", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResponseCode MbedTLSConnection::ConnectInternal() {
|
||||
ResponseCode rc = ResponseCode::SUCCESS;
|
||||
|
||||
int ret = 0;
|
||||
const util::String pers = "aws_iot_tls_wrapper";
|
||||
char port_buf[6];
|
||||
char vrfy_buf[512];
|
||||
|
||||
mbedtls_net_init(&server_fd_);
|
||||
mbedtls_ssl_init(&ssl_);
|
||||
mbedtls_ssl_config_init(&conf_);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg_);
|
||||
mbedtls_x509_crt_init(&cacert_);
|
||||
mbedtls_x509_crt_init(&clicert_);
|
||||
mbedtls_pk_init(&pkey_);
|
||||
|
||||
requires_free_ = true;
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "...............................%d", MBEDTLS_SSL_MAX_CONTENT_LEN);
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Seeding the random number generator...");
|
||||
mbedtls_entropy_init(&entropy_);
|
||||
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_,
|
||||
(const unsigned char *) (pers.c_str()), pers.length())) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, " Connect Failed!!! mbedtls_ctr_drbg_seed returned -0x%x", -ret);
|
||||
return ResponseCode::NETWORK_SSL_INIT_ERROR;
|
||||
}
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Loading the CA root certificate... %s", root_ca_location_.c_str());
|
||||
ret = mbedtls_x509_crt_parse_file(&cacert_, root_ca_location_.c_str());
|
||||
if (ret < 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG,
|
||||
"Failed!!! mbedtls_x509_crt_parse returned -0x%x while parsing root cert\n\n",
|
||||
-ret);
|
||||
return ResponseCode::NETWORK_SSL_ROOT_CRT_PARSE_ERROR;
|
||||
}
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "ok (%d skipped)\n", ret);
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Loading the client cert. and key...");
|
||||
ret = mbedtls_x509_crt_parse_file(&clicert_, device_cert_location_.c_str());
|
||||
if (ret != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG,
|
||||
"Failed!!! mbedtls_x509_crt_parse returned -0x%x while parsing device cert\n\n",
|
||||
-ret);
|
||||
return ResponseCode::NETWORK_SSL_DEVICE_CRT_PARSE_ERROR;
|
||||
}
|
||||
|
||||
ret = mbedtls_pk_parse_keyfile(&pkey_, device_private_key_location_.c_str(), "");
|
||||
if (ret != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG,
|
||||
"Failed!!! mbedtls_pk_parse_key returned -0x%x while parsing private key\n\n",
|
||||
-ret);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " path : %s ", device_private_key_location_.c_str());
|
||||
return ResponseCode::NETWORK_SSL_KEY_PARSE_ERROR;
|
||||
}
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " ok\n");
|
||||
snprintf(port_buf, MAX_CHARS_IN_PORT_NUMBER, "%d", endpoint_port_);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Connecting to %s/%s...", endpoint_.c_str(), port_buf);
|
||||
if ((ret = mbedtls_net_connect(&server_fd_, endpoint_.c_str(), port_buf, MBEDTLS_NET_PROTO_TCP)) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_net_connect returned -0x%x\n\n", -ret);
|
||||
switch (ret) {
|
||||
case MBEDTLS_ERR_NET_SOCKET_FAILED:
|
||||
return ResponseCode::NETWORK_TCP_SETUP_ERROR;
|
||||
case MBEDTLS_ERR_NET_UNKNOWN_HOST:
|
||||
return ResponseCode::NETWORK_TCP_UNKNOWN_HOST;
|
||||
case MBEDTLS_ERR_NET_CONNECT_FAILED:
|
||||
default:
|
||||
return ResponseCode::NETWORK_TCP_CONNECT_ERROR;
|
||||
};
|
||||
}
|
||||
|
||||
ret = mbedtls_net_set_block(&server_fd_);
|
||||
if (ret != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! net_set_(non)block() returned -0x%x\n\n", -ret);
|
||||
return ResponseCode::NETWORK_SSL_UNKNOWN_ERROR;
|
||||
}
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "Ok!");
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Setting up the SSL/TLS structure...");
|
||||
if ((ret = mbedtls_ssl_config_defaults(&conf_, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
|
||||
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG,
|
||||
"Failed!!! mbedtls_ssl_config_defaults returned -0x%x\n\n",
|
||||
-ret);
|
||||
return ResponseCode::NETWORK_SSL_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_verify(&conf_, &MbedTLSConnection::VerifyCertificate, NULL);
|
||||
if (server_verification_flag_) {
|
||||
mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
} else {
|
||||
mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_OPTIONAL);
|
||||
}
|
||||
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
|
||||
|
||||
mbedtls_ssl_conf_ca_chain(&conf_, &cacert_, NULL);
|
||||
if ((ret = mbedtls_ssl_conf_own_cert(&conf_, &clicert_, &pkey_)) !=
|
||||
0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_ssl_conf_own_cert returned %d\n\n", ret);
|
||||
return ResponseCode::NETWORK_SSL_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_read_timeout(&conf_, static_cast<uint32_t>(tls_handshake_timeout_.count()));
|
||||
if ((ret = mbedtls_ssl_set_hostname(&ssl_, endpoint_.c_str())) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_ssl_set_hostname returned %d\n\n", ret);
|
||||
return ResponseCode::NETWORK_SSL_UNKNOWN_ERROR;
|
||||
}
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "\n\nSSL state connect : %d ", ssl_.state);
|
||||
mbedtls_ssl_set_bio(&ssl_, &server_fd_, mbedtls_net_send, NULL, mbedtls_net_recv_timeout);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "Ok!");
|
||||
|
||||
if ((ret = mbedtls_ssl_setup(&ssl_, &conf_)) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_ssl_setup returned -0x%x\n\n", -ret);
|
||||
return ResponseCode::NETWORK_SSL_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "\n\nSSL state connect : %d ", ssl_.state);
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Performing the SSL/TLS handshake...");
|
||||
while ((ret = mbedtls_ssl_handshake(&ssl_)) != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_ssl_handshake returned -0x%x\n", -ret);
|
||||
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, " Unable to verify the server's certificate. "
|
||||
"Either it is invalid,\n"
|
||||
" or you didn't set ca_file or ca_path "
|
||||
"to an appropriate value.\n"
|
||||
" Alternatively, you may want to use "
|
||||
"auth_mode=optional for testing purposes.\n");
|
||||
}
|
||||
return ResponseCode::NETWORK_SSL_TLS_HANDSHAKE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG,
|
||||
" ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n",
|
||||
mbedtls_ssl_get_version(&ssl_),
|
||||
mbedtls_ssl_get_ciphersuite(&ssl_));
|
||||
if ((ret = mbedtls_ssl_get_record_expansion(&ssl_)) >= 0) {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " [ Record expansion is %d ]\n", ret);
|
||||
} else {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " [ Record expansion is unknown (compression) ]\n");
|
||||
}
|
||||
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, "....Verifying peer X.509 certificate...");
|
||||
|
||||
if (server_verification_flag_) {
|
||||
if ((flags_ = mbedtls_ssl_get_verify_result(&ssl_)) != 0) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, " failed\n");
|
||||
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags_);
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "%s\n", vrfy_buf);
|
||||
rc = ResponseCode::NETWORK_SSL_SERVER_VERIFICATION_ERROR;
|
||||
} else {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " ok\n");
|
||||
rc = ResponseCode::SUCCESS;
|
||||
}
|
||||
} else {
|
||||
AWS_LOG_INFO(MBEDTLS_WRAPPER_LOG_TAG, " Server Verification skipped\n");
|
||||
rc = ResponseCode::SUCCESS;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_read_timeout(&conf_, static_cast<uint32_t>(tls_read_timeout_.count()));
|
||||
is_connected_ = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
ResponseCode MbedTLSConnection::WriteInternal(const util::String &buf, size_t &size_written_bytes_out) {
|
||||
size_t total_written_length = 0;
|
||||
ResponseCode rc = ResponseCode::SUCCESS;
|
||||
size_t bytes_to_write = buf.length();
|
||||
const unsigned char *buf_cstr = (const unsigned char *) (buf.c_str());
|
||||
bool isErrorFlag = false;
|
||||
int ret;
|
||||
|
||||
auto timeout = std::chrono::system_clock::now() + tls_write_timeout_;
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
do {
|
||||
ret = mbedtls_ssl_write(&ssl_, buf_cstr + total_written_length, bytes_to_write - total_written_length);
|
||||
if (ret > 0) {
|
||||
total_written_length += ret;
|
||||
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
AWS_LOG_ERROR(MBEDTLS_WRAPPER_LOG_TAG, "Failed!!! mbedtls_ssl_write returned -0x%x\n\n", -ret);
|
||||
/* All other negative return values indicate connection needs to be reset.
|
||||
* Will be caught in ping request so ignored here */
|
||||
isErrorFlag = true;
|
||||
break;
|
||||
}
|
||||
now = std::chrono::system_clock::now();
|
||||
} while (now < timeout && total_written_length < bytes_to_write);
|
||||
|
||||
size_written_bytes_out = total_written_length;
|
||||
|
||||
if (isErrorFlag) {
|
||||
rc = ResponseCode::NETWORK_SSL_WRITE_ERROR;
|
||||
} else if (now < timeout && total_written_length != bytes_to_write) {
|
||||
return ResponseCode::NETWORK_SSL_WRITE_TIMEOUT_ERROR;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
ResponseCode MbedTLSConnection::ReadInternal(util::Vector<unsigned char> &buf, size_t buf_read_offset,
|
||||
size_t size_bytes_to_read, size_t &size_read_bytes_out) {
|
||||
int ret;
|
||||
size_t total_read_length = 0;
|
||||
size_t remaining_bytes_to_read = size_bytes_to_read;
|
||||
auto timeout = std::chrono::system_clock::now() + tls_read_timeout_;
|
||||
do {
|
||||
// This read will timeout after IOT_SSL_READ_TIMEOUT if there's no data to be read
|
||||
ret = mbedtls_ssl_read(&ssl_, &buf[buf_read_offset], remaining_bytes_to_read);
|
||||
if (ret > 0) {
|
||||
buf_read_offset += ret;
|
||||
total_read_length += ret;
|
||||
remaining_bytes_to_read -= ret;
|
||||
size_read_bytes_out = total_read_length;
|
||||
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE
|
||||
&& ret != MBEDTLS_ERR_SSL_TIMEOUT) {
|
||||
return ResponseCode::NETWORK_SSL_READ_ERROR;
|
||||
}
|
||||
} while (remaining_bytes_to_read > 0 && timeout < std::chrono::system_clock::now());
|
||||
|
||||
if (0 == total_read_length) {
|
||||
return ResponseCode::NETWORK_SSL_NOTHING_TO_READ;
|
||||
} else if (size_read_bytes_out == size_bytes_to_read) {
|
||||
return ResponseCode::SUCCESS;
|
||||
} else if (0 < total_read_length) {
|
||||
return ResponseCode::NETWORK_SSL_READ_TIMEOUT_ERROR;
|
||||
}
|
||||
|
||||
return ResponseCode::NETWORK_SSL_READ_ERROR;
|
||||
}
|
||||
|
||||
ResponseCode MbedTLSConnection::DisconnectInternal() {
|
||||
if (is_connected_) {
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = mbedtls_ssl_close_notify(&ssl_);
|
||||
} while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
}
|
||||
|
||||
if(requires_free_) {
|
||||
mbedtls_net_free(&server_fd_);
|
||||
|
||||
mbedtls_x509_crt_free(&clicert_);
|
||||
mbedtls_x509_crt_free(&cacert_);
|
||||
mbedtls_pk_free(&pkey_);
|
||||
mbedtls_ssl_free(&ssl_);
|
||||
mbedtls_ssl_config_free(&conf_);
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg_);
|
||||
mbedtls_entropy_free(&entropy_);
|
||||
requires_free_ = false;
|
||||
}
|
||||
|
||||
is_connected_ = false;
|
||||
|
||||
/* All other negative return values indicate connection needs to be reset.
|
||||
* No further action required since this is disconnect call */
|
||||
return ResponseCode::SUCCESS;
|
||||
}
|
||||
|
||||
MbedTLSConnection::~MbedTLSConnection() {
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
179
libguh-core/MbedTLS/MbedTLSConnection.hpp
Normal file
179
libguh-core/MbedTLS/MbedTLSConnection.hpp
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://aws.amazon.com/apache2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MbedTLSConnection.hpp
|
||||
* @brief Defines a reference implementation for an MbedTLS library wrapper *
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "mbedtls/config.h"
|
||||
|
||||
#include "mbedtls/platform.h"
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/certs.h"
|
||||
#include "mbedtls/x509.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/debug.h"
|
||||
#include "mbedtls/timing.h"
|
||||
|
||||
#include "NetworkConnection.hpp"
|
||||
#include "ResponseCode.hpp"
|
||||
|
||||
namespace awsiotsdk {
|
||||
namespace network {
|
||||
/**
|
||||
* @brief MbedTLS Wrapper Class
|
||||
*
|
||||
* Defines a reference wrapper for MbedTLS libraries
|
||||
*/
|
||||
class MbedTLSConnection : public NetworkConnection {
|
||||
protected:
|
||||
util::String root_ca_location_; ///< Pointer to string containing the filename (including path) of the root CA file.
|
||||
util::String device_cert_location_; ///< Pointer to string containing the filename (including path) of the device certificate.
|
||||
util::String device_private_key_location_; ///< Pointer to string containing the filename (including path) of the device private key file.
|
||||
std::atomic_bool server_verification_flag_; ///< Boolean. True = perform server certificate hostname validation. False = skip validation \b NOT recommended.
|
||||
std::atomic_bool is_connected_; ///< Boolean indicating connection status
|
||||
std::chrono::milliseconds tls_handshake_timeout_; ///< Timeout for TLS handshake command
|
||||
std::chrono::milliseconds tls_read_timeout_; ///< Timeout for the TLS Read command
|
||||
std::chrono::milliseconds tls_write_timeout_; ///< Timeout for the TLS Write command
|
||||
|
||||
// Endpoint information
|
||||
uint16_t endpoint_port_; ///< Endpoint port
|
||||
util::String endpoint_; ///< Endpoint for this connection
|
||||
|
||||
mbedtls_entropy_context entropy_;
|
||||
mbedtls_ctr_drbg_context ctr_drbg_;
|
||||
mbedtls_ssl_context ssl_;
|
||||
mbedtls_ssl_config conf_;
|
||||
uint32_t flags_;
|
||||
mbedtls_x509_crt cacert_;
|
||||
mbedtls_x509_crt clicert_;
|
||||
mbedtls_pk_context pkey_;
|
||||
mbedtls_net_context server_fd_;
|
||||
|
||||
// TODO: This is a Hotfix, requires a better approach
|
||||
std::atomic_bool requires_free_; ///< Boolean indicating whether the mbedtls struct variables have been allocated or not
|
||||
|
||||
/**
|
||||
* @brief Create a TLS socket and open the connection
|
||||
*
|
||||
* Creates an open socket connection including TLS handshake.
|
||||
*
|
||||
* @return ResponseCode - successful connection or TLS error
|
||||
*/
|
||||
ResponseCode ConnectInternal();
|
||||
|
||||
/**
|
||||
* @brief Write bytes to the network socket
|
||||
*
|
||||
* @param util::String - const reference to buffer which should be written to socket
|
||||
* @return size_t - number of bytes written or Network error
|
||||
* @return ResponseCode - successful write or Network error code
|
||||
*/
|
||||
ResponseCode WriteInternal(const util::String &buf, size_t &size_written_bytes_out);
|
||||
|
||||
/**
|
||||
* @brief Read bytes from the network socket
|
||||
*
|
||||
* @param util::String - reference to buffer where read bytes should be copied
|
||||
* @param size_t - number of bytes to read
|
||||
* @param size_t - reference to store number of bytes read
|
||||
* @return ResponseCode - successful read or TLS error code
|
||||
*/
|
||||
ResponseCode ReadInternal(util::Vector<unsigned char> &buf, size_t buf_read_offset,
|
||||
size_t size_bytes_to_read, size_t &size_read_bytes_out);
|
||||
|
||||
/**
|
||||
* @brief Disconnect from network socket
|
||||
*
|
||||
* @return ResponseCode - successful read or TLS error code
|
||||
*/
|
||||
ResponseCode DisconnectInternal();
|
||||
|
||||
public:
|
||||
static int VerifyCertificate(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
|
||||
|
||||
/**
|
||||
* @brief Constructor for the MbedTLS TLS implementation
|
||||
*
|
||||
* Performs any initialization required by the TLS layer.
|
||||
*
|
||||
* @param util::String endpoint - The target endpoint to connect to
|
||||
* @param uint16_t endpoint_port - The port on the target to connect to
|
||||
* @param util::String root_ca_location - Path of the location of the Root CA
|
||||
* @param util::String device_cert_location - Path to the location of the Device Cert
|
||||
* @param util::String device_private_key_location - Path to the location of the device private key file
|
||||
* @param std::chrono::milliseconds tls_handshake_timeout - The value to use for timeout of handshake operation
|
||||
* @param std::chrono::milliseconds tls_read_timeout - The value to use for timeout of read operation
|
||||
* @param std::chrono::milliseconds tls_write_timeout - The value to use for timeout of write operation
|
||||
* @param bool server_verification_flag - used to decide whether server verification is needed or not
|
||||
*
|
||||
*/
|
||||
MbedTLSConnection(util::String endpoint, uint16_t endpoint_port, util::String root_ca_location,
|
||||
util::String device_cert_location, util::String device_private_key_location,
|
||||
std::chrono::milliseconds tls_handshake_timeout,
|
||||
std::chrono::milliseconds tls_read_timeout, std::chrono::milliseconds tls_write_timeout,
|
||||
bool server_verification_flag);
|
||||
|
||||
/**
|
||||
* @brief Check if TLS layer is still connected
|
||||
*
|
||||
* Called to check if the TLS layer is still connected or not.
|
||||
*
|
||||
* @return bool - indicating status of network TLS layer connection
|
||||
*/
|
||||
bool IsConnected();
|
||||
|
||||
/**
|
||||
* @brief Check if Network Physical layer is still connected
|
||||
*
|
||||
* Called to check if the Network Physical layer is still connected or not.
|
||||
*
|
||||
* @return bool - indicating status of network physical layer connection
|
||||
*/
|
||||
bool IsPhysicalLayerConnected();
|
||||
|
||||
/**
|
||||
* @brief sets the path to the root CA
|
||||
*
|
||||
* Called to change the location of the root CA after the constructor has initialized the OpenSSL object.
|
||||
*
|
||||
* @param root_ca_location
|
||||
*/
|
||||
void SetRootCAPath(util::String root_ca_location) { root_ca_location_ = root_ca_location; }
|
||||
|
||||
/**
|
||||
* @brief sets the endpoint and the port
|
||||
*
|
||||
* Called to change the endpoint and the port after the constructor has initialized the OpenSSL object.
|
||||
* @param endpoint
|
||||
* @param endpoint_port
|
||||
*/
|
||||
void SetEndpointAndPort(util::String endpoint, uint16_t endpoint_port) {
|
||||
endpoint_ = endpoint;
|
||||
endpoint_port_ = endpoint_port;
|
||||
}
|
||||
|
||||
virtual ~MbedTLSConnection();
|
||||
};
|
||||
}
|
||||
}
|
||||
6
libguh-core/MbedTLS/README.txt
Normal file
6
libguh-core/MbedTLS/README.txt
Normal file
@ -0,0 +1,6 @@
|
||||
The files in this folder have been taken from
|
||||
|
||||
https://github.com/aws/aws-iot-device-sdk-cpp
|
||||
|
||||
under the term of the Apache License, Version 2.0
|
||||
|
||||
166
libguh-core/awsconnector.cpp
Normal file
166
libguh-core/awsconnector.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include "awsconnector.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QJsonDocument>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
using namespace awsiotsdk;
|
||||
using namespace awsiotsdk::network;
|
||||
using namespace awsiotsdk::mqtt;
|
||||
|
||||
QHash<quint16, AWSConnector*> AWSConnector::s_requestMap;
|
||||
|
||||
AWSConnector::AWSConnector(QObject *parent) : QObject(parent)
|
||||
{
|
||||
connect(this, &AWSConnector::connected, this, &AWSConnector::onConnected);
|
||||
}
|
||||
|
||||
AWSConnector::~AWSConnector()
|
||||
{
|
||||
}
|
||||
|
||||
void AWSConnector::connect2AWS(const QString &endpoint, const QString &clientId, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile)
|
||||
{
|
||||
m_networkConnection = std::shared_ptr<MbedTLSConnection>(new MbedTLSConnection(
|
||||
endpoint.toStdString(),
|
||||
8883,
|
||||
caFile.toStdString(),
|
||||
clientCertFile.toStdString(),
|
||||
clientPrivKeyFile.toStdString(),
|
||||
std::chrono::milliseconds(30000),
|
||||
std::chrono::milliseconds(30000),
|
||||
std::chrono::milliseconds(30000),
|
||||
true
|
||||
));
|
||||
m_client = MqttClient::Create(m_networkConnection, std::chrono::milliseconds(30000));
|
||||
m_client->SetDisconnectCallbackPtr(&onDisconnected, std::shared_ptr<DisconnectCallbackContextData>(this));
|
||||
m_client->SetAutoReconnectEnabled(true);
|
||||
m_clientId = clientId;
|
||||
|
||||
m_connectingFuture = QtConcurrent::run([&]() {
|
||||
ResponseCode rc = m_client->Connect(std::chrono::milliseconds(30000), true, mqtt::Version::MQTT_3_1_1, std::chrono::seconds(60), Utf8String::Create(m_clientId.toStdString()), nullptr, nullptr, nullptr);
|
||||
if (rc == ResponseCode::MQTT_CONNACK_CONNECTION_ACCEPTED) {
|
||||
qCDebug(dcCloud) << "Connected to AWS.";
|
||||
emit connected();
|
||||
} else {
|
||||
qCWarning(dcCloud) << "Error connecting to AWS. Response code:" << QString::fromStdString(ResponseHelper::ToString(rc));
|
||||
m_client.reset();
|
||||
m_networkConnection.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DisconnectCallbackContextData::~DisconnectCallbackContextData() {}
|
||||
|
||||
void AWSConnector::disconnectAWS()
|
||||
{
|
||||
if (isConnected()) {
|
||||
m_client->Disconnect(std::chrono::seconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
bool AWSConnector::isConnected() const
|
||||
{
|
||||
return m_connectingFuture.isFinished() && m_networkConnection && m_client && m_client->IsConnected();
|
||||
}
|
||||
|
||||
|
||||
quint16 AWSConnector::publish(const QString &topic, const QVariantMap &message)
|
||||
{
|
||||
if (!isConnected()) {
|
||||
qCWarning(dcCloud()) << "Can't publish to AWS: Not connected.";
|
||||
return -1;
|
||||
}
|
||||
QString fullTopic = QString("%1/%2").arg(m_clientId, topic);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(message);
|
||||
|
||||
uint16_t packetId = 0;
|
||||
ResponseCode res = m_client->PublishAsync(Utf8String::Create(fullTopic.toStdString()), false, false, mqtt::QoS::QOS1, jsonDoc.toJson().toStdString(), &publishCallback, packetId);
|
||||
qCDebug(dcCloud()) << "publish call queued with status:" << QString::fromStdString(ResponseHelper::ToString(res)) << packetId;
|
||||
s_requestMap.insert(packetId, this);
|
||||
return packetId;
|
||||
}
|
||||
|
||||
void AWSConnector::subscribe(const QStringList &topics)
|
||||
{
|
||||
m_subscribedTopics.append(topics);
|
||||
|
||||
if (!isConnected()) {
|
||||
qCDebug(dcCloud()) << "Can't subscribe to AWS: Not connected. Subscription will happen upon next connection.";
|
||||
return;
|
||||
}
|
||||
subscribeInternally(topics);
|
||||
}
|
||||
|
||||
void AWSConnector::onConnected()
|
||||
{
|
||||
if (!m_subscribedTopics.isEmpty()) {
|
||||
subscribeInternally(m_subscribedTopics);
|
||||
}
|
||||
}
|
||||
|
||||
void AWSConnector::subscribeInternally(const QStringList &topics)
|
||||
{
|
||||
util::Vector<std::shared_ptr<mqtt::Subscription>> subscription_list;
|
||||
foreach (const QString &topic, topics) {
|
||||
QString finalTopic = QString("%1/%2").arg(m_clientId, topic);
|
||||
qCDebug(dcCloud()) << "topic to subscribe is" << finalTopic << "is valid topic:" << Subscription::IsValidTopicName(finalTopic.toStdString());
|
||||
auto subscription = mqtt::Subscription::Create(Utf8String::Create(finalTopic.toStdString()), mqtt::QoS::QOS1, &onSubscriptionReceivedCallback, std::shared_ptr<SubscriptionHandlerContextData>(this));
|
||||
subscription_list.push_back(subscription);
|
||||
}
|
||||
|
||||
|
||||
uint16_t packetId;
|
||||
ResponseCode res = m_client->SubscribeAsync(subscription_list, subscribeCallback, packetId);
|
||||
qCDebug(dcCloud()) << "subscribe call queued with status:" << QString::fromStdString(ResponseHelper::ToString(res)) << packetId;
|
||||
s_requestMap.insert(packetId, this);
|
||||
}
|
||||
|
||||
void AWSConnector::publishCallback(uint16_t actionId, ResponseCode rc)
|
||||
{
|
||||
AWSConnector* obj = s_requestMap.take(actionId);
|
||||
if (!obj) {
|
||||
qCWarning(dcCloud())<< "Received a response callback but don't have an object waiting for it.";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (rc) {
|
||||
case ResponseCode::SUCCESS:
|
||||
emit obj->responseReceived(actionId, true);
|
||||
qCDebug(dcCloud()) << "Successfully published" << actionId;
|
||||
break;
|
||||
default:
|
||||
qCDebug(dcCloud())<< "Error publishing data to AWS:" << QString::fromStdString(ResponseHelper::ToString(rc));
|
||||
emit obj->responseReceived(actionId, false);
|
||||
}
|
||||
}
|
||||
|
||||
void AWSConnector::subscribeCallback(uint16_t actionId, ResponseCode rc)
|
||||
{
|
||||
qCDebug(dcCloud()) << "subscribed to topic" << actionId << QString::fromStdString(ResponseHelper::ToString(rc));
|
||||
}
|
||||
|
||||
ResponseCode AWSConnector::onSubscriptionReceivedCallback(util::String topic_name, util::String payload, std::shared_ptr<SubscriptionHandlerContextData> p_app_handler_data)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(QByteArray::fromStdString(payload), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCDebug(dcCloud()) << "Failed to parse JSON from AWS subscription on topic" << QString::fromStdString(topic_name) << ":" << error.errorString() << "\n" << QString::fromStdString(payload);
|
||||
return ResponseCode::JSON_PARSING_ERROR;
|
||||
}
|
||||
|
||||
AWSConnector *connector = dynamic_cast<AWSConnector*>(p_app_handler_data.get());
|
||||
QString topic = QString::fromStdString(topic_name);
|
||||
topic.remove(QRegExp("^" + connector->m_clientId + "/"));
|
||||
emit connector->subscriptionReceived(topic, jsonDoc.toVariant().toMap());
|
||||
return ResponseCode::SUCCESS;
|
||||
}
|
||||
|
||||
ResponseCode AWSConnector::onDisconnected(util::String mqtt_client_id, std::shared_ptr<DisconnectCallbackContextData> p_app_handler_data)
|
||||
{
|
||||
Q_UNUSED(p_app_handler_data)
|
||||
qCDebug(dcCloud()) << "disconnected" << QString::fromStdString(mqtt_client_id);
|
||||
return ResponseCode::SUCCESS;
|
||||
}
|
||||
54
libguh-core/awsconnector.h
Normal file
54
libguh-core/awsconnector.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef AWSCONNECTOR_H
|
||||
#define AWSCONNECTOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
|
||||
#include "MbedTLS/MbedTLSConnection.hpp"
|
||||
#include <mqtt/Client.hpp>
|
||||
#include <mqtt/Common.hpp>
|
||||
|
||||
class AWSConnector : public QObject, public awsiotsdk::mqtt::SubscriptionHandlerContextData, public awsiotsdk::DisconnectCallbackContextData
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AWSConnector(QObject *parent = 0);
|
||||
~AWSConnector();
|
||||
|
||||
void connect2AWS(const QString &endpoint, const QString &clientId, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile);
|
||||
void disconnectAWS();
|
||||
bool isConnected() const;
|
||||
|
||||
quint16 publish(const QString &topic, const QVariantMap &message);
|
||||
|
||||
void subscribe(const QStringList &topics);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void responseReceived(quint16 id, bool success);
|
||||
void subscriptionReceived(const QString &topic, const QVariantMap &data);
|
||||
|
||||
private slots:
|
||||
void onConnected();
|
||||
|
||||
private:
|
||||
void subscribeInternally(const QStringList &topcis);
|
||||
static void publishCallback(uint16_t actionId, awsiotsdk::ResponseCode rc);
|
||||
static void subscribeCallback(uint16_t actionId, awsiotsdk::ResponseCode rc);
|
||||
static awsiotsdk::ResponseCode onSubscriptionReceivedCallback(awsiotsdk::util::String topic_name, awsiotsdk::util::String payload,
|
||||
std::shared_ptr<SubscriptionHandlerContextData> p_app_handler_data);
|
||||
static awsiotsdk::ResponseCode onDisconnected(awsiotsdk::util::String mqtt_client_id,
|
||||
std::shared_ptr<DisconnectCallbackContextData> p_app_handler_data);
|
||||
|
||||
private:
|
||||
std::shared_ptr<awsiotsdk::network::MbedTLSConnection> m_networkConnection;
|
||||
std::shared_ptr<awsiotsdk::MqttClient> m_client;
|
||||
|
||||
QString m_clientId;
|
||||
QFuture<void> m_connectingFuture;
|
||||
QStringList m_subscribedTopics;
|
||||
|
||||
static QHash<quint16, AWSConnector*> s_requestMap;
|
||||
};
|
||||
|
||||
#endif // AWSCONNECTOR_H
|
||||
123
libguh-core/cloudmanager.cpp
Normal file
123
libguh-core/cloudmanager.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "cloudmanager.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
#include <QNetworkSession>
|
||||
#include <QNetworkConfigurationManager>
|
||||
|
||||
CloudManager::CloudManager(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_awsConnector = new AWSConnector(this);
|
||||
connect(m_awsConnector, &AWSConnector::subscriptionReceived, this, &CloudManager::subscriptionReceived);
|
||||
|
||||
// Extract the machine id so we have a unique identifier for this machine
|
||||
// TODO: this only works for debian based systems, perhaps we should find something more general
|
||||
QFile f("/etc/machine-id");
|
||||
if (f.open(QFile::ReadOnly)) {
|
||||
m_deviceId = QString::fromLatin1(f.readAll()).trimmed();
|
||||
qCDebug(dcCloud()) << "Device ID is:" << m_deviceId;
|
||||
setEnabled(true);
|
||||
m_awsConnector->subscribe({"pair/response"});
|
||||
} else {
|
||||
qWarning(dcCloud()) << "Failed to open /etc/machine-id for reading. Cloud connection will not work.";
|
||||
}
|
||||
|
||||
connect(GuhCore::instance()->networkManager(), &NetworkManager::stateChanged, this, &CloudManager::onlineStateChanged);
|
||||
}
|
||||
|
||||
void CloudManager::setServerUrl(const QString &serverUrl)
|
||||
{
|
||||
m_serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
void CloudManager::setDeviceId(const QString &deviceId)
|
||||
{
|
||||
m_deviceId = deviceId;
|
||||
}
|
||||
|
||||
void CloudManager::setClientCertificates(const QString &caCertificate, const QString &clientCertificate, const QString &clientCertificateKey)
|
||||
{
|
||||
m_caCertificate = caCertificate;
|
||||
m_clientCertificate = clientCertificate;
|
||||
m_clientCertificateKey = clientCertificateKey;
|
||||
}
|
||||
|
||||
bool CloudManager::enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void CloudManager::setEnabled(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
bool missingConfig = false;
|
||||
if (m_deviceId.isEmpty()) {
|
||||
qCWarning(dcCloud()) << "Don't have a unique device ID (/etc/machine-id).";
|
||||
missingConfig = true;
|
||||
}
|
||||
|
||||
if (GuhCore::instance()->configuration()->cloudServerUrl().isEmpty()) {
|
||||
qCWarning(dcCloud()) << "Cloud server URL not set in configuration.";
|
||||
missingConfig = true;
|
||||
}
|
||||
if (GuhCore::instance()->configuration()->cloudCertificate().isEmpty()) {
|
||||
qCWarning(dcCloud()) << "Cloud certificate not set in configuration.";
|
||||
missingConfig = true;
|
||||
}
|
||||
if (GuhCore::instance()->configuration()->cloudCertificateKey().isEmpty()) {
|
||||
qCWarning(dcCloud()) << "Cloud certificate key not set in configuration.";
|
||||
missingConfig = true;
|
||||
}
|
||||
if (GuhCore::instance()->configuration()->cloudCertificateCA().isEmpty()) {
|
||||
qCWarning(dcCloud()) << "Cloud certificate CA not set in configuration.";
|
||||
missingConfig = true;
|
||||
}
|
||||
if (missingConfig) {
|
||||
qCWarning(dcCloud()) << "Cloud configuration incomplete. Not enabling cloud connection.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_enabled = true;
|
||||
if (!m_awsConnector->isConnected() && GuhCore::instance()->networkManager()->state() == NetworkManager::NetworkManagerStateConnectedGlobal) {
|
||||
connect2aws();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CloudManager::pairDevice(const QString &idToken, const QString &authToken, const QString &cognitoId)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert("id", ++m_id);
|
||||
map.insert("idToken", idToken);
|
||||
map.insert("authToken", authToken);
|
||||
map.insert("cognitoId", cognitoId);
|
||||
m_awsConnector->publish("pair", map);
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void CloudManager::connect2aws()
|
||||
{
|
||||
m_awsConnector->connect2AWS(GuhCore::instance()->configuration()->cloudServerUrl(),
|
||||
m_deviceId,
|
||||
GuhCore::instance()->configuration()->cloudCertificateCA(),
|
||||
GuhCore::instance()->configuration()->cloudCertificate(),
|
||||
GuhCore::instance()->configuration()->cloudCertificateKey()
|
||||
);
|
||||
}
|
||||
|
||||
void CloudManager::onlineStateChanged()
|
||||
{
|
||||
qWarning() << "online state changed" << GuhCore::instance()->networkManager()->state();
|
||||
if (GuhCore::instance()->networkManager()->state() == NetworkManager::NetworkManagerStateConnectedGlobal) {
|
||||
if (m_enabled && !m_awsConnector->isConnected()) {
|
||||
connect2aws();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloudManager::subscriptionReceived(const QString &topic, const QVariantMap &message)
|
||||
{
|
||||
qCDebug(dcCloud()) << "subscription reveiv ed" << topic << message;
|
||||
if (topic == "pair/response") {
|
||||
emit pairingReply(message.value("id").toInt(), message.value("status").toInt());
|
||||
}
|
||||
}
|
||||
49
libguh-core/cloudmanager.h
Normal file
49
libguh-core/cloudmanager.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef CLOUDMANAGER_H
|
||||
#define CLOUDMANAGER_H
|
||||
|
||||
#include "awsconnector.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QNetworkSession>
|
||||
|
||||
class CloudManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CloudManager(QObject *parent = nullptr);
|
||||
|
||||
void setServerUrl(const QString &serverUrl);
|
||||
void setDeviceId(const QString &deviceId);
|
||||
void setClientCertificates(const QString &caCertificate, const QString &clientCertificate, const QString &clientCertificateKey);
|
||||
|
||||
bool enabled() const;
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
int pairDevice(const QString &idToken, const QString &authToken, const QString &cognitoId);
|
||||
|
||||
signals:
|
||||
void pairingReply(int pairingTransactionId, int status);
|
||||
|
||||
private:
|
||||
void connect2aws();
|
||||
|
||||
private slots:
|
||||
void onlineStateChanged();
|
||||
void subscriptionReceived(const QString &topic, const QVariantMap &message);
|
||||
|
||||
private:
|
||||
QNetworkSession *m_networkSession;
|
||||
QTimer m_reconnectTimer;
|
||||
bool m_enabled = false;
|
||||
AWSConnector *m_awsConnector = nullptr;
|
||||
int m_id = 0; // id for transactions. e.g. pairDevice
|
||||
|
||||
QString m_serverUrl;
|
||||
QString m_deviceId;
|
||||
QString m_caCertificate;
|
||||
QString m_clientCertificate;
|
||||
QString m_clientCertificateKey;
|
||||
};
|
||||
|
||||
#endif // CLOUDMANAGER_H
|
||||
@ -305,30 +305,46 @@ void GuhConfiguration::setBluetoothServerEnabled(const bool &enabled)
|
||||
emit bluetoothServerEnabled();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::cloudServerUrl() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("Cloud");
|
||||
return settings.value("cloudServerUrl").toString();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::cloudCertificateCA() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("Cloud");
|
||||
return settings.value("cloudCertificateCA").toString();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::cloudCertificate() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("Cloud");
|
||||
return settings.value("cloudCertificate").toString();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::cloudCertificateKey() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("Cloud");
|
||||
return settings.value("cloudCertificateKey").toString();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::sslCertificate() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("SSL");
|
||||
return settings.value("certificate", "/etc/ssl/certs/guhd-certificate.crt").toString();
|
||||
return settings.value("certificate").toString();
|
||||
}
|
||||
|
||||
QString GuhConfiguration::sslCertificateKey() const
|
||||
{
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("SSL");
|
||||
return settings.value("certificate-key", "/etc/ssl/certs/guhd-certificate.key").toString();
|
||||
}
|
||||
|
||||
void GuhConfiguration::setSslCertificate(const QString &sslCertificate, const QString &sslCertificateKey)
|
||||
{
|
||||
qCDebug(dcApplication()) << "Configuration: SSL certificate:" << sslCertificate << "SSL certificate key:" << sslCertificateKey;
|
||||
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
settings.beginGroup("SSL");
|
||||
settings.setValue("certificate", sslCertificate);
|
||||
settings.setValue("certificate-key", sslCertificateKey);
|
||||
settings.endGroup();
|
||||
emit sslCertificateChanged();
|
||||
return settings.value("certificate-key").toString();
|
||||
}
|
||||
|
||||
void GuhConfiguration::setServerUuid(const QUuid &uuid)
|
||||
|
||||
@ -86,6 +86,9 @@ public:
|
||||
QLocale locale() const;
|
||||
void setLocale(const QLocale &locale);
|
||||
|
||||
QString sslCertificate() const;
|
||||
QString sslCertificateKey() const;
|
||||
|
||||
// TCP server
|
||||
QHash<QString, ServerConfiguration> tcpServerConfigurations() const;
|
||||
void setTcpServerConfiguration(const ServerConfiguration &config);
|
||||
@ -105,9 +108,11 @@ public:
|
||||
bool bluetoothServerEnabled() const;
|
||||
void setBluetoothServerEnabled(const bool &enabled);
|
||||
|
||||
QString sslCertificate() const;
|
||||
QString sslCertificateKey() const;
|
||||
void setSslCertificate(const QString &sslCertificate, const QString &sslCertificateKey);
|
||||
// Cloud
|
||||
QString cloudServerUrl() const;
|
||||
QString cloudCertificateCA() const;
|
||||
QString cloudCertificate() const;
|
||||
QString cloudCertificateKey() const;
|
||||
|
||||
private:
|
||||
QHash<QString, ServerConfiguration> m_tcpServerConfigs;
|
||||
@ -138,8 +143,6 @@ signals:
|
||||
void webSocketServerConfigurationRemoved(const QString &configId);
|
||||
|
||||
void bluetoothServerEnabledChanged();
|
||||
|
||||
void sslCertificateChanged();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -408,6 +408,12 @@ UserManager *GuhCore::userManager() const
|
||||
return m_userManager;
|
||||
}
|
||||
|
||||
CloudManager *GuhCore::cloudManager() const
|
||||
{
|
||||
return m_cloudManager;
|
||||
}
|
||||
|
||||
|
||||
/*! Constructs GuhCore with the given \a parent. This is private.
|
||||
Use \l{GuhCore::instance()} to access the single instance.*/
|
||||
GuhCore::GuhCore(QObject *parent) :
|
||||
@ -440,6 +446,8 @@ void GuhCore::init() {
|
||||
|
||||
m_userManager = new UserManager(this);
|
||||
|
||||
m_cloudManager = new CloudManager(this);
|
||||
|
||||
connect(m_configuration, &GuhConfiguration::localeChanged, this, &GuhCore::onLocaleChanged);
|
||||
|
||||
connect(m_deviceManager, &DeviceManager::pluginConfigChanged, this, &GuhCore::pluginConfigChanged);
|
||||
@ -464,6 +472,7 @@ void GuhCore::init() {
|
||||
connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManager::timeTick);
|
||||
|
||||
m_logger->logSystemEvent(m_timeManager->currentDateTime(), true);
|
||||
|
||||
emit initialized();
|
||||
|
||||
// Evaluate rules on current time
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "devicemanager.h"
|
||||
#include "ruleengine.h"
|
||||
#include "servermanager.h"
|
||||
#include "cloudmanager.h"
|
||||
|
||||
#include "time/timemanager.h"
|
||||
|
||||
@ -77,6 +78,7 @@ public:
|
||||
BluetoothServer *bluetoothServer() const;
|
||||
NetworkManager *networkManager() const;
|
||||
UserManager *userManager() const;
|
||||
CloudManager *cloudManager() const;
|
||||
|
||||
static QStringList getAvailableLanguages();
|
||||
|
||||
@ -110,6 +112,7 @@ private:
|
||||
RuleEngine *m_ruleEngine;
|
||||
LogEngine *m_logger;
|
||||
TimeManager *m_timeManager;
|
||||
CloudManager *m_cloudManager;
|
||||
|
||||
NetworkManager *m_networkManager;
|
||||
UserManager *m_userManager;
|
||||
|
||||
@ -144,7 +144,7 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
|
||||
setDescription("SetupRemoteAccess", "Setup the remote connection by providing AWS token information");
|
||||
params.insert("idToken", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("authToken", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("cognitoIdentityPoolIdentityId", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("cognitoId", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
setParams("SetupRemoteAccess", params);
|
||||
returns.insert("status", JsonTypes::basicTypeToString(JsonTypes::Int));
|
||||
setReturns("SetupRemoteAccess", returns);
|
||||
@ -264,6 +264,14 @@ JsonReply *JsonRPCServer::RemoveToken(const QVariantMap ¶ms)
|
||||
JsonReply *JsonRPCServer::SetupRemoteAccess(const QVariantMap ¶ms)
|
||||
{
|
||||
|
||||
int id = GuhCore::instance()->cloudManager()->pairDevice(params.value("idToken").toString(), params.value("authToken").toString(), params.value("cognitoId").toString());
|
||||
qWarning() << "waiting for id" << id;
|
||||
JsonReply *reply = createAsyncReply("SetupRemoteAccess");
|
||||
m_pairingRequests.insert(id, reply);
|
||||
connect(reply, &JsonReply::finished, [this, id](){
|
||||
m_pairingRequests.remove(id);
|
||||
});
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*! Returns the list of registred \l{JsonHandler}{JsonHandlers} and their name.*/
|
||||
@ -353,6 +361,8 @@ void JsonRPCServer::setup()
|
||||
registerHandler(new StateHandler(this));
|
||||
registerHandler(new ConfigurationHandler(this));
|
||||
registerHandler(new NetworkManagerHandler(this));
|
||||
|
||||
connect(GuhCore::instance()->cloudManager(), &CloudManager::pairingReply, this, &JsonRPCServer::pairingFinished);
|
||||
}
|
||||
|
||||
void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &data)
|
||||
@ -486,6 +496,20 @@ void JsonRPCServer::asyncReplyFinished()
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void JsonRPCServer::pairingFinished(int pairingTransactionId, int status)
|
||||
{
|
||||
qWarning() << "pairing finished" << pairingTransactionId << status;
|
||||
JsonReply *reply = m_pairingRequests.take(pairingTransactionId);
|
||||
if (!reply) {
|
||||
return;
|
||||
}
|
||||
qWarning() << "blubbbbb";
|
||||
QVariantMap returns;
|
||||
returns.insert("status", status);
|
||||
reply->setData(returns);
|
||||
reply->finished();
|
||||
}
|
||||
|
||||
void JsonRPCServer::registerHandler(JsonHandler *handler)
|
||||
{
|
||||
m_handlers.insert(handler->name(), handler);
|
||||
|
||||
@ -81,6 +81,8 @@ private slots:
|
||||
|
||||
void asyncReplyFinished();
|
||||
|
||||
void pairingFinished(int pairingTransactionId, int status);
|
||||
|
||||
private:
|
||||
QMap<TransportInterface*, bool> m_interfaces;
|
||||
QHash<QString, JsonHandler *> m_handlers;
|
||||
@ -89,6 +91,8 @@ private:
|
||||
// clientId, notificationsEnabled
|
||||
QHash<QUuid, bool> m_clients;
|
||||
|
||||
QHash<int, JsonReply*> m_pairingRequests;
|
||||
|
||||
int m_notificationId;
|
||||
|
||||
void registerHandler(JsonHandler *handler);
|
||||
|
||||
@ -67,6 +67,9 @@ HEADERS += $$top_srcdir/libguh-core/guhcore.h \
|
||||
$$top_srcdir/libguh-core/tokeninfo.h \
|
||||
$$top_srcdir/libguh-core/certificategenerator.h \
|
||||
$$top_srcdir/libguh-core/logging/logvaluetool.h
|
||||
$$top_srcdir/libguh-core/awsconnector.h \
|
||||
$$top_srcdir/libguh-core/cloudconnector.h \
|
||||
$$top_srcdir/libguh-core/MbedTLS/MbedTLSConnection.hpp \
|
||||
|
||||
|
||||
SOURCES += $$top_srcdir/libguh-core/guhcore.cpp \
|
||||
@ -121,4 +124,6 @@ SOURCES += $$top_srcdir/libguh-core/guhcore.cpp \
|
||||
$$top_srcdir/libguh-core/tokeninfo.cpp \
|
||||
$$top_srcdir/libguh-core/certificategenerator.cpp \
|
||||
$$top_srcdir/libguh-core/logging/logvaluetool.cpp
|
||||
|
||||
$$top_srcdir/libguh-core/awsconnector.cpp \
|
||||
$$top_srcdir/libguh-core/cloudconnector.cpp \
|
||||
$$top_srcdir/libguh-core/MbedTLS/MbedTLSConnection.cpp \
|
||||
|
||||
@ -8,9 +8,9 @@ INCLUDEPATH += ../libguh-core ../libguh-core/jsonrpc ../libguh
|
||||
target.path = /usr/bin
|
||||
INSTALLS += target
|
||||
|
||||
QT *= sql xml websockets bluetooth dbus
|
||||
QT *= sql xml websockets bluetooth dbus network
|
||||
|
||||
LIBS += -L$$top_builddir/libguh/ -lguh -L$$top_builddir/libguh-core -lguh-core
|
||||
LIBS += -L$$top_builddir/libguh/ -lguh -L$$top_builddir/libguh-core -lguh-core -lssl -lcrypto -laws-iot-sdk-cpp -lmbedtls -lmbedx509 -lmbedcrypto
|
||||
|
||||
# Translations
|
||||
TRANSLATIONS *= $$top_srcdir/translations/guhd-en_US.ts \
|
||||
|
||||
@ -13,7 +13,7 @@ INCLUDEPATH += $$top_srcdir/server/ \
|
||||
$$top_srcdir/libguh \
|
||||
$$top_srcdir/tests/auto/
|
||||
|
||||
LIBS += -L$$top_builddir/libguh/ -lguh -lssl -lcrypto
|
||||
LIBS += -L$$top_builddir/libguh/ -lguh -lssl -lcrypto -laws-iot-sdk-cpp -lmbedtls -lmbedx509 -lmbedcrypto
|
||||
|
||||
target.path = /usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')
|
||||
INSTALLS += target
|
||||
|
||||
7
tests/scripts/setupremoteaccess.sh
Executable file
7
tests/scripts/setupremoteaccess.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z $4 ]; then
|
||||
echo "usage: $0 host idToken authToken cognitoId"
|
||||
else
|
||||
(echo '{"id":1, "method": "JSONRPC.SetupRemoteAccess", "params": { "idToken": "'$2'", "authToken": "'$3'", "cognitoId": "'$4'"}}'; sleep 1) | nc $1 2222
|
||||
fi
|
||||
Reference in New Issue
Block a user