#include "mbedtls/ssl.h" #include "mbedtls/x509_crl.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/net_sockets.h" #include "mbedtls/platform.h" //#include //#include //#include #include //#include /*********************************tls begin**************************************/ #include #include #include #include #include #include #include "httpc.h" #define TAG "httpc" #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) { /* To avoid gcc warnings */ ( void ) data; ( void ) depth; 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 = os_malloc(mem_size); if(ptr) memset(ptr, 0, mem_size); return ptr; } #endif /* HTTPC_USE_POLARSSL */ static int httpc_get_random(unsigned char *buf, size_t sz) { int r; int len; while (sz > 0) { len = sizeof(r) > sz ? sz : sizeof(r); r = rand(); os_memcpy(buf, &r, len); buf += len; sz -= len; } return 0; } static int _random_func(void *p_rng, unsigned char *output, size_t output_len) { /* To avoid gcc warnings */ ( void ) p_rng; httpc_get_random(output, 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); if((ret = ssl_init(ssl))!=0){ printf("\n[HTTPC] ERROR: ssl_init %d\n", ret); ret = -1; goto exit; } 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, free); 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_OPTIONAL); 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((unsigned char*)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 } /***********************************************tls end****************************************************/ #define RCV_HEADER_SIZE 5*1024 #define RCV_BUFF_SIZE 5*1024 #define SEND_HEADER_SIZE 1024 struct httpc_conn *httpc_conn_new(uint8_t secure, char *client_cert, char *client_key, char *ca_certs) { struct httpc_conn *conn = malloc(sizeof(struct httpc_conn)); if (conn == NULL) { BK_LOGE(TAG,"%s: create failed, no memory!\r\n",__func__); return NULL; } // Initialize other members conn->response.header = NULL; conn->response.header_len = 0; conn->response.version = NULL; conn->response.version_len = 0; conn->response.status = NULL; conn->response.status_len = 0; conn->response.content_type = NULL; conn->response.content_type_len = 0; conn->response.content_len = 0; conn->request_header = NULL; conn->host = NULL; conn->port = 0; conn->user_password = NULL; if (secure) { struct httpc_tls *tls = (struct httpc_tls *) httpc_tls_new(&conn->sock, client_cert, client_key, ca_certs); if(tls == NULL) { BK_LOGE(TAG, "FAIL to httpc_tls_new\r\n"); } conn->tls = tls; return conn; } else { conn->tls = NULL; } return conn; } struct timeval* bk_utils_ms_to_timeval(int timeout_ms, struct timeval *tv) { if (timeout_ms == -1) { return NULL; } tv->tv_sec = timeout_ms / 1000; tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000; return tv; } static void bk_httpc_hex_dump(char *s, int length) { BK_LOGE(TAG, "begin to print (length=%d):\r\n", length); for (int index = 0; index < length; index++) { BK_LOG_RAW("%c", s[index]); } os_printf("\r\n"); } static bk_err_t httpc_tls_hostname_to_fd(const char *host, size_t hostlen, int port, struct sockaddr_storage *address, int* fd) { struct addrinfo *address_info; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; char *use_host = strndup(host, hostlen); if (!use_host) { return -1; } BK_LOGD(TAG, "host:%s: strlen %lu\r\n", use_host, (unsigned long)hostlen); int res = getaddrinfo(use_host, NULL, &hints, &address_info); if (res != 0 || address_info == NULL) { BK_LOGE(TAG, "couldn't get hostname for :%s: " "getaddrinfo() returns %d, addrinfo=%p\r\n", use_host, res, address_info); free(use_host); return -1; } free(use_host); *fd = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol); if (*fd < 0) { BK_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)\r\n", address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol); freeaddrinfo(address_info); return -1; } if (address_info->ai_family == AF_INET) { struct sockaddr_in *p = (struct sockaddr_in *)address_info->ai_addr; p->sin_port = htons(port); BK_LOGE(TAG, "[sock=%d] Resolved IPv4 address: %s\r\n", *fd, ipaddr_ntoa((const ip_addr_t*)&p->sin_addr.s_addr)); memcpy(address, p, sizeof(struct sockaddr )); } #if CONFIG_LWIP_IPV6 else if (address_info->ai_family == AF_INET6) { struct sockaddr_in6 *p = (struct sockaddr_in6 *)address_info->ai_addr; p->sin6_port = htons(port); p->sin6_family = AF_INET6; BK_LOGD(TAG, "[sock=%d] Resolved IPv6 address: %s\r\n", *fd, ip6addr_ntoa((const ip6_addr_t*)&p->sin6_addr)); memcpy(address, p, sizeof(struct sockaddr_in6 )); } #endif else { BK_LOGE(TAG, "Unsupported protocol family %d\r\n", address_info->ai_family); close(*fd); freeaddrinfo(address_info); return -1; } freeaddrinfo(address_info); return BK_OK; } int httpc_conn_connect(struct httpc_conn *conn, char *host, uint16_t port, uint32_t timeout) { conn->port = port; conn->host = os_strdup(host); struct sockaddr_storage address; if (port == 443) { bk_err_t ret = httpc_tls_hostname_to_fd(host, strlen(host), port, &address, &conn->sock); BK_LOGE(TAG, "[sock=%d] Connecting to server. HOST: %s, Port: %d ret: %d\r\n", conn->sock, host, port, ret); ret = connect(conn->sock, (struct sockaddr *)&address, sizeof(struct sockaddr)); if (ret < 0) { BK_LOGE(TAG, "Fail to connect ret:%d\r\n", ret); return -1; } else BK_LOGE(TAG, "success to connect ret:%d\r\n", ret); ret = httpc_tls_handshake(conn->tls, host); if (ret < 0) { BK_LOGE(TAG, "Fail to handshake ret:%d\r\n", ret); return -1; } else BK_LOGE(TAG, "success to handshake ret:%d\r\n", ret); } #if 1 struct timeval tv; bk_utils_ms_to_timeval(timeout, &tv); if (setsockopt(conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { BK_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO\r\n"); return -1; } if (setsockopt(conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { BK_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO\r\n"); return -1; } #endif return BK_OK; } void httpc_conn_free(struct httpc_conn *conn) { if (!conn) return; if (conn->tls) { httpc_tls_free(conn->tls); } if (conn->sock >= 0) { closesocket(conn->sock); conn->sock = -1; } return; } void httpc_conn_close(struct httpc_conn *conn) { if (conn && conn->tls) { httpc_tls_close(conn->tls); } return; } void httpc_free(void *ptr) { struct httpc_conn *conn = (struct httpc_conn *)ptr; if (!conn) return; if (conn->request_header) { os_free(conn->request_header); conn->request_header = NULL; } if (conn->response.header) { os_free(conn->response.header); conn->response.header = NULL; } if (conn->response.version) { os_free(conn->response.version); conn->response.version = NULL; } if (conn->response.status) { os_free(conn->response.status); conn->response.status = NULL; } if (conn->response.content_type) { os_free(conn->response.content_type); conn->response.content_type = NULL; } if (conn->host) { os_free(conn->host); conn->host = NULL; } if (conn) { os_free(conn); conn = NULL; } return; } int httpc_conn_setup_user_password_v1(struct httpc_conn *conn, char *user, char *password) { if (user == NULL || password == NULL) { return -1; } if (!conn) return -1; size_t user_password_len = strlen(user) + strlen(password) + 2; conn->user_password = (char *)malloc(user_password_len); if (conn->user_password == NULL) { BK_LOGE(TAG,"%s: create failed, no memory!\r\n",__func__); return -1; } snprintf(conn->user_password, user_password_len, "%s:%s", user, password); return 0; } int httpc_conn_setup_user_password_v2(struct httpc_conn *conn, char *user, char *password) { int out; char *user_info = NULL; char *digest = NULL; size_t n = 0; asprintf(&user_info, "%s:%s", user, password); mbedtls_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info)); digest = calloc(1, 6 + n + 1); strcpy(digest, "Basic "); mbedtls_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info)); if (!conn) { os_free(digest); return -1; } conn->user_password = (char *)malloc(n); if (conn->user_password == NULL) { BK_LOGE(TAG,"%s: create failed, no memory!\r\n",__func__); os_free(digest); os_free(conn->user_password); return -1; } snprintf(conn->user_password, n, "%s", (unsigned char *)digest + 6); os_free(digest); os_free(conn->user_password); /*TODO more*/ return 0; } void httpc_setup_debug(uint8_t debug) { if (debug) bk_printf("%s\r\n", __func__); } void httpc_conn_dump_header(struct httpc_conn *conn) { BK_LOGE(TAG, "HTTP Version: %s %d\n", conn->response.version, (int) strtol((char *)conn->response.version, NULL, 10)); BK_LOGE(TAG, "Status: %s\n", conn->response.status); BK_LOGE(TAG, "Content-Type: %s\n", conn->response.content_type); BK_LOGE(TAG, "Content-Length: %d\n", conn->response.content_len); } int httpc_request_write_header_start(struct httpc_conn *conn, char *method, char *resource, char *content_type, size_t content_len) { conn->request_header = (uint8_t *)malloc(SEND_HEADER_SIZE); if (conn->request_header == NULL) { BK_LOGE(TAG,"%s: create failed, no memory!\r\n",__func__); return -1; } if (resource) snprintf((char *)conn->request_header, SEND_HEADER_SIZE, "%s %s HTTP/1.1\r\n", method, resource); else snprintf((char *)conn->request_header, SEND_HEADER_SIZE, "%s / HTTP/1.1\r\n", method); if (content_type != NULL) { snprintf((char *)conn->request_header + strlen((char *)conn->request_header), SEND_HEADER_SIZE - strlen((char *)conn->request_header), "Content-Type: %s\r\n", content_type); } if (content_len > 0) { snprintf((char *)conn->request_header + strlen((char *)conn->request_header), SEND_HEADER_SIZE - strlen((char *)conn->request_header), "Content-Length: %zu\r\n", content_len); } snprintf((char *)conn->request_header + strlen((char *)conn->request_header), SEND_HEADER_SIZE - strlen((char *)conn->request_header), "Host: %s\r\n", conn->host); return 0; } int httpc_request_write_header(struct httpc_conn *conn, char *name, char *value) { snprintf((char *)conn->request_header + strlen((char *)conn->request_header), SEND_HEADER_SIZE - strlen((char *)conn->request_header), "%s: %s\r\n", name, value); return 0; } int httpc_request_write_header_finish(struct httpc_conn *conn) { snprintf((char *)conn->request_header + strlen((char *)conn->request_header), SEND_HEADER_SIZE - strlen((char *)conn->request_header), "\r\n"); int bytes_written = httpc_tls_write(conn->tls, conn->request_header, strlen((char *)conn->request_header)); if (bytes_written < 0) { BK_LOGE(TAG,"%s: create failed, no memory!\r\n",__func__); free(conn->request_header); return -1; } else BK_LOGE(TAG,"write_header_finish, bytes_written:%d\r\n", bytes_written); if(conn->request_header) { os_free(conn->request_header); conn->request_header = NULL; } return bytes_written; } static int httpc_ssl_base_poll_read(struct httpc_conn *conn, int timeout_ms) { int ret = -1; int remain = 0; struct timeval timeout; struct httpc_tls *tls = (struct httpc_tls *) conn->tls; fd_set readset; fd_set errset; FD_ZERO(&readset); FD_ZERO(&errset); FD_SET(conn->sock, &readset); FD_SET(conn->sock, &errset); if (tls && (remain = mbedtls_ssl_get_bytes_avail(&tls->ctx)) > 0) { BK_LOGD(TAG, "remain data in cache, need to read again\r\n"); return remain; } else BK_LOGD(TAG, "NO data available\r\n"); ret = select(conn->sock + 1, &readset, NULL, &errset, bk_utils_ms_to_timeval(timeout_ms, &timeout)); if (ret > 0 && FD_ISSET(conn->sock, &errset)) { int sock_errno = 0; uint32_t optlen = sizeof(sock_errno); getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); BK_LOGE(TAG, "poll_read select error %d, errno = %s, fd = %d\r\n", sock_errno, strerror(sock_errno), conn->sock); ret = -1; } return ret; } static int httpc_ssl_base_poll_write(struct httpc_conn *conn, int timeout_ms) { int ret = -1; struct timeval timeout; fd_set writeset; fd_set errset; FD_ZERO(&writeset); FD_ZERO(&errset); FD_SET(conn->sock, &writeset); FD_SET(conn->sock, &errset); ret = select(conn->sock + 1, &writeset, NULL, &errset, bk_utils_ms_to_timeval(timeout_ms, &timeout)); if (ret > 0 && FD_ISSET(conn->sock, &errset)) { int sock_errno = 0; uint32_t optlen = sizeof(sock_errno); getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); BK_LOGE(TAG, "poll_write select error %d, errno = %s, fd = %d\r\n", sock_errno, strerror(sock_errno), conn->sock); ret = -1; } return ret; } int httpc_request_write_data(struct httpc_conn *conn, uint8_t *data, size_t data_len) { int bytes_write = 0; int total_write = 0; int left = data_len; BK_ASSERT(conn); if (conn->sock < 0) { return -1; } if (data_len == 0) { return 0; } /* send all of data on the buffer. */ do { bytes_write = httpc_tls_write(conn->tls, (void *)((char *)data + total_write), left); if (bytes_write <= 0) { #if (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS) if(conn->tls && (bytes_write == MBEDTLS_ERR_SSL_WANT_READ || bytes_write == MBEDTLS_ERR_SSL_WANT_WRITE)) { continue; } #endif if (errno == EWOULDBLOCK || errno == EAGAIN) { /* send timeout */ if (total_write) { return total_write; } continue; /* TODO: whether return the TIMEOUT*/ } else { closesocket(conn->sock); conn->sock = -1; if (total_write == 0) { return -1; } break; } } left -= bytes_write; total_write += bytes_write; BK_LOGE(TAG, "%s, total_write: %d\r\n", __func__, total_write); } while (left); return total_write; } int httpc_response_is_status(struct httpc_conn *conn, char *status) { if (os_strncmp((char *)conn->response.status, status, conn->response.status_len) == 0) { return 1; } else return 0; } int httpc_response_get_header_field(struct httpc_conn *conn, char *field, char **value) { char *field_start, *value_start, *value_end; char * header_begin = (char *)conn->response.header; field_start = strstr(header_begin, field); if (field_start == NULL) { conn->response.header = (uint8_t *)header_begin; BK_LOGE(TAG,"%s: %d no such field\r\n",__func__, __LINE__); return -1; } value_start = strchr(field_start, ':'); if (value_start == NULL) { conn->response.header = (uint8_t *)header_begin; BK_LOGE(TAG,"%s: %d this field no value\r\n",__func__, __LINE__); return -1; } value_start += 2; value_end = strstr(value_start, "\n"); if (value_end == NULL) { conn->response.header = (uint8_t *)header_begin; BK_LOGE(TAG,"%s LINE:%d value_end null\r\n", __func__, __LINE__); return -1; } int value_length = value_end - value_start; *value = malloc(value_length + 1); if (*value == NULL) { conn->response.header = (uint8_t *)header_begin; BK_LOGE(TAG,"%d alloc fail\r\n", __LINE__); return -1; } strncpy(*value, value_start, value_length); (*value)[value_length] = '\0'; conn->response.header = (uint8_t *)header_begin; return 0; } static int httpc_read_line(struct httpc_conn *conn, char *buffer, int size) { int rc, count = 0; char ch = 0, last_ch = 0; struct httpc_tls *tls = (struct httpc_tls *) conn->tls; BK_ASSERT(conn); BK_ASSERT(buffer); while (count < size) { rc = httpc_tls_read(tls, (unsigned char *) &ch, 1); #if (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS) if (tls && (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { continue; } #endif if (rc <= 0) return rc; if (ch == '\n' && last_ch == '\r') break; buffer[count++] = ch; last_ch = ch; } if (count > size) { BK_LOGE(TAG,"read line failed. The line data length is out of buffer size(%d)!\r\n", count); return -1; } return count; } int httpc_response_read_header(struct httpc_conn *conn) { int rc; char *mime_buffer = NULL; conn->response.header = malloc(RCV_HEADER_SIZE); if (conn->response.header == NULL) { BK_LOGE(TAG," create failed, no memory for session header!\r\n"); return -1; } while (1) { mime_buffer = (char *)conn->response.header + conn->response.header_len; rc = httpc_read_line(conn, mime_buffer, RCV_HEADER_SIZE - conn->response.header_len); if (rc < 0) break; if (rc == 0) break; if ((rc == 1) && (mime_buffer[0] == '\r')) { mime_buffer[0] = '\0'; break; } mime_buffer[rc - 1] = '\n'; conn->response.header_len += rc; if (conn->response.header_len >= RCV_HEADER_SIZE) { BK_LOGE(TAG,"not enough header buffer size(%d)!\r\n", RCV_HEADER_SIZE); return -1; } } conn->response.header = (uint8_t *)mime_buffer - conn->response.header_len; BK_LOGE(TAG,"conn->response.header_len:%d\r\n", conn->response.header_len); //bk_httpc_hex_dump((char *)conn->response.header, conn->response.header_len); httpc_response_get_header_field(conn, "Content-Type", (char **)&conn->response.content_type); char *content_len = NULL; httpc_response_get_header_field(conn, "Content-Length", &content_len); if (content_len) { conn->response.content_len = atoi(content_len); os_free(content_len); content_len = NULL; } int i = 0; char * header_v = (char *)conn->response.header; char * header_s = (char *)conn->response.header; char * header_temp = (char *)conn->response.header; if (strstr(header_v, "HTTP/1.")) { char *ptr = header_v; ptr += strlen("HTTP/1."); while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; for (i = 0; ((ptr[i] != ' ') && (ptr[i] != '\t')); i++); ptr[i] = '\0'; conn->response.version = (uint8_t *)os_strdup(ptr); } if (strstr(header_s, "HTTP/1.")) { char *ptr = header_s; int len=0; ptr += strlen("HTTP/1.x "); // while (*ptr && (*ptr == ' ' || *ptr == '\t') && (*ptr != '\n')) { while (*ptr && (*ptr != '\n')) { ptr++; len++; } conn->response.status = os_malloc(len + 1); os_memset(conn->response.status, 0, len + 1); if (len && ptr) os_memcpy(conn->response.status, ptr-len, len); conn->response.status[len] = '\0'; //conn->response.status = (uint8_t *)os_strdup(ptr); } conn->response.header = (uint8_t *)header_temp; return 0; } int httpc_response_read_data(struct httpc_conn *conn, uint8_t *data, size_t data_len) { int bytes_read = 0; int total_read = 0; int left; BK_ASSERT(conn); if (conn->sock < 0) { return -1; } if (data_len == 0) { return 0; } if (conn->response.content_len < 0) { return -1; } left = data_len; do { int poll=0; /* * Read until: there is an error, we've read "size" bytes or the remote * side has closed the connection. */ if ((poll = httpc_ssl_base_poll_read(conn, 10)) <= 0) { BK_LOGE(TAG, "ssl_base_poll_read no data already\r\n"); return total_read;; } else BK_LOGD(TAG, "ssl_base_poll_read:%d\r\n", poll); bytes_read = httpc_tls_read(conn->tls, (void *)((char *)data + total_read), left); if (bytes_read <= 0) { BK_LOGE(TAG,"%s bytes_read <=0 :%d\r\n", __func__, bytes_read); #if (HTTPC_USE_TLS == HTTPC_TLS_MBEDTLS) if(conn->tls && (bytes_read == MBEDTLS_ERR_SSL_WANT_READ || bytes_read == MBEDTLS_ERR_SSL_WANT_WRITE)) { continue; } #endif BK_LOGE(TAG,"receive data error(%d).\r\n", bytes_read); if (total_read) { break; } else { if (errno == EWOULDBLOCK || errno == EAGAIN) { /* recv timeout */ BK_LOGI(TAG,"receive data timeout.\r\n"); return -1; } else { closesocket(conn->sock); conn->sock = -1; return 0; } } } left -= bytes_read; total_read += bytes_read; BK_LOGE(TAG,"%s bytes_read:%d total_read:%d\r\n", __func__, bytes_read, total_read); } while (left); return total_read; } #if 0 /******************************************************demo*****************************************************/ #define USE_HTTPS 1 #define SERVER_HOST "192.168.32.250"//"www.rt-thread.com"//"www.baidu.com" static void example_httpc_post(char *url) { BK_LOGE(TAG, "------Example: HTTPC post------\r\n"); struct httpc_conn *conn = NULL; #if 1 const char *host_addr = 0; const char *path_ptr; const char *request; char *req_url = NULL; const char *port_ptr; char port_str[6] = "80"; int host_addr_len = 0; int url_len = strlen(url); if (strncmp(url, "http://", 7) == 0) { host_addr = url + 7; } else if (strncmp(url, "https://", 8) == 0) { host_addr = url + 8; } else { return; } path_ptr = strstr(host_addr, "/"); request = path_ptr ? path_ptr : "/"; if (request) { req_url = strdup(request); } port_ptr = strstr(host_addr + host_addr_len, ":"); if (port_ptr && path_ptr && (port_ptr < path_ptr)) { int port_len = path_ptr - port_ptr - 1; strncpy(port_str, port_ptr + 1, port_len); port_str[port_len] = '\0'; } if (port_ptr && (!path_ptr)) { strcpy(port_str, port_ptr + 1); } if (!host_addr_len) { if ((port_ptr && path_ptr && (port_ptr < path_ptr) )|| (port_ptr && (!path_ptr))) { host_addr_len = port_ptr - host_addr; } else if (path_ptr) { host_addr_len = path_ptr - host_addr; } else { host_addr_len = strlen(host_addr); } } if ((host_addr_len < 1) || (host_addr_len > url_len)) { return; } char *host_addr_new = os_malloc(host_addr_len + 1); if (!host_addr_new) { return; } memcpy(host_addr_new, host_addr, host_addr_len); host_addr_new[host_addr_len] = '\0'; BK_LOGE(TAG,"host_addr_new:%s port_str:%d\r\n", host_addr_new, port_str); #endif #if USE_HTTPS conn = httpc_conn_new(HTTPC_SECURE_TLS, NULL, NULL, NULL); #else conn = httpc_conn_new(HTTPC_SECURE_NONE, NULL, NULL, NULL); #endif if(conn) { #if USE_HTTPS if(httpc_conn_connect(conn, host_addr_new, 443, 0) == 0) { #else if(httpc_conn_connect(conn, SERVER_HOST, 80, 0) == 0) { #endif /* HTTP POST request */ char *post_data = "param1=test_data1¶m2=test_data2"; BK_LOGE(TAG,"----------------BEGIN WRITE HEADER------------\r\n"); // start a header and add Host (added automatically), Content-Type and Content-Length (added by input param) httpc_request_write_header_start(conn, "POST", req_url, NULL, strlen(post_data));//"/post" // add other header fields if necessary //httpc_request_write_header(conn, "Connection", "close"); // finish and send header httpc_request_write_header_finish(conn); // send http body httpc_request_write_data(conn, (uint8_t *)post_data, strlen(post_data)); BK_LOGE(TAG,"----------------BEGIN READ HEADER------------\r\n"); // receive response header if(httpc_response_read_header(conn) == 0) { httpc_conn_dump_header(conn); // receive response body if(httpc_response_is_status(conn, "200 OK")) { //uint8_t buf[RCV_BUFF_SIZE]; uint8_t *buf = (uint8_t *)malloc(RCV_BUFF_SIZE); int read_size = 0, total_size = 0; BK_LOGE(TAG,"----------------BEGIN READ PAYLOAD---------------\r\n"); while(1) { memset(buf, 0, RCV_BUFF_SIZE); read_size = httpc_response_read_data(conn, buf, RCV_BUFF_SIZE - 1); if(read_size > 0) { total_size += read_size; bk_httpc_hex_dump((char *)buf, read_size); } else { break; } if(conn->response.content_len && (total_size >= conn->response.content_len)) break; } if (buf) { os_free(buf); buf = NULL; } } } } else { os_printf("\nERROR: httpc_conn_connect\n"); } } BK_LOGE(TAG,"----------------BEGIN CLOSE---------------\r\n"); httpc_conn_close(conn); httpc_conn_free(conn); httpc_free(conn); } static void example_httpc_get(char *url) { BK_LOGE(TAG, "-------Example: HTTPC get-----\r\n"); struct httpc_conn *conn = NULL; #if 1 const char *host_addr = 0; const char *path_ptr; const char *request; char *req_url = NULL; const char *port_ptr; char port_str[6] = "80"; int host_addr_len = 0; int url_len = strlen(url); if (strncmp(url, "http://", 7) == 0) { host_addr = url + 7; } else if (strncmp(url, "https://", 8) == 0) { host_addr = url + 8; } else { return; } path_ptr = strstr(host_addr, "/"); request = path_ptr ? path_ptr : "/"; if (request) { req_url = strdup(request); } port_ptr = strstr(host_addr + host_addr_len, ":"); if (port_ptr && path_ptr && (port_ptr < path_ptr)) { int port_len = path_ptr - port_ptr - 1; strncpy(port_str, port_ptr + 1, port_len); port_str[port_len] = '\0'; } if (port_ptr && (!path_ptr)) { strcpy(port_str, port_ptr + 1); } if (!host_addr_len) { if ((port_ptr && path_ptr && (port_ptr < path_ptr) )|| (port_ptr && (!path_ptr))) { host_addr_len = port_ptr - host_addr; } else if (path_ptr) { host_addr_len = path_ptr - host_addr; } else { host_addr_len = strlen(host_addr); } } if ((host_addr_len < 1) || (host_addr_len > url_len)) { return; } char *host_addr_new = os_malloc(host_addr_len + 1); if (!host_addr_new) { return; } memcpy(host_addr_new, host_addr, host_addr_len); host_addr_new[host_addr_len] = '\0'; //BK_LOGE(TAG,"host_addr_new:%s port_str:%d\r\n", host_addr_new, port_str); #endif #if USE_HTTPS conn = httpc_conn_new(HTTPC_SECURE_TLS, NULL, NULL, NULL); #else conn = httpc_conn_new(HTTPC_SECURE_NONE, NULL, NULL, NULL); #endif if(conn) { #if USE_HTTPS if(httpc_conn_connect(conn, host_addr_new, 443, 0) == 0) { #else if(httpc_conn_connect(conn, SERVER_HOST, 80, 0) == 0) { #endif /* HTTP GET request */ BK_LOGE(TAG,"----------------BEGIN WRITE HEADER------------\r\n"); httpc_request_write_header_start(conn, "GET", req_url, NULL, 0); httpc_request_write_header(conn, "User-Agent", "BEKEN HTTP Client/1.0"); // finish and send header httpc_request_write_header_finish(conn); BK_LOGE(TAG,"----------------BEGIN READ HEADER---------------\r\n"); // receive response header if(httpc_response_read_header(conn) == 0) { httpc_conn_dump_header(conn); // receive response body if(httpc_response_is_status(conn, "200 OK")) { //uint8_t buf[RCV_BUFF_SIZE]; uint8_t *buf = (uint8_t *)malloc(RCV_BUFF_SIZE); int read_size = 0, total_size = 0; BK_LOGE(TAG,"----------------BEGIN READ PAYLOAD---------------\r\n"); while(1) { memset(buf, 0, RCV_BUFF_SIZE); read_size = httpc_response_read_data(conn, buf, RCV_BUFF_SIZE - 1); if(read_size > 0) { BK_LOGE(TAG,"httpc get response data: \r\n"); total_size += read_size; bk_httpc_hex_dump((char *)buf, read_size); } else { break; } if(conn->response.content_len && (total_size >= conn->response.content_len)) break; } if (buf) { os_free(buf); buf = NULL; } } } } else { printf("\nERROR: httpc_conn_connect\n"); } } BK_LOGE(TAG,"----------------BEGIN CLOSE---------------\r\n"); httpc_conn_close(conn); httpc_conn_free(conn); httpc_free((void *)conn); } extern char *httpc_url; static void example_httpc_thread(void *param) { printf("\nExample: HTTPC\n"); /* test GET to http://httpbin.org/get?param1=test_data1¶m2=test_data2 */ example_httpc_get(httpc_url); /* test POST to http://httpbin.org/post with data of param1=test_data1¶m2=test_data2 */ example_httpc_post(httpc_url); rtos_delete_thread(NULL); } void example_httpc(void) { if( rtos_create_thread(NULL, 7, "example_httpc_thread", (beken_thread_function_t)example_httpc_thread, 6*1024, 0) != 0) { os_printf("%s (example_httpc_thread) failed\r\n", __func__); } } #endif