From 275e3b3921413bcf804b820111a8131a006c2e7c Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 11 Sep 2017 19:47:50 +0200 Subject: [PATCH] added cloud connection --- debian/control | 3 +- libguh-core/MbedTLS/MbedTLSConnection.cpp | 348 ++++++++++++++++++++++ libguh-core/MbedTLS/MbedTLSConnection.hpp | 179 +++++++++++ libguh-core/MbedTLS/README.txt | 6 + libguh-core/awsconnector.cpp | 166 +++++++++++ libguh-core/awsconnector.h | 54 ++++ libguh-core/cloudmanager.cpp | 123 ++++++++ libguh-core/cloudmanager.h | 49 +++ libguh-core/guhconfiguration.cpp | 44 ++- libguh-core/guhconfiguration.h | 13 +- libguh-core/guhcore.cpp | 9 + libguh-core/guhcore.h | 3 + libguh-core/jsonrpc/jsonrpcserver.cpp | 26 +- libguh-core/jsonrpc/jsonrpcserver.h | 4 + libguh-core/libguh-core.pro | 7 +- server/server.pro | 4 +- tests/libguh-core/libguh-core.pro | 2 +- tests/scripts/setupremoteaccess.sh | 7 + 18 files changed, 1022 insertions(+), 25 deletions(-) create mode 100644 libguh-core/MbedTLS/MbedTLSConnection.cpp create mode 100644 libguh-core/MbedTLS/MbedTLSConnection.hpp create mode 100644 libguh-core/MbedTLS/README.txt create mode 100644 libguh-core/awsconnector.cpp create mode 100644 libguh-core/awsconnector.h create mode 100644 libguh-core/cloudmanager.cpp create mode 100644 libguh-core/cloudmanager.h create mode 100755 tests/scripts/setupremoteaccess.sh diff --git a/debian/control b/debian/control index 1b47ad73..ab3bebec 100644 --- a/debian/control +++ b/debian/control @@ -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 diff --git a/libguh-core/MbedTLS/MbedTLSConnection.cpp b/libguh-core/MbedTLS/MbedTLSConnection.cpp new file mode 100644 index 00000000..9add91b4 --- /dev/null +++ b/libguh-core/MbedTLS/MbedTLSConnection.cpp @@ -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 +#include + +#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(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(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 &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(); + } + } +} diff --git a/libguh-core/MbedTLS/MbedTLSConnection.hpp b/libguh-core/MbedTLS/MbedTLSConnection.hpp new file mode 100644 index 00000000..8aa17bd1 --- /dev/null +++ b/libguh-core/MbedTLS/MbedTLSConnection.hpp @@ -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 + +#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 &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(); + }; + } +} diff --git a/libguh-core/MbedTLS/README.txt b/libguh-core/MbedTLS/README.txt new file mode 100644 index 00000000..a1bbd537 --- /dev/null +++ b/libguh-core/MbedTLS/README.txt @@ -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 + diff --git a/libguh-core/awsconnector.cpp b/libguh-core/awsconnector.cpp new file mode 100644 index 00000000..9db6b93a --- /dev/null +++ b/libguh-core/awsconnector.cpp @@ -0,0 +1,166 @@ +#include "awsconnector.h" +#include "loggingcategories.h" + +#include +#include +#include +#include + +using namespace awsiotsdk; +using namespace awsiotsdk::network; +using namespace awsiotsdk::mqtt; + +QHash 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(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(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> 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(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 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(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 p_app_handler_data) +{ + Q_UNUSED(p_app_handler_data) + qCDebug(dcCloud()) << "disconnected" << QString::fromStdString(mqtt_client_id); + return ResponseCode::SUCCESS; +} diff --git a/libguh-core/awsconnector.h b/libguh-core/awsconnector.h new file mode 100644 index 00000000..7052e25d --- /dev/null +++ b/libguh-core/awsconnector.h @@ -0,0 +1,54 @@ +#ifndef AWSCONNECTOR_H +#define AWSCONNECTOR_H + +#include +#include + +#include "MbedTLS/MbedTLSConnection.hpp" +#include +#include + +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 p_app_handler_data); + static awsiotsdk::ResponseCode onDisconnected(awsiotsdk::util::String mqtt_client_id, + std::shared_ptr p_app_handler_data); + +private: + std::shared_ptr m_networkConnection; + std::shared_ptr m_client; + + QString m_clientId; + QFuture m_connectingFuture; + QStringList m_subscribedTopics; + + static QHash s_requestMap; +}; + +#endif // AWSCONNECTOR_H diff --git a/libguh-core/cloudmanager.cpp b/libguh-core/cloudmanager.cpp new file mode 100644 index 00000000..963782b1 --- /dev/null +++ b/libguh-core/cloudmanager.cpp @@ -0,0 +1,123 @@ +#include "cloudmanager.h" +#include "guhcore.h" + +#include +#include + +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()); + } +} diff --git a/libguh-core/cloudmanager.h b/libguh-core/cloudmanager.h new file mode 100644 index 00000000..bc5a3043 --- /dev/null +++ b/libguh-core/cloudmanager.h @@ -0,0 +1,49 @@ +#ifndef CLOUDMANAGER_H +#define CLOUDMANAGER_H + +#include "awsconnector.h" + +#include +#include +#include + +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 diff --git a/libguh-core/guhconfiguration.cpp b/libguh-core/guhconfiguration.cpp index 9279d479..5c3bf0c4 100644 --- a/libguh-core/guhconfiguration.cpp +++ b/libguh-core/guhconfiguration.cpp @@ -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) diff --git a/libguh-core/guhconfiguration.h b/libguh-core/guhconfiguration.h index d4a58ded..525a09e0 100644 --- a/libguh-core/guhconfiguration.h +++ b/libguh-core/guhconfiguration.h @@ -86,6 +86,9 @@ public: QLocale locale() const; void setLocale(const QLocale &locale); + QString sslCertificate() const; + QString sslCertificateKey() const; + // TCP server QHash 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 m_tcpServerConfigs; @@ -138,8 +143,6 @@ signals: void webSocketServerConfigurationRemoved(const QString &configId); void bluetoothServerEnabledChanged(); - - void sslCertificateChanged(); }; } diff --git a/libguh-core/guhcore.cpp b/libguh-core/guhcore.cpp index 38eabfd8..7587f3b1 100644 --- a/libguh-core/guhcore.cpp +++ b/libguh-core/guhcore.cpp @@ -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 diff --git a/libguh-core/guhcore.h b/libguh-core/guhcore.h index ad203511..9ac34d84 100644 --- a/libguh-core/guhcore.h +++ b/libguh-core/guhcore.h @@ -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; diff --git a/libguh-core/jsonrpc/jsonrpcserver.cpp b/libguh-core/jsonrpc/jsonrpcserver.cpp index 10a2b4fc..51b5e972 100644 --- a/libguh-core/jsonrpc/jsonrpcserver.cpp +++ b/libguh-core/jsonrpc/jsonrpcserver.cpp @@ -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); diff --git a/libguh-core/jsonrpc/jsonrpcserver.h b/libguh-core/jsonrpc/jsonrpcserver.h index ff9b1ff0..99b0d669 100644 --- a/libguh-core/jsonrpc/jsonrpcserver.h +++ b/libguh-core/jsonrpc/jsonrpcserver.h @@ -81,6 +81,8 @@ private slots: void asyncReplyFinished(); + void pairingFinished(int pairingTransactionId, int status); + private: QMap m_interfaces; QHash m_handlers; @@ -89,6 +91,8 @@ private: // clientId, notificationsEnabled QHash m_clients; + QHash m_pairingRequests; + int m_notificationId; void registerHandler(JsonHandler *handler); diff --git a/libguh-core/libguh-core.pro b/libguh-core/libguh-core.pro index bf77f9af..ac39fe6a 100644 --- a/libguh-core/libguh-core.pro +++ b/libguh-core/libguh-core.pro @@ -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 \ diff --git a/server/server.pro b/server/server.pro index 036b5669..996cee93 100644 --- a/server/server.pro +++ b/server/server.pro @@ -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 \ diff --git a/tests/libguh-core/libguh-core.pro b/tests/libguh-core/libguh-core.pro index f345f435..43223ff4 100644 --- a/tests/libguh-core/libguh-core.pro +++ b/tests/libguh-core/libguh-core.pro @@ -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 diff --git a/tests/scripts/setupremoteaccess.sh b/tests/scripts/setupremoteaccess.sh new file mode 100755 index 00000000..4a206946 --- /dev/null +++ b/tests/scripts/setupremoteaccess.sh @@ -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