Skip to content

Commit

Permalink
feat(modem): Support custom transport in AT TCP client example
Browse files Browse the repository at this point in the history
  • Loading branch information
david-cermak committed Aug 17, 2023
1 parent 42fe608 commit ae629ed
Show file tree
Hide file tree
Showing 17 changed files with 602 additions and 38 deletions.
6 changes: 5 additions & 1 deletion components/esp_modem/examples/modem_tcp_client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
## Overview
This example demonstrates how to act as a MQTT client using modem's TCP commands (provided, the device supports "socket" related commands)

This example could be used in two different configurations:
1) Custom TCP transport: Implements a TCP transport in form of AT commands and uses it as custom transport for mqtt client.
2) Localhost listener: Uses standard transports to connect and forwards socket layer data from the client to the modem using AT commands.

### Supported IDF versions

This example is supported from IDF `v4.4`.
This example is supported from IDF `v5.0`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRCS mbedtls_wrap.cpp
tls_transport.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Custom transports

This component is a placeholder of custom transports. It contains mbedTLS cxx wrapper which could be used to create a custom TLS layer on any kind of IO function.

# List of Transports

## TLS Transport

TLS layer on top of any custom transport. Very similar to `ssl_transport` (standard IDF transport), but this is customizable and could work on any kind of transport, not only the BSD socket based tcp transport (like `ssl_transport`).
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include <utility>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"

using const_buf = std::pair<const unsigned char *, std::size_t>;
using buf = std::pair<unsigned char *, std::size_t>;

class Tls {
public:
Tls();
bool init(bool is_server, bool verify);
int handshake();
int write(const unsigned char *buf, size_t len);
int read(unsigned char *buf, size_t len);
bool set_own_cert(const_buf crt, const_buf key);
bool set_ca_cert(const_buf crt);
virtual int send(const unsigned char *buf, size_t len) = 0;
virtual int recv(unsigned char *buf, size_t len) = 0;
size_t get_available_bytes();

private:
mbedtls_ssl_context ssl_{};
mbedtls_x509_crt public_cert_{};
mbedtls_pk_context pk_key_{};
mbedtls_x509_crt ca_cert_{};
mbedtls_ssl_config conf_{};
mbedtls_ctr_drbg_context ctr_drbg_{};
mbedtls_entropy_context entropy_{};

static void print_error(const char *function, int error_code);
static int bio_write(void *ctx, const unsigned char *buf, size_t len);
static int bio_read(void *ctx, unsigned char *buf, size_t len);
int mbedtls_pk_parse_key( mbedtls_pk_context *ctx,
const unsigned char *key, size_t keylen,
const unsigned char *pwd, size_t pwdlen);

};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include "esp_transport.h"

/**
* @brief Initializes TLS transport based on mbetls wrapper
*
* @param parent Transport on top of which we run TLS layer
* @return Transport handle on success
*/
esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ssl.h"
#include "mbedtls_wrap.hpp"

bool Tls::init(bool is_server, bool verify)
{
const char pers[] = "mbedtls_wrapper";
mbedtls_entropy_init(&entropy_);
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
int ret = mbedtls_ssl_config_defaults(&conf_, is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
if (ret) {
print_error("mbedtls_ssl_config_defaults", ret);
return false;
}
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
mbedtls_ssl_conf_authmode(&conf_, verify ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
if (ret) {
print_error("mbedtls_ssl_conf_own_cert", ret);
return false;
}
if (verify) {
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
}
ret = mbedtls_ssl_setup(&ssl_, &conf_);
if (ret) {
print_error("mbedtls_ssl_setup", ret);
return false;
}
return true;
}

void Tls::print_error(const char *function, int error_code)
{
static char error_buf[100];
mbedtls_strerror(error_code, error_buf, sizeof(error_buf));

printf("%s() returned -0x%04X\n", function, -error_code);
printf("-0x%04X: %s\n", -error_code, error_buf);
}
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
int Tls::handshake()
{
ESP_LOGI("TLS", "handshake");
int ret = 0;
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, nullptr);

while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
print_error( "mbedtls_ssl_handshake returned", ret );
return -1;
}
vTaskDelay(pdMS_TO_TICKS(500));
}
ESP_LOGI("TLS", "handshake done with %d", ret);
return ret;
}

int Tls::bio_write(void *ctx, const unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->send(buf, len);
}

int Tls::bio_read(void *ctx, unsigned char *buf, size_t len)
{
auto s = static_cast<Tls *>(ctx);
return s->recv(buf, len);
}

int Tls::write(const unsigned char *buf, size_t len)
{
return mbedtls_ssl_write( &ssl_, buf, len );
}

int Tls::read(unsigned char *buf, size_t len)
{
return mbedtls_ssl_read( &ssl_, buf, len );
}

bool Tls::set_own_cert(const_buf crt, const_buf key)
{
int ret = mbedtls_x509_crt_parse(&public_cert_, crt.first, crt.second);
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
ret = mbedtls_pk_parse_key(&pk_key_, key.first, key.second, nullptr, 0);
if (ret < 0) {
print_error("mbedtls_pk_parse_keyfile", ret);
return false;
}
return true;
}

