This commit is contained in:
pvvx 2017-06-24 05:03:03 +03:00
parent a9ef4ff5c0
commit b8c699eb1c
243 changed files with 1850 additions and 76723 deletions

View file

@ -0,0 +1,216 @@
/**
******************************************************************************
* @file httpc.h
* @author
* @version
* @brief This file provides user interface for HTTP/HTTPS client.
******************************************************************************
* @attention
*
* This module is a confidential and proprietary property of RealTek and possession or use of this module requires written permission of RealTek.
*
* Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved.
******************************************************************************
*/
#ifndef _HTTPC_H_
#define _HTTPC_H_
/** @addtogroup httpc HTTPC
* @ingroup network
* @brief HTTP/HTTPS client functions
* @{
*/
#include "platform_stdlib.h"
#include "platform_opts.h"
#define HTTPC_SECURE_NONE 0 /*!< Running with HTTP client */
#define HTTPC_SECURE_TLS 1 /*!< Running with HTTPS client */
#define HTTPC_DEBUG_OFF 0 /*!< Disable httpc debug log */
#define HTTPC_DEBUG_ON 1 /*!< Enable httpc debug log */
#define HTTPC_DEBUG_VERBOSE 2 /*!< Enable httpc verbose debug log */
#define HTTPC_TLS_POLARSSL 0 /*!< Use PolarSSL for TLS when HTTPS */
#define HTTPC_TLS_MBEDTLS 1 /*!< Use mbedTLS for TLS when HTTPS */
#if CONFIG_USE_POLARSSL
#define HTTPC_USE_TLS HTTPC_TLS_POLARSSL
#elif CONFIG_USE_MBEDTLS
#define HTTPC_USE_TLS HTTPC_TLS_MBEDTLS
#endif
/**
* @brief The structure is the context used for HTTP response header parsing.
* @note Only header string includes string terminator.
*/
struct http_response {
uint8_t *header; /*!< HTTP header string parsed in HTTP response */
size_t header_len; /*!< HTTP header string length */
uint8_t *version; /*!< Pointer to HTTP version in the parsed HTTP header string */
size_t version_len; /*!< HTTP version data length */
uint8_t *status; /*!< Pointer to HTTP status code in the parsed HTTP header string */
size_t status_len; /*!< HTTP status code data length */
uint8_t *content_type; /*!< Pointer to Content-Type header field in the parsed HTTP header string */
size_t content_type_len; /*!< Content-Type header field data length */
size_t content_len; /*!< Value of Content-Length header field parsed in HTTP header string */
};
/**
* @brief The structure is the context used for connection.
*/
struct httpc_conn {
int sock; /*!< Client socket descriptor for connection */
struct http_response response; /*!< Context for HTTP response */
void *tls; /*!< Context for TLS connection */
uint8_t *request_header; /*!< Pointer to transmission buffer of HTTP request header */
char *host; /*!< String of server host name or IP */
uint16_t port; /*!< Service port */
char *user_password; /*!< Base64 string for HTTP basic authorization */
};
/**
* @brief This function is used to generate connection context for an HTTP/HTTPS client.
* @param[in] secure: security mode for HTTP or HTTPS. Must be HTTPD_SECURE_NONE, HTTPD_SECURE_TLS.
* @param[in] client_cert: string of client certificate if required to be verified by server.
* @param[in] client_key: string of client private key if required to be verified by server.
* @param[in] ca_certs: string including certificates in CA trusted chain if want to verify server certificate.
* @return pointer to the generated connection context
*/
struct httpc_conn *httpc_conn_new(uint8_t secure, char *client_cert, char *client_key, char *ca_certs);
/**
* @brief This function is used to deallocate a connection context.
* @param[in] conn: pointer to connection context
* @return None
*/
void httpc_conn_free(struct httpc_conn *conn);
/**
* @brief This function is used to connect to a server.
* @param[in] conn: pointer to connection context
* @param[in] host: string of server host name or IP
* @param[in] port: service port
* @param[in] timeout: connection timeout in seconds
* @return 0 : if successful
* @return -1 : if error occurred
*/
int httpc_conn_connect(struct httpc_conn *conn, char *host, uint16_t port, uint32_t timeout);
/**
* @brief This function is used to close connection from a server.
* @param[in] conn: pointer to connection context
* @return None
*/
void httpc_conn_close(struct httpc_conn *conn);
/**
* @brief This function is used to setup authorization for connection.
* @param[in] conn: pointer to connection context
* @param[in] user: string of user name for authorization
* @param[in] password: string of password for authorization
* @return 0 : if successful
* @return -1 : if error occurred
* @note Must be used before httpc_conn_connect() if basic authorization is used
*/
int httpc_conn_setup_user_password(struct httpc_conn *conn, char *user, char *password);
/**
* @brief This function is used to setup httpc debug.
* @param[in] debug: flag to enable/disable httpc debug. Must be HTTPC_DEBUG_OFF, HTTPC_DEBUG_ON, HTTPC_DEBUG_VERBOSE.
* @return None
*/
void httpc_setup_debug(uint8_t debug);
/**
* @brief This function is used to free memory allocated by httpc API, such as httpc_response_get_header_field().
* @param[in] ptr: pointer to memory to be deallocated
* @return None
*/
void httpc_free(void *ptr);
/**
* @brief This function is used to dump the parsed HTTP header of response in connection context.
* @param[in] conn: pointer to connection context
* @return None
*/
void httpc_conn_dump_header(struct httpc_conn *conn);
/**
* @brief This function is used to start a HTTP request in connection.
* @param[in] conn: pointer to connection context
* @param[in] method: string of HTTP method in HTTP request
* @param[in] resource: string including path and query string to identify a resource
* @param[in] content_type: string of Content-Type header field written to HTTP request. No Content-Type in HTTP request if NULL.
* @param[in] content_len: value of Content-Length header field written to HTTP request. No Content-Length in HTTP request if NULL.
* @return 0 : if successful
* @return -1 : if error occurred
*/
int httpc_request_write_header_start(struct httpc_conn *conn, char *method, char *resource, char *content_type, size_t content_len);
/**
* @brief This function is used to add an HTTP header field to HTTP request.
* @param[in] conn: pointer to connection context
* @param[in] name: HTTP header field name string
* @param[in] value: HTTP header field value
* @return 0 : if successful
* @return -1 : if error occurred
*/
int httpc_request_write_header(struct httpc_conn *conn, char *name, char *value);
/**
* @brief This function is used to write HTTP request header data to connection.
* @param[in] conn: pointer to connection context
* @return return value of lwip socket write() for HTTP and PolarSSL ssl_write() for HTTPS
*/
int httpc_request_write_header_finish(struct httpc_conn *conn);
/**
* @brief This function is used to write HTTP request body data to connection.
* @param[in] conn: pointer to connection context
* @param[in] data: data to be written
* @param[in] data_len: data length
* @return return value of lwip socket write() for HTTP and PolarSSL ssl_write() for HTTPS
*/
int httpc_request_write_data(struct httpc_conn *conn, uint8_t *data, size_t data_len);
/**
* @brief This function is used to check HTTP status of response in connection context.
* @param[in] conn: pointer to connection context
* @param[in] status: HTTP status string to compare with
* @return 0 : if different
* @return 1 : if matched
*/
int httpc_response_is_status(struct httpc_conn *conn, char *status);
/**
* @brief This function is used to read HTTP header from client socket descriptor and parse content to connection context.
* @param[in] conn: pointer to connection context
* @return 0 : if successful
* @return -1 : if error occurred
*/
int httpc_response_read_header(struct httpc_conn *conn);
/**
* @brief This function is used to read data from HTTP/HTTPS connection.
* @param[in] conn: pointer to connection context
* @param[out] data: buffer for data read
* @param[in] data_len: buffer length
* @return return value of lwip socket read() for HTTP and PolarSSL ssl_read() for HTTPS
*/
int httpc_response_read_data(struct httpc_conn *conn, uint8_t *data, size_t data_len);
/**
* @brief This function is used to get a header field from HTTP header of connection context.
* @param[in] conn: pointer to connection context
* @param[in] field: header field string to search
* @param[out] value: search result stored in memory allocated
* @return 0 : if found
* @return -1 : if not found
* @note The search result memory should be free by httpc_free().
*/
int httpc_response_get_header_field(struct httpc_conn *conn, char *field, char **value);
/*\@}*/
#endif /* _HTTPC_H_ */

