#include <sys/socket.h>
#include <netdb.h>
+#include <http_parser.h>
#include "esp-tls.h"
return tls;
}
+static int get_port(const char *url, struct http_parser_url *u)
+{
+ if (u->field_data[UF_PORT].len) {
+ return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
+ } else {
+ if (strncmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
+ return 80;
+ } else if (strncmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
+ return 443;
+ }
+ }
+ return 0;
+}
+
+struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg)
+{
+ /* Parse URI */
+ struct http_parser_url u;
+ http_parser_url_init(&u);
+ http_parser_parse_url(url, strlen(url), 0, &u);
+
+ /* Connect to host */
+ return esp_tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
+ get_port(url, &u), cfg);
+}
*/
struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg);
+/* 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)
{
return tls->write(tls, data, datalen);
+++ /dev/null
-/* With adaptations by Espressif Systems
- *
- * Copyright (c) 2013 Tatsuhiro Tsujikawa
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-#include <stdlib.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <netdb.h>
-
-#include "connectlib.h"
-
-/* Basic parser from nghttp2/examples/client.c */
-int parse_uri(struct uri *res, const char *uri)
-{
- /* We only interested in https */
- size_t len, i, offset;
- int ipv6addr = 0;
- memset(res, 0, sizeof(struct uri));
- len = strlen(uri);
- if (len < 9 || memcmp("https://", uri, 8) != 0) {
- return -1;
- }
- offset = 8;
- res->host = res->hostport = &uri[offset];
- res->hostlen = 0;
- if (uri[offset] == '[') {
- /* IPv6 literal address */
- ++offset;
- ++res->host;
- ipv6addr = 1;
- for (i = offset; i < len; ++i) {
- if (uri[i] == ']') {
- res->hostlen = i - offset;
- offset = i + 1;
- break;
- }
- }
- } else {
- const char delims[] = ":/?#";
- for (i = offset; i < len; ++i) {
- if (strchr(delims, uri[i]) != NULL) {
- break;
- }
- }
- res->hostlen = i - offset;
- offset = i;
- }
- if (res->hostlen == 0) {
- return -1;
- }
- /* Assuming https */
- res->port = 443;
- if (offset < len) {
- if (uri[offset] == ':') {
- /* port */
- const char delims[] = "/?#";
- int port = 0;
- ++offset;
- for (i = offset; i < len; ++i) {
- if (strchr(delims, uri[i]) != NULL) {
- break;
- }
- if ('0' <= uri[i] && uri[i] <= '9') {
- port *= 10;
- port += uri[i] - '0';
- if (port > 65535) {
- return -1;
- }
- } else {
- return -1;
- }
- }
- if (port == 0) {
- return -1;
- }
- offset = i;
- res->port = (uint16_t)port;
- }
- }
- res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host);
- for (i = offset; i < len; ++i) {
- if (uri[i] == '#') {
- break;
- }
- }
- if (i - offset == 0) {
- res->path = "/";
- res->pathlen = 1;
- } else {
- res->path = &uri[offset];
- res->pathlen = i - offset;
- }
- return 0;
-}
-
-int connect_to_host(const char *host, size_t hostlen, uint16_t port)
-{
- int ret;
- struct addrinfo hints = {
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- };
-
- char service[6];
- snprintf(service, sizeof(service), "%u", port);
-
- char *use_host = calloc(1, hostlen + 1);
- if (!use_host) {
- return -1;
- }
- strncpy(use_host, host, hostlen);
-
- struct addrinfo *res;
- ret = getaddrinfo(use_host, service, &hints, &res);
- if (ret) {
- free(use_host);
- return -1;
- }
- free(use_host);
-
- ret = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if (ret < 0) {
- goto err_freeaddr;
- }
-
- int fd = ret;
- ret = connect(fd, res->ai_addr, res->ai_addrlen);
- if (ret < 0) {
- goto err_freesocket;
- }
-
- return fd;
-
-err_freesocket:
- close(fd);
-err_freeaddr:
- freeaddrinfo(res);
- return -1;
-}
-
+++ /dev/null
-// Copyright 2017 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_EXAMPLE_CONNECT_LIB_H_
-#define __ESP_EXAMPLE_CONNECT_LIB_H_
-
-struct uri {
- const char *host;
- /* In this program, path contains query component as well. */
- const char *path;
- size_t pathlen;
- const char *hostport;
- size_t hostlen;
- size_t hostportlen;
- uint16_t port;
-};
-
-/* connect() to a TCP host, return socket descriptor */
-int connect_to_host(const char *host, size_t hostlen, uint16_t port);
-
-/* Parse a URI into its components */
-int parse_uri(struct uri *res, const char *uri);
-
-#endif /* ! __ESP_EXAMPLE_CONNECT_LIB_H_ */
#include <ctype.h>
#include <netdb.h>
#include <esp_log.h>
+#include <esp-tls.h>
-#include "connectlib.h"
#include "sh2lib.h"
static const char *TAG = "sh2lib";
static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data,
size_t length)
{
- int rv = SSL_write(hd->ssl, data, (int)length);
+ int rv = esp_tls_conn_write(hd->http2_tls, (const char *)data, (int)length);
if (rv <= 0) {
- int err = SSL_get_error(hd->ssl, rv);
- if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+ if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
{
struct sh2lib_handle *hd = user_data;
int rv;
- rv = SSL_read(hd->ssl, buf, (int)length);
+ rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
if (rv < 0) {
- int err = SSL_get_error(hd->ssl, rv);
- if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+ if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
{
memset(hd, 0, sizeof(*hd));
-
- struct uri res;
- /* Parse the URI */
- if (parse_uri(&res, uri) != 0) {
- ESP_LOGE(TAG, "[sh2-connect] Failed to parse URI");
- return -1;
- }
-
- /* TCP connection with the server */
- int sockfd = connect_to_host(res.host, res.hostlen, res.port);
- if (sockfd < 0) {
- ESP_LOGE(TAG, "[sh2-connect] Failed to connect to %s", uri);
- return -1;
- }
-
- /* SSL Connection on the socket */
- if (do_ssl_connect(hd, sockfd, res.host) != 0) {
- ESP_LOGE(TAG, "[sh2-connect] SSL Handshake failed with %s", uri);
+ struct esp_tls_cfg tls_cfg;
+ tls_cfg.alpn_protos = (unsigned char *) "\x02h2";
+ if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) {
+ ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed");
goto error;
}
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);
nghttp2_session_del(hd->http2_sess);
hd->http2_sess = NULL;
}
- if (hd->ssl) {
- SSL_free(hd->ssl);
- hd->ssl = NULL;
- }
- if (hd->ssl_ctx) {
- SSL_CTX_free(hd->ssl_ctx);
- hd->ssl_ctx = NULL;
- }
- if (hd->sockfd) {
- close(hd->sockfd);
- hd->ssl_ctx = 0;
+ if (hd->http2_tls) {
+ esp_tls_conn_delete(hd->http2_tls);
+ hd->http2_tls = NULL;
}
if (hd->hostname) {
free(hd->hostname);
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret);
return -1;
}
+
ret = nghttp2_session_recv(hd->http2_sess);
if (ret != 0) {
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret);
return -1;
}
+
return 0;
}
* @brief Handle for working with sh2lib APIs
*/
struct sh2lib_handle {
- /* Ideally, CTX is per-program, so we could potentially take it out of this
- * per-connection structure
- */
- SSL_CTX *ssl_ctx; /*!< Pointer to the SSL context */
- SSL *ssl; /*!< Pointer to the SSL handle */
nghttp2_session *http2_sess; /*!< Pointer to the HTTP2 session handle */
int sockfd; /*!< Socket file descriptor */
char *hostname; /*!< The hostname we are connected to */
+ struct esp_tls *http2_tls; /*!< Pointer to the TLS session handle */
};
/** Flag indicating receive stream is reset */