bool Tls::set_ca_cert(const_buf crt)
{
int ret = mbedtls_x509_crt_parse(&ca_cert_, crt.first, crt.second);
if (ret < 0) {
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
return true;
}

Tls::Tls()
{
mbedtls_x509_crt_init(&public_cert_);
mbedtls_pk_init(&pk_key_);
mbedtls_x509_crt_init(&ca_cert_);
}

int Tls::mbedtls_pk_parse_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen, const unsigned char *pwd, size_t pwdlen)
{

return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen, nullptr, nullptr);
}

size_t Tls::get_available_bytes()
{
return ::mbedtls_ssl_get_bytes_avail(&ssl_);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_transport.h"
#include "mbedtls_wrap.hpp"

static const char *TAG = "tls_transport";

class TlsTransport: public Tls {
public:
explicit TlsTransport(esp_transport_handle_t parent) : Tls(), transport_(parent) {}
int send(const unsigned char *buf, size_t len) override;
int recv(unsigned char *buf, size_t len) override;
static bool set_func(esp_transport_handle_t tls_transport);

private:
esp_transport_handle_t transport_{};
int connect(const char *host, int port, int timeout_ms);

struct priv {
static int connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms);
static int read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms);
static int write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms);
static int close(esp_transport_handle_t t);
static int poll_read(esp_transport_handle_t t, int timeout_ms);
static int poll_write(esp_transport_handle_t t, int timeout_ms);
static int destroy(esp_transport_handle_t t);
};
};

esp_transport_handle_t esp_transport_tls_init(esp_transport_handle_t parent)
{
esp_transport_handle_t ssl = esp_transport_init();
auto *tls = new TlsTransport(parent);
esp_transport_set_context_data(ssl, tls);
TlsTransport::set_func(ssl);
return ssl;
}

int TlsTransport::send(const unsigned char *buf, size_t len)
{
return esp_transport_write(transport_, reinterpret_cast<const char *>(buf), len, 0);
}

int TlsTransport::recv(unsigned char *buf, size_t len)
{
int ret = esp_transport_read(transport_, reinterpret_cast<char *>(buf), len, 0);

if (ret == ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return ret == ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN ? 0 : ret;
}

bool TlsTransport::set_func(esp_transport_handle_t tls_transport)
{
return esp_transport_set_func(tls_transport, TlsTransport::priv::connect, TlsTransport::priv::read, TlsTransport::priv::write, TlsTransport::priv::close, TlsTransport::priv::poll_read, TlsTransport::priv::poll_write, TlsTransport::priv::destroy) == ESP_OK;
}

int TlsTransport::connect(const char *host, int port, int timeout_ms)
{
return esp_transport_connect(transport_, host, port, timeout_ms);
}

int TlsTransport::priv::connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
ESP_LOGI("tag", "SSL connect!");
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
tls->init(false, false);

ESP_LOGI("tag", "TCP connect!");
auto ret = tls->connect(host, port, timeout_ms);
if (ret < 0) {
ESP_LOGI("tag", "TCP connect fail!");
return ret;
}
return tls->handshake();
}

int TlsTransport::priv::read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
if (tls->get_available_bytes() <= 0) {
int poll = esp_transport_poll_read(t, timeout_ms);
if (poll == -1) {
return ERR_TCP_TRANSPORT_CONNECTION_FAILED;
}
if (poll == 0) {
return ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT;
}
}
return tls->read(reinterpret_cast<unsigned char *>(buffer), len);
}

int TlsTransport::priv::write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll;
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
ESP_LOGW(TAG, "Poll timeout or error timeout_ms=%d", timeout_ms);
return poll;
}

auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return tls->write(reinterpret_cast<const unsigned char *>(buffer), len);
}

int TlsTransport::priv::close(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_close(tls->transport_);
}

int TlsTransport::priv::poll_read(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_read(tls->transport_, timeout_ms);
}

int TlsTransport::priv::poll_write(esp_transport_handle_t t, int timeout_ms)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_poll_write(tls->transport_, timeout_ms);
}

int TlsTransport::priv::destroy(esp_transport_handle_t t)
{
auto tls = static_cast<TlsTransport *>(esp_transport_get_context_data(t));
return esp_transport_destroy(tls->transport_);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ endif()

idf_component_register(SRCS "modem_client.cpp"
"sock_dce.cpp"
"tcp_transport_at.cpp"
"${device_srcs}"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
menu "Example Configuration"

config EXAMPLE_CUSTOM_TCP_TRANSPORT
bool "Custom TCP transport"
help
Use custom TCP transport to connect to MQTT broker

choice EXAMPLE_MODEM_DEVICE
prompt "Choose supported modem device (DCE)"
default EXAMPLE_MODEM_DEVICE_BG96
Expand Down
Loading

0 comments on commit ae629ed

Please sign in to comment.