View file

@ -0,0 +1,355 @@
#include "FreeRTOS.h"
#include "task.h"
#include "platform_stdlib.h"
#include "httpc.h"
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
#include "polarssl/ssl.h"
#include "polarssl/memory.h"
#include "polarssl/base64.h"
struct httpc_tls {
ssl_context ctx; /*!< Context for PolarSSL */
x509_crt ca; /*!< CA certificates */
x509_crt cert; /*!< Certificate */
pk_context key; /*!< Private key */
};
static int _verify_func(void *data, x509_crt *crt, int depth, int *flags)
{
char buf[1024];
x509_crt_info(buf, sizeof(buf) - 1, "", crt);
if(*flags)
printf("\n[HTTPC] ERROR: certificate verify\n%s\n", buf);
else
printf("\n[HTTPC] Certificate verified\n%s\n", buf);
return 0;
}
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
#include "mbedtls/ssl.h"
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/base64.h"
struct httpc_tls {
mbedtls_ssl_context ctx; /*!< Context for mbedTLS */
mbedtls_ssl_config conf; /*!< Configuration for mbedTLS */
mbedtls_x509_crt ca; /*!< CA certificates */
mbedtls_x509_crt cert; /*!< Certificate */
mbedtls_pk_context key; /*!< Private key */
};
static int _verify_func(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
char buf[1024];
mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
if(*flags)
printf("\n[HTTPC] ERROR: certificate verify\n%s\n", buf);
else
printf("\n[HTTPC] Certificate verified\n%s\n", buf);
return 0;
}
static void* _calloc_func(size_t nmemb, size_t size)
{
size_t mem_size;
void *ptr = NULL;
mem_size = nmemb * size;
ptr = pvPortMalloc(mem_size);
if(ptr)
memset(ptr, 0, mem_size);
return ptr;
}
#endif /* HTTPC_USE_POLARSSL */
static int _random_func(void *p_rng, unsigned char *output, size_t output_len)
{
rtw_get_random_bytes(output, output_len);
return 0;
}
void *httpc_tls_new(int *sock, char *client_cert, char *client_key, char *ca_certs)
{
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
int ret = 0;
struct httpc_tls *tls = NULL;
memory_set_own(pvPortMalloc, vPortFree);
tls = (struct httpc_tls *) malloc(sizeof(struct httpc_tls));
if(tls) {
ssl_context *ssl = &tls->ctx;
memset(tls, 0, sizeof(struct httpc_tls));
x509_crt_init(&tls->ca);
x509_crt_init(&tls->cert);
pk_init(&tls->key);
ssl_init(ssl);
ssl_set_endpoint(ssl, SSL_IS_CLIENT);
ssl_set_authmode(ssl, SSL_VERIFY_NONE);
ssl_set_rng(ssl, _random_func, NULL);
ssl_set_bio(ssl, net_recv, sock, net_send, sock);
if(client_cert && client_key) {
if((ret = x509_crt_parse(&tls->cert, (const unsigned char *) client_cert, strlen(client_cert))) != 0) {
printf("\n[HTTPC] ERROR: x509_crt_parse %d\n", ret);
ret = -1;
goto exit;
}
if((ret = pk_parse_key(&tls->key, (const unsigned char *) client_key, strlen(client_key), NULL, 0)) != 0) {
printf("\n[HTTPC] ERROR: pk_parse_key %d\n", ret);
ret = -1;
goto exit;
}
if((ret = ssl_set_own_cert(ssl, &tls->cert, &tls->key)) != 0) {
printf("\n[HTTPC] ERROR: ssl_set_own_cert %d\n", ret);
ret = -1;
goto exit;
}
}
if(ca_certs) {
// set trusted ca certificates next to client certificate
if((ret = x509_crt_parse(&tls->ca, (const unsigned char *) ca_certs, strlen(ca_certs))) != 0) {
printf("\n[HTTPC] ERROR: x509_crt_parse %d\n", ret);
ret = -1;
goto exit;
}
ssl_set_ca_chain(ssl, &tls->ca, NULL, NULL);
ssl_set_authmode(ssl, SSL_VERIFY_REQUIRED);
ssl_set_verify(ssl, _verify_func, NULL);
}
}
else {
printf("\n[HTTPC] ERROR: malloc\n");
ret = -1;
goto exit;
}
exit:
if(ret && tls) {
ssl_free(&tls->ctx);
x509_crt_free(&tls->ca);
x509_crt_free(&tls->cert);
pk_free(&tls->key);
free(tls);
tls = NULL;
}
return (void *) tls;
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
int ret = 0;
struct httpc_tls *tls = NULL;
mbedtls_platform_set_calloc_free(_calloc_func, vPortFree);
tls = (struct httpc_tls *) malloc(sizeof(struct httpc_tls));
if(tls) {
mbedtls_ssl_context *ssl = &tls->ctx;
mbedtls_ssl_config *conf = &tls->conf;
memset(tls, 0, sizeof(struct httpc_tls));
mbedtls_x509_crt_init(&tls->ca);
mbedtls_x509_crt_init(&tls->cert);
mbedtls_pk_init(&tls->key);
mbedtls_ssl_init(ssl);
mbedtls_ssl_config_init(conf);
if((ret = mbedtls_ssl_config_defaults(conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_ssl_config_defaults %d\n", ret);
ret = -1;
goto exit;
}
mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_rng(conf, _random_func, NULL);
if(client_cert && client_key) {
if((ret = mbedtls_x509_crt_parse(&tls->cert, (const unsigned char *) client_cert, strlen(client_cert) + 1)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_x509_crt_parse %d\n", ret);
ret = -1;
goto exit;
}
if((ret = mbedtls_pk_parse_key(&tls->key, (const unsigned char *) client_key, strlen(client_key) + 1, NULL, 0)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_pk_parse_key %d\n", ret);
ret = -1;
goto exit;
}
if((ret = mbedtls_ssl_conf_own_cert(conf, &tls->cert, &tls->key)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_ssl_conf_own_cert %d\n", ret);
ret = -1;
goto exit;
}
}
if(ca_certs) {
// set trusted ca certificates next to client certificate
if((ret = mbedtls_x509_crt_parse(&tls->ca, (const unsigned char *) ca_certs, strlen(ca_certs) + 1)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_x509_crt_parse %d\n", ret);
ret = -1;
goto exit;
}
mbedtls_ssl_conf_ca_chain(conf, &tls->ca, NULL);
mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_verify(conf, _verify_func, NULL);
}
if((ret = mbedtls_ssl_setup(ssl, conf)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_ssl_setup %d\n", ret);
ret = -1;
goto exit;
}
mbedtls_ssl_set_bio(ssl, sock, mbedtls_net_send, mbedtls_net_recv, NULL);
}
else {
printf("\n[HTTPC] ERROR: malloc\n");
ret = -1;
goto exit;
}
exit:
if(ret && tls) {
mbedtls_ssl_free(&tls->ctx);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_x509_crt_free(&tls->ca);
mbedtls_x509_crt_free(&tls->cert);
mbedtls_pk_free(&tls->key);
free(tls);
tls = NULL;
}
return (void *) tls;
#endif
}
void httpc_tls_free(void *tls_in)
{
struct httpc_tls *tls = (struct httpc_tls *) tls_in;
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
ssl_free(&tls->ctx);
x509_crt_free(&tls->ca);
x509_crt_free(&tls->cert);
pk_free(&tls->key);
free(tls);
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
mbedtls_ssl_free(&tls->ctx);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_x509_crt_free(&tls->ca);
mbedtls_x509_crt_free(&tls->cert);
mbedtls_pk_free(&tls->key);
free(tls);
#endif
}
int httpc_tls_handshake(void *tls_in, char *host)
{
struct httpc_tls *tls = (struct httpc_tls *) tls_in;
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
int ret = 0;
ssl_set_hostname(&tls->ctx, host);
if((ret = ssl_handshake(&tls->ctx)) != 0) {
printf("\n[HTTPC] ERROR: ssl_handshake %d\n", ret);
ret = -1;
}
else {
printf("\n[HTTPC] Use ciphersuite %s\n", ssl_get_ciphersuite(&tls->ctx));
}
return ret;
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
int ret = 0;
mbedtls_ssl_set_hostname(&tls->ctx, host);
if((ret = mbedtls_ssl_handshake(&tls->ctx)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_ssl_handshake %d\n", ret);
ret = -1;
}
else {
printf("\n[HTTPC] Use ciphersuite %s\n", mbedtls_ssl_get_ciphersuite(&tls->ctx));
}
return ret;
#endif
}
void httpc_tls_close(void *tls_in)
{
struct httpc_tls *tls = (struct httpc_tls *) tls_in;
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
ssl_close_notify(&tls->ctx);
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
mbedtls_ssl_close_notify(&tls->ctx);
#endif
}
int httpc_tls_read(void *tls_in, uint8_t *buf, size_t buf_len)
{
struct httpc_tls *tls = (struct httpc_tls *) tls_in;
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
return ssl_read(&tls->ctx, buf, buf_len);
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
return mbedtls_ssl_read(&tls->ctx, buf, buf_len);
#endif
}
int httpc_tls_write(void *tls_in, uint8_t *buf, size_t buf_len)
{
struct httpc_tls *tls = (struct httpc_tls *) tls_in;
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
return ssl_write(&tls->ctx, buf, buf_len);
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
return mbedtls_ssl_write(&tls->ctx, buf, buf_len);
#endif
}
int httpc_base64_encode(uint8_t *data, size_t data_len, char *base64_buf, size_t buf_len)
{
#if (HTTPC_USE_TLS == HTTPC_TLS_POLARSSL)
int ret = 0;
if((ret = base64_encode(base64_buf, &buf_len, data, data_len)) != 0) {
printf("\n[HTTPC] ERROR: base64_encode %d\n", ret);
ret = -1;
}
return ret;
#elif (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS)
int ret = 0;
size_t output_len = 0;
if((ret = mbedtls_base64_encode(base64_buf, buf_len, &output_len, data, data_len)) != 0) {
printf("\n[HTTPC] ERROR: mbedtls_base64_encode %d\n", ret);
ret = -1;
}
return ret;
#endif
}