From 1d4f97bb4c6253c94aa811b25516456c0094cf4a Mon Sep 17 00:00:00 2001 From: rojer Date: Thu, 9 Jun 2016 18:33:22 +0100 Subject: [PATCH] SimpleLink net_if impl w/ async support; MG_F_SSL SimpleLink sockets are suffciently different from BSD that all the ifdefs have become too messy to warrant a separate net_if implementation. As part of this we also implement proper async connect support. Added MG_F_SSL to identify SSL-enabled connections in a generic way, since SSL state can be different depending on the implementation. PUBLISHED_FROM=9cdb8c880b90683e4a26b972cf439d47d6f60917 --- docs/c-api/net.h/struct_mg_connection.md | 9 +- mongoose.c | 681 +++++++++++++++++------ mongoose.h | 9 +- 3 files changed, 529 insertions(+), 170 deletions(-) diff --git a/docs/c-api/net.h/struct_mg_connection.md b/docs/c-api/net.h/struct_mg_connection.md index 338482ae..9d1bf333 100644 --- a/docs/c-api/net.h/struct_mg_connection.md +++ b/docs/c-api/net.h/struct_mg_connection.md @@ -48,10 +48,11 @@ signature: | #define MG_F_UDP (1 << 1) /* This connection is UDP */ #define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ #define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ - #define MG_F_SSL_HANDSHAKE_DONE (1 << 4) /* SSL specific */ - #define MG_F_WANT_READ (1 << 5) /* SSL specific */ - #define MG_F_WANT_WRITE (1 << 6) /* SSL specific */ - #define MG_F_IS_WEBSOCKET (1 << 7) /* Websocket specific */ + #define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ + #define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ + #define MG_F_WANT_READ (1 << 6) /* SSL specific */ + #define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ + #define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ /* Flags that are settable by user */ #define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */ diff --git a/mongoose.c b/mongoose.c index 7506d329..60906d6b 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2119,9 +2119,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen) { #define intptr_t long #endif -int mg_is_error(int n); -void mg_set_non_blocking_mode(sock_t sock); - extern void mg_ev_mgr_init(struct mg_mgr *mgr); extern void mg_ev_mgr_free(struct mg_mgr *mgr); extern void mg_ev_mgr_add_conn(struct mg_connection *nc); @@ -2204,13 +2201,9 @@ void mg_if_timer(struct mg_connection *c, double now) { } void mg_if_poll(struct mg_connection *nc, time_t now) { -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) - if (nc->ssl == NULL || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { + if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { mg_call(nc, NULL, MG_EV_POLL, &now); } -#else - mg_call(nc, NULL, MG_EV_POLL, &now); -#endif } static void mg_destroy_conn(struct mg_connection *conn) { @@ -2218,16 +2211,9 @@ static void mg_destroy_conn(struct mg_connection *conn) { conn->proto_data_destructor(conn->proto_data); } mg_if_destroy_conn(conn); -#if defined(MG_ENABLE_SSL) -#if !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) if (conn->ssl != NULL) SSL_free(conn->ssl); if (conn->ssl_ctx != NULL) SSL_CTX_free(conn->ssl_ctx); -#else - MG_FREE(conn->ssl_cert); - MG_FREE(conn->ssl_key); - MG_FREE(conn->ssl_ca_cert); - MG_FREE(conn->ssl_server_name); -#endif #endif mbuf_free(&conn->recv_mbuf); mbuf_free(&conn->send_mbuf); @@ -2667,6 +2653,9 @@ static const char *mg_set_ssl2(struct mg_connection *nc, const char *cert, #ifndef MG_DISABLE_PFS SSL_CTX_set_cipher_list(nc->ssl_ctx, mg_s_cipher_list); #endif + + if (result == NULL) nc->flags |= MG_F_SSL; + return result; } @@ -2675,29 +2664,9 @@ const char *mg_set_ssl(struct mg_connection *nc, const char *cert, return mg_set_ssl2(nc, cert, NULL, ca_cert); } -#else /* !MG_SOCKET_SIMPLELINK */ - -static const char *mg_set_ssl2(struct mg_connection *nc, const char *cert, - const char *key, const char *ca_cert) { - DBG(("%p %s,%s,%s", nc, (cert ? cert : ""), (key ? key : ""), - (ca_cert ? ca_cert : ""))); - - if (nc->flags & MG_F_UDP) { - return "SSL for UDP is not supported"; - } - - if (cert != NULL || key != NULL) { - if (cert != NULL && key != NULL) { - nc->ssl_cert = strdup(cert); - nc->ssl_key = strdup(key); - } else { - return "both cert and key are required"; - } - } - if (ca_cert != NULL) nc->ssl_ca_cert = strdup(ca_cert); - - return NULL; -} +#else +const char *mg_set_ssl2(struct mg_connection *nc, const char *cert, + const char *key, const char *ca_cert); #endif /* MG_SOCKET_SIMPLELINK */ #endif /* MG_ENABLE_SSL */ @@ -2712,6 +2681,7 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { nc->proto_handler = lc->proto_handler; nc->user_data = lc->user_data; nc->recv_mbuf_limit = lc->recv_mbuf_limit; + if (lc->flags & MG_F_SSL) nc->flags |= MG_F_SSL; mg_add_conn(nc->mgr, nc); DBG(("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); return nc; @@ -3179,7 +3149,7 @@ double mg_time() { * All rights reserved */ -#ifndef MG_DISABLE_SOCKET_IF +#if !defined(MG_DISABLE_SOCKET_IF) && !defined(MG_SOCKET_SIMPLELINK) /* Amalgamated: #include "mongoose/src/internal.h" */ /* Amalgamated: #include "mongoose/src/util.h" */ @@ -3189,7 +3159,7 @@ double mg_time() { static sock_t mg_open_listening_socket(union socket_address *sa, int type, int proto); -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) static void mg_ssl_begin(struct mg_connection *nc); static int mg_ssl_err(struct mg_connection *conn, int res); #endif @@ -3198,26 +3168,15 @@ void mg_set_non_blocking_mode(sock_t sock) { #ifdef _WIN32 unsigned long on = 1; ioctlsocket(sock, FIONBIO, &on); -#elif defined(MG_SOCKET_SIMPLELINK) - SlSockNonblocking_t opt; - opt.NonblockingEnabled = 1; - sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); #else int flags = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flags | O_NONBLOCK); #endif } -int mg_is_error(int n) { -#ifdef MG_SOCKET_SIMPLELINK - DBG(("n = %d, errno = %d", n, errno)); - if (n < 0) errno = n; -#endif +static int mg_is_error(int n) { return n == 0 || (n < 0 && errno != EINTR && errno != EINPROGRESS && errno != EAGAIN && errno != EWOULDBLOCK -#ifdef MG_SOCKET_SIMPLELINK - && errno != SL_EALREADY -#endif #ifdef _WIN32 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK @@ -3228,25 +3187,15 @@ int mg_is_error(int n) { void mg_if_connect_tcp(struct mg_connection *nc, const union socket_address *sa) { int rc, proto = 0; -#if defined(MG_ENABLE_SSL) && defined(MG_SOCKET_SIMPLELINK) - if (nc->ssl_cert != NULL || nc->ssl_ca_cert != NULL) proto = SL_SEC_SOCKET; -#endif nc->sock = socket(AF_INET, SOCK_STREAM, proto); if (nc->sock == INVALID_SOCKET) { nc->err = errno ? errno : 1; return; } -#if !defined(MG_SOCKET_SIMPLELINK) && !defined(MG_ESP8266) +#if !defined(MG_ESP8266) mg_set_non_blocking_mode(nc->sock); -#endif -#if defined(MG_ENABLE_SSL) && defined(MG_SOCKET_SIMPLELINK) - int is_ssl_no_vrfy = (nc->ssl_ca_cert != NULL && nc->ssl_ca_cert[0] == '\0'); - if (!sl_set_ssl_opts(nc)) return; #endif rc = connect(nc->sock, &sa->sa, sizeof(sa->sin)); -#if defined(MG_ENABLE_SSL) && defined(MG_SOCKET_SIMPLELINK) - if (is_ssl_no_vrfy && rc == SL_ESECSNOVERIFY) rc = 0; -#endif nc->err = mg_is_error(rc) ? errno : 0; LOG(LL_INFO, ("%p sock %d err %d", nc, nc->sock, nc->err)); } @@ -3262,17 +3211,11 @@ void mg_if_connect_udp(struct mg_connection *nc) { int mg_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { int proto = 0; -#if defined(MG_ENABLE_SSL) && defined(MG_SOCKET_SIMPLELINK) - if (nc->ssl_cert != NULL || nc->ssl_ca_cert != NULL) proto = SL_SEC_SOCKET; -#endif sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); if (sock == INVALID_SOCKET) { return (errno ? errno : 1); } mg_sock_set(nc, sock); -#if defined(MG_ENABLE_SSL) && defined(MG_SOCKET_SIMPLELINK) - if (!sl_set_ssl_opts(nc)) return nc->err; -#endif return 0; } @@ -3309,13 +3252,6 @@ void mg_if_destroy_conn(struct mg_connection *nc) { /* Only close outgoing UDP sockets or listeners. */ if (nc->listener == NULL) closesocket(nc->sock); } - /* - * avoid users accidentally double close a socket - * because it can lead to difficult to debug situations. - * It would happen only if reusing a destroyed mg_connection - * but it's not always possible to run the code through an - * address sanitizer. - */ nc->sock = INVALID_SOCKET; } @@ -3337,7 +3273,7 @@ static int mg_accept_conn(struct mg_connection *lc) { DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), ntohs(sa.sin.sin_port))); mg_sock_set(nc, sock); -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) if (lc->ssl_ctx != NULL) { nc->ssl = SSL_new(lc->ssl_ctx); if (nc->ssl == NULL || SSL_set_fd(nc->ssl, sock) != 1) { @@ -3358,13 +3294,12 @@ static sock_t mg_open_listening_socket(union socket_address *sa, int type, socklen_t sa_len = (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); sock_t sock = INVALID_SOCKET; -#if !defined(MG_SOCKET_SIMPLELINK) && !defined(MG_LWIP) +#if !defined(MG_LWIP) int on = 1; #endif if ((sock = socket(sa->sa.sa_family, type, proto)) != INVALID_SOCKET && -#if !defined(MG_SOCKET_SIMPLELINK) && \ - !defined(MG_LWIP) /* SimpleLink and LWIP don't support either */ +#if !defined(MG_LWIP) /* LWIP doesn't support either */ #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) /* "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" http://goo.gl/RmrFTm */ !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, @@ -3383,12 +3318,11 @@ static sock_t mg_open_listening_socket(union socket_address *sa, int type, */ !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && #endif -#endif /* !MG_SOCKET_SIMPLELINK && !MG_LWIP */ +#endif /* !MG_LWIP */ !bind(sock, &sa->sa, sa_len) && (type == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { -#if !defined(MG_SOCKET_SIMPLELINK) && \ - !defined(MG_LWIP) /* TODO(rojer): Fix this. */ +#if !defined(MG_LWIP) mg_set_non_blocking_mode(sock); /* In case port was set to 0, get the real port number */ (void) getsockname(sock, &sa->sa, &sa_len); @@ -3419,12 +3353,12 @@ static void mg_write_to_socket(struct mg_connection *nc) { inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); if (n > 0) { mbuf_remove(io, n); + mg_if_sent_cb(nc, n); } - mg_if_sent_cb(nc, n); return; } -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) if (nc->ssl != NULL) { if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { n = SSL_write(nc->ssl, io->buf, io->len); @@ -3452,8 +3386,8 @@ static void mg_write_to_socket(struct mg_connection *nc) { if (n > 0) { mbuf_remove(io, n); + mg_if_sent_cb(nc, n); } - mg_if_sent_cb(nc, n); } MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { @@ -3463,7 +3397,7 @@ MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { return avail > max ? max : avail; } -static void mg_read_from_socket(struct mg_connection *conn) { +static void mg_handle_tcp_read(struct mg_connection *conn) { int n = 0; char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); @@ -3472,7 +3406,7 @@ static void mg_read_from_socket(struct mg_connection *conn) { return; } -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) if (conn->ssl != NULL) { if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { /* SSL library may have more bytes ready to read then we ask to read. @@ -3540,7 +3474,7 @@ static void mg_handle_udp_read(struct mg_connection *nc) { mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); } -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) static int mg_ssl_err(struct mg_connection *conn, int res) { int ssl_err = SSL_get_error(conn->ssl, res); DBG(("%p %d -> %d", conn, res, ssl_err)); @@ -3583,7 +3517,7 @@ static void mg_ssl_begin(struct mg_connection *nc) { } } } -#endif /* defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) */ +#endif /* defined(MG_ENABLE_SSL) */ #define _MG_F_FD_CAN_READ 1 #define _MG_F_FD_CAN_WRITE 1 << 1 @@ -3596,7 +3530,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { if (nc->flags & MG_F_CONNECTING) { if (fd_flags != 0) { int err = 0; -#if !defined(MG_SOCKET_SIMPLELINK) && !defined(MG_ESP8266) +#if !defined(MG_ESP8266) if (!(nc->flags & MG_F_UDP)) { socklen_t len = sizeof(err); int ret = @@ -3605,16 +3539,11 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { } #else /* - * On SimpleLink and ESP8266 we use blocking connect. - * TODO(rojer): Figure out why it fails where blocking succeeds. + * On ESP8266 we use blocking connect. */ err = nc->err; -#if defined(MG_SOCKET_SIMPLELINK) - /* TODO(rojer): Provide API to set date. */ - if (err == SL_ESECDATEERROR) err = 0; #endif -#endif -#if defined(MG_ENABLE_SSL) && !defined(MG_SOCKET_SIMPLELINK) +#if defined(MG_ENABLE_SSL) if (nc->ssl != NULL && err == 0) { SSL_set_fd(nc->ssl, nc->sock); mg_ssl_begin(nc); @@ -3639,9 +3568,9 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { * a time. The reason is that eCos does not respect non-blocking * flag on a listening socket and hangs in a loop. */ - if (fd_flags & _MG_F_FD_CAN_READ) mg_accept_conn(nc); + mg_accept_conn(nc); } else { - mg_read_from_socket(nc); + mg_handle_tcp_read(nc); } } } @@ -3824,13 +3753,6 @@ time_t mg_mgr_poll(struct mg_mgr *mgr, int timeout_ms) { (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); } -#ifdef MG_SOCKET_SIMPLELINK - /* SimpleLink does not report UDP sockets as writeable. */ - if (nc->flags & MG_F_UDP && - (nc->send_mbuf.len > 0 || nc->flags & MG_F_CONNECTING)) { - fd_flags |= _MG_F_FD_CAN_WRITE; - } -#endif #ifdef MG_LWIP /* With LWIP socket emulation layer, we don't get write events */ fd_flags |= _MG_F_FD_CAN_WRITE; @@ -3895,7 +3817,6 @@ int mg_socketpair(sock_t sp[2], int sock_type) { } #endif /* MG_DISABLE_SOCKETPAIR */ -#ifndef MG_SOCKET_SIMPLELINK static void mg_sock_get_addr(sock_t sock, int remote, union socket_address *sa) { socklen_t slen = sizeof(*sa); @@ -3912,21 +3833,13 @@ void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { mg_sock_get_addr(sock, flags & MG_SOCK_STRINGIFY_REMOTE, &sa); mg_sock_addr_to_str(&sa, buf, len, flags); } -#endif void mg_if_get_conn_addr(struct mg_connection *nc, int remote, union socket_address *sa) { -#ifndef MG_SOCKET_SIMPLELINK mg_sock_get_addr(nc->sock, remote, sa); -#else - /* SimpleLink does not provide a way to get socket's peer address after - * accept or connect. Address hould have been preserved in the connection, - * so we do our best here by using it. */ - if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); -#endif } -#endif /* !MG_DISABLE_SOCKET_IF */ +#endif /* !defined(MG_DISABLE_SOCKET_IF) && !defined(MG_SOCKET_SIMPLELINK) */ #ifdef MG_MODULE_LINES #line 1 "./src/multithreading.c" #endif @@ -11110,50 +11023,6 @@ int inet_pton(int af, const char *src, void *dst) { return 1; } -#ifdef MG_ENABLE_SSL -int sl_set_ssl_opts(struct mg_connection *nc) { - DBG(("%p %s,%s,%s,%s", nc, (nc->ssl_cert ? nc->ssl_cert : ""), (nc->ssl_key ? nc->ssl_cert : ""), (nc->ssl_ca_cert ? nc->ssl_ca_cert : ""), (nc->ssl_server_name ? nc->ssl_server_name : ""))); - if (nc->ssl_cert != NULL && nc->ssl_key != NULL) { - nc->err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, - nc->ssl_cert, - strlen(nc->ssl_cert)); - DBG(("CERTIFICATE_FILE_NAME %s -> %d", nc->ssl_cert, nc->err)); - if (nc->err != 0) return 0; - nc->err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, - nc->ssl_key, - strlen(nc->ssl_key)); - DBG(("PRIVATE_KEY_FILE_NAME %s -> %d", nc->ssl_key, nc->err)); - if (nc->err != 0) return 0; - MG_FREE(nc->ssl_cert); - MG_FREE(nc->ssl_key); - nc->ssl_cert = nc->ssl_key = NULL; - } - if (nc->ssl_ca_cert != NULL && nc->ssl_ca_cert[0] != '\0') { - nc->err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CA_FILE_NAME, - nc->ssl_ca_cert, - strlen(nc->ssl_ca_cert)); - DBG(("CA_FILE_NAME %s -> %d", nc->ssl_ca_cert, nc->err)); - if (nc->err != 0) return 0; - MG_FREE(nc->ssl_ca_cert); - nc->ssl_ca_cert = NULL; - } - if (nc->ssl_server_name != NULL) { - nc->err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, - SO_SECURE_DOMAIN_NAME_VERIFICATION, - nc->ssl_server_name, - strlen(nc->ssl_server_name)); - DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", nc->ssl_server_name, nc->err)); - if (nc->err != 0) return 0; - MG_FREE(nc->ssl_server_name); - nc->ssl_server_name = NULL; - } - return 1; -} -#endif - #endif /* CS_COMMON_PLATFORMS_SIMPLELINK_SL_SOCKET_C_ */ #ifdef MG_MODULE_LINES #line 1 "./src/../../common/platforms/simplelink/sl_mg_task.c" @@ -11209,3 +11078,491 @@ void mg_run_in_task(void (*cb)(struct mg_mgr *mgr, void *arg), void *cb_arg) { } #endif /* defined(MG_SOCKET_SIMPLELINK) */ +#ifdef MG_MODULE_LINES +#line 1 "./src/../../common/platforms/simplelink/sl_net_if.c" +#endif +/* + * Copyright (c) 2014-2016 Cesanta Software Limited + * All rights reserved + */ + +#if !defined(MG_DISABLE_SOCKET_IF) && defined(MG_SOCKET_SIMPLELINK) + +/* Amalgamated: #include "mongoose/src/internal.h" */ +/* Amalgamated: #include "mongoose/src/util.h" */ + +#define MG_TCP_RECV_BUFFER_SIZE 1024 +#define MG_UDP_RECV_BUFFER_SIZE 1500 + +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto); + +#ifdef MG_ENABLE_SSL +const char *mg_set_ssl2(struct mg_connection *nc, const char *cert, + const char *key, const char *ca_cert) { + DBG(("%p %s,%s,%s", nc, (cert ? cert : ""), (key ? key : ""), + (ca_cert ? ca_cert : ""))); + + if (nc->flags & MG_F_UDP) { + return "SSL for UDP is not supported"; + } + + if (cert != NULL || key != NULL) { + if (cert != NULL && key != NULL) { + nc->ssl_cert = strdup(cert); + nc->ssl_key = strdup(key); + } else { + return "both cert and key are required"; + } + } + if (ca_cert != NULL) nc->ssl_ca_cert = strdup(ca_cert); + + nc->flags |= MG_F_SSL; + + return NULL; +} + +int sl_set_ssl_opts(struct mg_connection *nc) { + int err; + DBG(("%p %s,%s,%s,%s", nc, (nc->ssl_cert ? nc->ssl_cert : ""), + (nc->ssl_key ? nc->ssl_cert : ""), + (nc->ssl_ca_cert ? nc->ssl_ca_cert : ""), + (nc->ssl_server_name ? nc->ssl_server_name : ""))); + if (nc->ssl_cert != NULL && nc->ssl_key != NULL) { + err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, nc->ssl_cert, + strlen(nc->ssl_cert)); + DBG(("CERTIFICATE_FILE_NAME %s -> %d", nc->ssl_cert, nc->err)); + if (err != 0) return err; + err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, nc->ssl_key, + strlen(nc->ssl_key)); + DBG(("PRIVATE_KEY_FILE_NAME %s -> %d", nc->ssl_key, nc->err)); + if (err != 0) return err; + MG_FREE(nc->ssl_cert); + MG_FREE(nc->ssl_key); + nc->ssl_cert = nc->ssl_key = NULL; + } + if (nc->ssl_ca_cert != NULL) { + if (nc->ssl_ca_cert[0] != '\0') { + err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CA_FILE_NAME, nc->ssl_ca_cert, + strlen(nc->ssl_ca_cert)); + DBG(("CA_FILE_NAME %s -> %d", nc->ssl_ca_cert, nc->err)); + if (err != 0) return err; + } + MG_FREE(nc->ssl_ca_cert); + nc->ssl_ca_cert = NULL; + } + if (nc->ssl_server_name != NULL) { + err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET, + SO_SECURE_DOMAIN_NAME_VERIFICATION, nc->ssl_server_name, + strlen(nc->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", nc->ssl_server_name, nc->err)); + if (err != 0) return err; + MG_FREE(nc->ssl_server_name); + nc->ssl_server_name = NULL; + } + return 0; +} +#endif + +void mg_set_non_blocking_mode(sock_t sock) { + SlSockNonblocking_t opt; + opt.NonblockingEnabled = 1; + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt)); +} + +static int mg_is_error(int n) { + return (n < 0 && n != SL_EALREADY); +} + +void mg_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); + if (sock < 0) { + nc->err = sock; + goto out; + } + mg_sock_set(nc, sock); +#ifdef MG_ENABLE_SSL + nc->err = sl_set_ssl_opts(nc); + if (nc->err != 0) goto out; +#endif + nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin)); +out: + LOG(LL_INFO, ("%p sock %d err %d", nc, nc->sock, nc->err)); +} + +void mg_if_connect_udp(struct mg_connection *nc) { + sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + nc->err = sock; + return; + } + mg_sock_set(nc, sock); + nc->err = 0; +} + +int mg_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { + int proto = 0; + if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; + sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto); + if (sock == INVALID_SOCKET) { + return (errno ? errno : 1); + } + mg_sock_set(nc, sock); +#ifdef MG_ENABLE_SSL + return sl_set_ssl_opts(nc); +#else + return 0; +#endif +} + +int mg_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { + sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) return (errno ? errno : 1); + mg_sock_set(nc, sock); + return 0; +} + +void mg_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { + mbuf_append(&nc->send_mbuf, buf, len); +} + +void mg_if_recved(struct mg_connection *nc, size_t len) { + (void) nc; + (void) len; +} + +int mg_if_create_conn(struct mg_connection *nc) { + (void) nc; + return 1; +} + +void mg_if_destroy_conn(struct mg_connection *nc) { + if (nc->sock == INVALID_SOCKET) return; + if (!(nc->flags & MG_F_UDP)) { + sl_Close(nc->sock); + } else { + /* Only close outgoing UDP sockets or listeners. */ + if (nc->listener == NULL) sl_Close(nc->sock); + } + nc->sock = INVALID_SOCKET; +#ifdef MG_ENABLE_SSL + MG_FREE(nc->ssl_cert); + MG_FREE(nc->ssl_key); + MG_FREE(nc->ssl_ca_cert); + MG_FREE(nc->ssl_server_name); +#endif +} + +static int mg_accept_conn(struct mg_connection *lc) { + struct mg_connection *nc; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + sock_t sock = sl_Accept(lc->sock, &sa.sa, &sa_len); + if (sock < 0) { + DBG(("%p: failed to accept: %d", lc, sock)); + return 0; + } + nc = mg_if_accept_new_conn(lc); + if (nc == NULL) { + sl_Close(sock); + return 0; + } + DBG(("%p conn from %s:%d", nc, inet_ntoa(sa.sin.sin_addr), + ntohs(sa.sin.sin_port))); + mg_sock_set(nc, sock); + if (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + mg_if_accept_tcp_cb(nc, &sa, sa_len); + return 1; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t mg_open_listening_socket(union socket_address *sa, int type, + int proto) { + socklen_t sa_len = + (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = sl_Socket(sa->sa.sa_family, type, proto); + if (sock < 0) return INVALID_SOCKET; + if (bind(sock, &sa->sa, sa_len) < 0) { + sl_Close(sock); + return INVALID_SOCKET; + } + if (type != SOCK_DGRAM && sl_Listen(sock, SOMAXCONN) < 0) { + sl_Close(sock); + return INVALID_SOCKET; + } + mg_set_non_blocking_mode(sock); + return sock; +} + +static void mg_write_to_socket(struct mg_connection *nc) { + struct mbuf *io = &nc->send_mbuf; + int n = 0; + + if (nc->flags & MG_F_UDP) { + int n = sl_SendTo(nc->sock, io->buf, io->len, 0, &nc->sa.sa, + sizeof(nc->sa.sin)); + DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, errno, + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); + if (n > 0) mbuf_remove(io, n); + mg_if_sent_cb(nc, n); + return; + } else { + n = (int) sl_Send(nc->sock, io->buf, io->len, 0); + DBG(("%p %d bytes -> %d", nc, n, nc->sock)); + if (n < 0 && !mg_is_error(n)) return; + } + + if (n > 0) { + mbuf_remove(io, n); + mg_if_sent_cb(nc, n); + } +} + +MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) { + size_t avail; + if (conn->recv_mbuf_limit < conn->recv_mbuf.len) return 0; + avail = conn->recv_mbuf_limit - conn->recv_mbuf.len; + return avail > max ? max : avail; +} + +static void mg_handle_tcp_read(struct mg_connection *conn) { + int n = 0; + char *buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); + + if (buf == NULL) { + DBG(("OOM")); + return; + } + + n = (int) sl_Recv(conn->sock, buf, + recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); + DBG(("%p %d bytes <- %d", conn, n, conn->sock)); + if (n > 0) { + mg_if_recv_tcp_cb(conn, buf, n); + } else { + MG_FREE(buf); + } + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + conn->flags |= MG_F_SEND_AND_CLOSE; + } else if (mg_is_error(n)) { + conn->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} + +static void mg_handle_udp_read(struct mg_connection *nc) { + char *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); + if (buf == NULL) return; + union socket_address sa; + socklen_t sa_len = sizeof(sa); + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) &sa, &sa_len); + DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (n > 0) { + mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); + } else { + MG_FREE(buf); + } +} + +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 + +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + + if (nc->flags & MG_F_CONNECTING) { + if (nc->flags & MG_F_UDP || nc->err != SL_EALREADY) { + mg_if_connect_cb(nc, nc->err); + } else { + /* In SimpleLink, to get status of non-blocking connect() we need to wait + * until socket is writable and repeat the call to sl_Connect again, + * which will now return the real status. */ + if (fd_flags & _MG_F_FD_CAN_WRITE) { + nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin)); + if (nc->err == SL_ESECSNOVERIFY || + /* TODO(rojer): Provide API to set the date for verification. */ + nc->err == SL_ESECDATEERROR) { + nc->err = 0; + } + if (nc->flags & MG_F_SSL && nc->err == 0) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + } + mg_if_connect_cb(nc, nc->err); + } + } + /* Ignore read/write in further processing, we've handled it. */ + fd_flags &= ~(_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE); + } + + if (fd_flags & _MG_F_FD_CAN_READ) { + if (nc->flags & MG_F_UDP) { + mg_handle_udp_read(nc); + } else { + if (nc->flags & MG_F_LISTENING) { + mg_accept_conn(nc); + } else { + mg_handle_tcp_read(nc); + } + } + } + + if (!(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { + if ((fd_flags & _MG_F_FD_CAN_WRITE) && nc->send_mbuf.len > 0) { + mg_write_to_socket(nc); + } + + if (!(fd_flags & (_MG_F_FD_CAN_READ | _MG_F_FD_CAN_WRITE))) { + mg_if_poll(nc, now); + } + mg_if_timer(nc, now); + } + + DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); +} + +/* Associate a socket to a connection. */ +void mg_sock_set(struct mg_connection *nc, sock_t sock) { + mg_set_non_blocking_mode(sock); + nc->sock = sock; + DBG(("%p %d", nc, sock)); +} + +void mg_ev_mgr_init(struct mg_mgr *mgr) { + (void) mgr; + DBG(("%p using sl_Select()", mgr)); +} + +void mg_ev_mgr_free(struct mg_mgr *mgr) { + (void) mgr; +} + +void mg_ev_mgr_add_conn(struct mg_connection *nc) { + (void) nc; +} + +void mg_ev_mgr_remove_conn(struct mg_connection *nc) { + (void) nc; +} + +time_t mg_mgr_poll(struct mg_mgr *mgr, int timeout_ms) { + double now = mg_time(); + double min_timer; + struct mg_connection *nc, *tmp; + struct SlTimeval_t tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + int num_fds, num_ev, num_timers = 0; + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); + + /* + * Note: it is ok to have connections with sock == INVALID_SOCKET in the list, + * e.g. timer-only "connections". + */ + min_timer = 0; + for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) { + tmp = nc->next; + + if (nc->sock != INVALID_SOCKET) { + num_fds++; + + if (!(nc->flags & MG_F_WANT_WRITE) && + nc->recv_mbuf.len < nc->recv_mbuf_limit && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { + FD_SET(nc->sock, &read_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + + if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) || + (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) { + FD_SET(nc->sock, &write_set); + FD_SET(nc->sock, &err_set); + if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock; + } + } + + if (nc->ev_timer_time > 0) { + if (num_timers == 0 || nc->ev_timer_time < min_timer) { + min_timer = nc->ev_timer_time; + } + num_timers++; + } + } + + /* + * If there is a timer to be fired earlier than the requested timeout, + * adjust the timeout. + */ + if (num_timers > 0) { + double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */; + if (timer_timeout_ms < timeout_ms) { + timeout_ms = timer_timeout_ms; + } + } + if (timeout_ms < 0) timeout_ms = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + num_ev = sl_Select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv); + now = mg_time(); + DBG(("sl_Select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds, + timeout_ms)); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + int fd_flags = 0; + if (nc->sock != INVALID_SOCKET) { + if (num_ev > 0) { + fd_flags = (FD_ISSET(nc->sock, &read_set) && + (!(nc->flags & MG_F_UDP) || nc->listener == NULL) + ? _MG_F_FD_CAN_READ + : 0) | + (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) | + (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0); + } + /* SimpleLink does not report UDP sockets as writeable. */ + if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) { + fd_flags |= _MG_F_FD_CAN_WRITE; + } + } + tmp = nc->next; + mg_mgr_handle_conn(nc, fd_flags, now); + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + } + } + + return now; +} + +void mg_if_get_conn_addr(struct mg_connection *nc, int remote, + union socket_address *sa) { + /* SimpleLink does not provide a way to get socket's peer address after + * accept or connect. Address hould have been preserved in the connection, + * so we do our best here by using it. */ + if (remote) memcpy(sa, &nc->sa, sizeof(*sa)); +} + +#endif /* !defined(MG_DISABLE_SOCKET_IF) && defined(MG_SOCKET_SIMPLELINK) */ diff --git a/mongoose.h b/mongoose.h index c659c40d..7ecccc13 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1293,10 +1293,11 @@ struct mg_connection { #define MG_F_UDP (1 << 1) /* This connection is UDP */ #define MG_F_RESOLVING (1 << 2) /* Waiting for async resolver */ #define MG_F_CONNECTING (1 << 3) /* connect() call in progress */ -#define MG_F_SSL_HANDSHAKE_DONE (1 << 4) /* SSL specific */ -#define MG_F_WANT_READ (1 << 5) /* SSL specific */ -#define MG_F_WANT_WRITE (1 << 6) /* SSL specific */ -#define MG_F_IS_WEBSOCKET (1 << 7) /* Websocket specific */ +#define MG_F_SSL (1 << 4) /* SSL is enabled on the connection */ +#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */ +#define MG_F_WANT_READ (1 << 6) /* SSL specific */ +#define MG_F_WANT_WRITE (1 << 7) /* SSL specific */ +#define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */ /* Flags that are settable by user */ #define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */