Resolved Issues

This commit is contained in:
Jitin George
2018-02-14 15:15:50 +05:30
parent 8cd3c47956
commit e29294b49a
9 changed files with 220 additions and 91 deletions

View File

@@ -1,3 +1,16 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -32,10 +45,10 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen)
return NULL;
}
ESP_LOGD(TAG, "host:%s: strlen %zu\n", use_host, hostlen);
ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
struct addrinfo *res;
if (getaddrinfo(use_host, NULL, &hints, &res)) {
ESP_LOGE(TAG, "couldn't get hostname for :%s:\n", use_host);
ESP_LOGE(TAG, "couldn't get hostname for :%s:", use_host);
free(use_host);
return NULL;
}
@@ -43,18 +56,18 @@ static struct addrinfo *resolve_host_name(const char *host, size_t hostlen)
return res;
}
static ssize_t tcp_read(struct esp_tls *tls, char *data, size_t datalen)
static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
{
return recv(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_read(struct esp_tls *tls, char *data, size_t datalen)
static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
{
ssize_t ret = SSL_read(tls->ssl, data, datalen);
if (ret < 0) {
int err = SSL_get_error(tls->ssl, ret);
if (err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) {
ESP_LOGE(TAG, "read error :%d:\n", ret);
ESP_LOGE(TAG, "read error :%d:", ret);
}
return -err;
}
@@ -104,11 +117,12 @@ err_freeaddr:
return -1;
}
static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t hostlen, struct esp_tls_cfg *cfg)
static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg)
{
int ret;
SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
const SSL_METHOD *method = cfg->ssl_method!= NULL ? cfg->ssl_method : TLSv1_2_client_method();
SSL_CTX *ssl_ctx = SSL_CTX_new(method);
if (!ssl_ctx) {
return -1;
}
@@ -128,9 +142,13 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h
X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL);
if (!ca) {
ESP_LOGE(TAG, "CA Error\n");
ESP_LOGE(TAG, "CA Error");
X509_free(ca);
BIO_free(bio);
SSL_CTX_free(ssl_ctx);
return -1;
}
ESP_LOGD(TAG, "CA OK\n");
ESP_LOGD(TAG, "CA OK");
X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), ca);
@@ -149,8 +167,8 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h
char *use_host = strndup(hostname, hostlen);
if (!use_host) {
SSL_CTX_free(ssl_ctx);
return -1;
SSL_CTX_free(ssl_ctx);
return -1;
}
SSL_set_tlsext_host_name(ssl, use_host);
free(use_host);
@@ -169,7 +187,10 @@ static int create_ssl_handle(struct esp_tls *tls, const char *hostname, size_t h
return 0;
}
void esp_tls_conn_delete(struct esp_tls *tls)
/**
* @brief Close the TLS connection and free any allocated resources.
*/
void esp_tls_conn_delete(esp_tls_t *tls)
{
if (!tls) {
return;
@@ -186,32 +207,35 @@ void esp_tls_conn_delete(struct esp_tls *tls)
free(tls);
};
static ssize_t tcp_write(struct esp_tls *tls, const char *data, size_t datalen)
static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{
return send(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_write(struct esp_tls *tls, const char *data, size_t datalen)
static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
{
ssize_t ret = SSL_write(tls->ssl, data, datalen);
if (ret < 0) {
int err = SSL_get_error(tls->ssl, ret);
if (err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) {
ESP_LOGE(TAG, "write error :%d:\n", ret);
ESP_LOGE(TAG, "write error :%d:", ret);
}
return -err;
}
return ret;
}
struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg)
/**
* @brief Create a new TLS/SSL connection
*/
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg)
{
int sockfd = esp_tcp_connect(hostname, hostlen, port);
if (sockfd < 0) {
return NULL;
}
struct esp_tls *tls = (struct esp_tls *)calloc(1, sizeof(struct esp_tls));
esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
if (!tls) {
close(sockfd);
return NULL;
@@ -228,6 +252,12 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, st
tls->read = tls_read;
tls->write = tls_write;
}
if(cfg->non_block == true) {
int flags = fcntl(tls->sockfd, F_GETFL, 0);
fcntl(tls->sockfd, F_SETFL, flags | O_NONBLOCK);
}
return tls;
}
@@ -245,7 +275,10 @@ static int get_port(const char *url, struct http_parser_url *u)
return 0;
}
struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg)
/**
* @brief Create a new TLS/SSL connection with a given "HTTP" url
*/
esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
{
/* Parse URI */
struct http_parser_url u;

View File

@@ -1,58 +1,149 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License 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.
#ifndef _ESP_TLS_H_
#define _ESP_TLS_H_
#include <stdbool.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <fcntl.h>
#ifdef __cplusplus
extern "C" {
#endif
struct esp_tls_cfg {
/* If HTTP2/ALPN support is required, a list of protocols that
* should be negotiated. The format is length followed by protocol
* name.
* For the most common cases the following is ok:
* "\x02h2"
* - where the first '2' is the length of the protocol and
* - the subsequent 'h2' is the protocol name
*/
const unsigned char *alpn_protos;
const unsigned char *cacert_pem_buf;
const unsigned int cacert_pem_bytes;
};
/**
* @brief ESP-TLS configuration parameters
*/
typedef struct esp_tls_cfg {
const unsigned char *alpn_protos; /*!< Application protocols required for HTTP2.
If HTTP2/ALPN support is required, a list
of protocols that should be negotiated.
The format is length followed by protocol
name.
For the most common cases the following is ok:
"\x02h2"
- where the first '2' is the length of the protocol and
- the subsequent 'h2' is the protocol name */
const unsigned char *cacert_pem_buf; /*!< Certificate Authority's certificate in a buffer */
const unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate
pointed to by cacert_pem_buf */
const SSL_METHOD *ssl_method; /*!< SSL method that describes internal ssl library
methods/functions which implements the various protocol
versions. If set to NULL, it defaults to
method returned by TLSv1_2_client_method() API. */
bool non_block; /*!< Configure non-blocking mode. If set to true the
underneath socket will be configured in non
blocking mode after tls session is established */
} esp_tls_cfg_t;
struct esp_tls {
SSL_CTX *ctx;
SSL *ssl;
int sockfd;
ssize_t (*read)(struct esp_tls *tls, char *data, size_t datalen);
ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen);
};
/*
*
* cfg: If you wish to open non-TLS connection, keep this NULL. For TLS
* connection, a pass pointer to 'struct esp_tls_cfg'. At a minimum, this
* structure should be zero-initialized.
/**
* @brief ESP-TLS Connection Handle
*/
struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg);
typedef struct esp_tls {
SSL_CTX *ctx; /*!< SSL_CTX object is used to establish
TLS/SSL enabled connection */
SSL *ssl; /*!< SSL object which is needed to hold the data for a
TLS/SSL connection. The new structure inherits the settings of the
underlying context ctx: connection method (SSLv2/v3/TLSv1),
options, verification settings, timeout settings. */
int sockfd; /*!< Underlying socket file descriptor. */
ssize_t (*read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL
connection. */
ssize_t (*write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL
connection. */
} esp_tls_t;
/* Convenience API for HTTP URIs */
struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg);
static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen)
/**
* @brief Create a new TLS/SSL connection
*
* This function establishes a TLS/SSL connection with the specified host.
*
* @param[in] hostname Hostname of the host.
* @param[in] hostlen Length of hostname.
* @param[in] port Port number of the host.
* @param[in] cfg TLS configuration as esp_tls_cfg_t. If you wish to open
* non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to esp_tls_cfg_t. At a minimum, this
* structure should be zero-initialized.
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened.
*/
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg);
/**
* @brief Create a new TLS/SSL connection with a given "HTTP" url
*
* The behaviour is same as esp_tls_conn_new() API. However this API accepts host's url.
*
* @param[in] url url of host.
* @param[in] cfg TLS configuration as esp_tls_cfg_t. If you wish to open
* non-TLS connection, keep this NULL. For TLS connection,
* a pass pointer to 'esp_tls_cfg_t'. At a minimum, this
* structure should be zero-initialized.
* @return pointer to esp_tls_t, or NULL if connection couldn't be opened.
*/
esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg);
/**
* @brief Write from buffer 'data' into specified tls connection.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
* @param[in] data Buffer from which data will be written.
* @param[in] datalen Length of data buffer.
*
* @return
* - >0 if write operation was successful, the return value is the number
* of bytes actually written to the TLS/SSL connection.
* - 0 if write operation was not successful. The underlying
* connection was closed.
* - <0 if write operation was not successful, because either an
* error occured or an action must be taken by the calling process.
*/
static inline ssize_t esp_tls_conn_write(esp_tls_t *tls, const void *data, size_t datalen)
{
return tls->write(tls, data, datalen);
return tls->write(tls, (char *)data, datalen);
}
static inline ssize_t esp_tls_conn_read(struct esp_tls *tls, char *data, size_t datalen)
/**
* @brief Read from specified tls connection into the buffer 'data'.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
* @param[in] data Buffer to hold read data.
* @param[in] datalen Length of data buffer.
*
* @return
* - >0 if read operation was successful, the return value is the number
* of bytes actually read from the TLS/SSL connection.
* - 0 if read operation was not successful. The underlying
* connection was closed.
* - <0 if read operation was not successful, because either an
* error occured or an action must be taken by the calling process.
*/
static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen)
{
return tls->read(tls, data, datalen);
return tls->read(tls, (char *)data, datalen);
}
void esp_tls_conn_delete(struct esp_tls *tls);
/**
* @brief Close the TLS/SSL connection and free any allocated resources.
*
* This function should be called to close each tls connection opened with esp_tls_conn_new() or
* esp_tls_conn_http_new() APIs.
*
* @param[in] tls pointer to esp-tls as esp-tls handle.
*/
void esp_tls_conn_delete(esp_tls_t *tls);
#ifdef __cplusplus
}

View File

@@ -87,6 +87,9 @@ INPUT = \
##
## Protocols - API Reference
##
## ESP-TLS
../../components/esp-tls/esp_tls.h \
## mDNS
../../components/mdns/include/mdns.h \
##
## Storage - API Reference

View File

@@ -0,0 +1,24 @@
ESP-TLS
=======
Overview
--------
The ESP-TLS component provides a simplified API interface for accessing the commonly used TLS functionality.
It supports common scenarios like CA certification validation, SNI, ALPN negotiation, non-blocking connection among others.
All the configuration can be specified in the esp_tls_cfg_t data structure. Once done, TLS communication can be conducted using the following APIs:
* esp_tls_conn_new(): for opening a new TLS connection
* esp_tls_conn_read/write(): for reading/writing from the connection
* esp_tls_conn_delete(): for freeing up the connection
Any application layer protocol like HTTP1, HTTP2 etc can be executed on top of this layer.
Application Example
-------------------
Simple HTTPS example that uses ESP-TLS to establish a secure socket connection: :example:`protocols/https_request`.
API Reference
-------------
.. include:: /_build/inc/esp_tls.inc

View File

@@ -5,6 +5,6 @@ Protocols API
:maxdepth: 1
mDNS <mdns>
ESP-TLS <esp_tls>
Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.

View File

@@ -17,7 +17,6 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <esp_log.h>
@@ -75,7 +74,7 @@ static int do_ssl_connect(struct sh2lib_handle *hd, int sockfd, const char *host
static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data,
size_t length)
{
int rv = esp_tls_conn_write(hd->http2_tls, (const char *)data, (int)length);
int rv = esp_tls_conn_write(hd->http2_tls, data, length);
if (rv <= 0) {
if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) {
rv = NGHTTP2_ERR_WOULDBLOCK;
@@ -279,8 +278,10 @@ static int do_http2_connect(struct sh2lib_handle *hd)
int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
{
memset(hd, 0, sizeof(*hd));
struct esp_tls_cfg tls_cfg;
tls_cfg.alpn_protos = (unsigned char *) "\x02h2";
esp_tls_cfg_t tls_cfg = {
.alpn_protos = (unsigned char *) "\x02h2",
.non_block = true,
};
if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) {
ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed");
goto error;
@@ -292,9 +293,6 @@ int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
goto error;
}
int flags = fcntl(hd->http2_tls->sockfd, F_GETFL, 0);
fcntl(hd->http2_tls->sockfd, F_SETFL, flags | O_NONBLOCK);
return 0;
error:
sh2lib_free(hd);

View File

@@ -14,7 +14,7 @@
#ifndef __ESP_EXAMPLE_SH2_LIB_H_
#define __ESP_EXAMPLE_SH2_LIB_H_
#include <openssl/ssl.h>
#include "esp-tls.h"
#include <nghttp2/nghttp2.h>
/*

View File

@@ -1,5 +1,5 @@
# HTTPS Request Example
Uses an mbedTLS socket to make a very simple HTTPS request over a secure connection, including verifying the server TLS certificate.
Uses APIs from `esp-tls` component to make a very simple HTTPS request over a secure connection, including verifying the server TLS certificate.
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@@ -38,15 +38,6 @@
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include "esp-tls.h"
/* The examples use simple WiFi configuration that you can set via
@@ -144,7 +135,7 @@ static void https_get_task(void *pvParameters)
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to AP");
struct esp_tls_cfg cfg = {
esp_tls_cfg_t cfg = {
.cacert_pem_buf = server_root_cert_pem_start,
.cacert_pem_bytes = server_root_cert_pem_end - server_root_cert_pem_start,
};
@@ -161,13 +152,13 @@ static void https_get_task(void *pvParameters)
size_t written_bytes = 0;
do {
ret = esp_tls_conn_write(tls,
(const char *)REQUEST + written_bytes,
REQUEST + written_bytes,
strlen(REQUEST) - written_bytes);
if (ret >= 0) {
ESP_LOGI(TAG, "%d bytes written", ret);
written_bytes += ret;
} else if (-ret != SSL_ERROR_WANT_WRITE && -ret != SSL_ERROR_WANT_READ) {
ESP_LOGE(TAG, "esp_tls_conn_write returned -0x%x", ret);
} else if (ret != -SSL_ERROR_WANT_WRITE && ret != -SSL_ERROR_WANT_READ) {
ESP_LOGE(TAG, "esp_tls_conn_write returned 0x%x", ret);
goto exit;
}
} while(written_bytes < strlen(REQUEST));
@@ -180,17 +171,12 @@ static void https_get_task(void *pvParameters)
bzero(buf, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len);
if(-ret == SSL_ERROR_WANT_WRITE || -ret == SSL_ERROR_WANT_READ)
if(ret == -SSL_ERROR_WANT_WRITE || ret == -SSL_ERROR_WANT_READ)
continue;
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
ret = 0;
break;
}
if(ret < 0)
{
ESP_LOGE(TAG, "esp_tls_conn_read returned -0x%x", ret);
ESP_LOGE(TAG, "esp_tls_conn_read returned 0x%x", ret);
break;
}
@@ -209,12 +195,6 @@ static void https_get_task(void *pvParameters)
} while(1);
exit:
if(ret != 0)
{
mbedtls_strerror(ret, buf, 100);
ESP_LOGE(TAG, "Last error was: -0x%x - %s", -ret, buf);
}
esp_tls_conn_delete(tls);
putchar('\n'); // JSON output doesn't have a newline at end