Commit 1d4f97bb authored by rojer's avatar rojer Committed by Cesanta Bot

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
parent 183fc7ce
......@@ -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 */
......
......@@ -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) */
......@@ -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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment