diff --git a/mongoose.c b/mongoose.c index 1630eb367a..ce2ed4e660 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2185,6 +2185,13 @@ size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) { #define MG_MAX_HOST_LEN 200 +#ifndef MG_TCP_IO_SIZE +#define MG_TCP_IO_SIZE 1460 +#endif +#ifndef MG_UDP_IO_SIZE +#define MG_UDP_IO_SIZE 1460 +#endif + #define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ memcpy(dst, src, sizeof(*dst)); @@ -2225,8 +2232,6 @@ MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { MG_INTERNAL void mg_call(struct mg_connection *nc, mg_event_handler_t ev_handler, void *user_data, int ev, void *ev_data) { - static int nesting_level = 0; - nesting_level++; if (ev_handler == NULL) { /* * If protocol handler is specified, call it. Otherwise, call user-specified @@ -2235,7 +2240,7 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; } if (ev != MG_EV_POLL) { - DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, + DBG(("%p %s ev=%d ev_data=%p flags=0x%lx rmbl=%d smbl=%d", nc, ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } @@ -2248,33 +2253,24 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, #endif if (ev_handler != NULL) { unsigned long flags_before = nc->flags; - size_t recv_mbuf_before = nc->recv_mbuf.len, recved; ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); - recved = (recv_mbuf_before - nc->recv_mbuf.len); /* Prevent user handler from fiddling with system flags. */ if (ev_handler == nc->handler && nc->flags != flags_before) { nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); } - /* It's important to not double-count recved bytes, and since mg_call can be - * called recursively (e.g. proto_handler invokes user handler), we keep - * track of recursion and only report received bytes at the top level. */ - if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { - nc->iface->vtable->recved(nc, recved); - } } if (ev != MG_EV_POLL) { - DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, + DBG(("%p after %s flags=0x%lx rmbl=%d smbl=%d", nc, ev_handler == nc->handler ? "user" : "proto", nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } - nesting_level--; #if !MG_ENABLE_CALLBACK_USERDATA (void) user_data; #endif } -void mg_if_timer(struct mg_connection *c, double now) { +MG_INTERNAL void mg_timer(struct mg_connection *c, double now) { if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { double old_value = c->ev_timer_time; c->ev_timer_time = 0; @@ -2282,13 +2278,45 @@ void mg_if_timer(struct mg_connection *c, double now) { } } -void mg_if_poll(struct mg_connection *nc, time_t now) { - if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); +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 int mg_do_recv(struct mg_connection *nc); + +int mg_if_poll(struct mg_connection *nc, double now) { + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + return 0; + } +#if MG_ENABLE_SSL + if ((nc->flags & (MG_F_SSL | MG_F_LISTENING | MG_F_CONNECTING)) == MG_F_SSL) { + /* SSL library may have data to be delivered to the app in its buffers, + * drain them. */ + int recved = 0; + do { + if (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)) break; + if (recv_avail_size(nc, MG_TCP_IO_SIZE) <= 0) break; + recved = mg_do_recv(nc); + } while (recved > 0); + } +#endif /* MG_ENABLE_SSL */ + mg_timer(nc, now); + { + time_t now_t = (time_t) now; + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now_t); } + return 1; } void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { + if (conn->sock != INVALID_SOCKET) { /* Don't print timer-only conns */ + LOG(LL_DEBUG, ("%p 0x%lx %d", conn, conn->flags, destroy_if)); + } if (destroy_if) conn->iface->vtable->destroy_conn(conn); if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { conn->proto_data_destructor(conn->proto_data); @@ -2304,7 +2332,6 @@ void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { } void mg_close_conn(struct mg_connection *conn) { - DBG(("%p %lu %d", conn, conn->flags, conn->sock)); #if MG_ENABLE_SSL if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { mg_ssl_if_conn_close_notify(conn); @@ -2341,15 +2368,6 @@ void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, signal(SIGPIPE, SIG_IGN); #endif -#if MG_ENABLE_SSL - { - static int init_done; - if (!init_done) { - mg_ssl_if_init(); - init_done++; - } - } -#endif { int i; if (opts.num_ifaces == 0) { @@ -2372,6 +2390,15 @@ void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, } DBG(("==================================")); DBG(("init mgr=%p", m)); +#if MG_ENABLE_SSL + { + static int init_done; + if (!init_done) { + mg_ssl_if_init(); + init_done++; + } + } +#endif } void mg_mgr_free(struct mg_mgr *m) { @@ -2614,6 +2641,37 @@ MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; } +#if MG_ENABLE_SSL +MG_INTERNAL void mg_ssl_handshake(struct mg_connection *nc) { + int err = 0; + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res; + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) return; + res = mg_ssl_if_handshake(nc); + LOG(LL_DEBUG, ("%p %d res %d", nc, server_side, res)); + + if (res == MG_SSL_OK) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + if (server_side) { + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } else { + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } + } else if (res == MG_SSL_WANT_READ) { + nc->flags |= MG_F_WANT_READ; + } else if (res == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + } else { + if (!server_side) { + err = res; + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} +#endif /* MG_ENABLE_SSL */ + struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { struct mg_add_sock_opts opts; struct mg_connection *nc; @@ -2627,95 +2685,125 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { nc->iface = lc->iface; 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)); + LOG(LL_DEBUG, ("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); return nc; } void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, size_t sa_len) { - (void) sa_len; + LOG(LL_DEBUG, ("%p %s://%s:%hu", nc, (nc->flags & MG_F_UDP ? "udp" : "tcp"), + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); nc->sa = *sa; - mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); +#if MG_ENABLE_SSL + if (nc->listener->flags & MG_F_SSL) { + nc->flags |= MG_F_SSL; + if (mg_ssl_if_conn_accept(nc, nc->listener) == MG_SSL_OK) { + mg_ssl_handshake(nc); + } else { + mg_close_conn(nc); + } + } else +#endif + { + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } + (void) sa_len; } void mg_send(struct mg_connection *nc, const void *buf, int len) { nc->last_io_time = (time_t) mg_time(); - if (nc->flags & MG_F_UDP) { - nc->iface->vtable->udp_send(nc, buf, len); - } else { - nc->iface->vtable->tcp_send(nc, buf, len); - } + mbuf_append(&nc->send_mbuf, buf, len); } -void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { - DBG(("%p %d", nc, num_sent)); -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - char *buf = nc->send_mbuf.buf; - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); +static int mg_recv_tcp(struct mg_connection *nc, char *buf, size_t len); +static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len); + +static int mg_do_recv(struct mg_connection *nc) { + int res = 0; + char *buf = NULL; + size_t len = (nc->flags & MG_F_UDP ? MG_UDP_IO_SIZE : MG_TCP_IO_SIZE); + if ((nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_CONNECTING)) || + ((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) { + return -1; } -#endif - if (num_sent < 0) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; + len = recv_avail_size(nc, len); + if (len == 0) return -2; + if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) { + mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len); + } + buf = nc->recv_mbuf.buf + nc->recv_mbuf.len; + len = nc->recv_mbuf.size - nc->recv_mbuf.len; + if (nc->flags & MG_F_UDP) { + res = mg_recv_udp(nc, buf, len); } else { - mbuf_remove(&nc->send_mbuf, num_sent); - mbuf_trim(&nc->send_mbuf); + res = mg_recv_tcp(nc, buf, len); } - mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); + return res; } -MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, - int own) { - DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); - -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); - } -#endif +void mg_if_can_recv_cb(struct mg_connection *nc) { + mg_do_recv(nc); +} - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { - DBG(("%p discarded %d bytes", nc, len)); - /* - * This connection will not survive next poll. Do not deliver events, - * send data to /dev/null without acking. - */ - if (own) { - MG_FREE(buf); +static int mg_recv_tcp(struct mg_connection *nc, char *buf, size_t len) { + int n = 0; +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + n = mg_ssl_if_read(nc, buf, len); + DBG(("%p <- %d bytes (SSL)", nc, n)); + if (n < 0) { + if (n == MG_SSL_WANT_READ) { + nc->flags |= MG_F_WANT_READ; + n = 0; + } else { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else if (n > 0) { + nc->flags &= ~MG_F_WANT_READ; + } + } else { + mg_ssl_handshake(nc); } - return; + } else +#endif + { + n = nc->iface->vtable->tcp_recv(nc, buf, len); + DBG(("%p <- %d bytes", nc, n)); } - nc->last_io_time = (time_t) mg_time(); - if (!own) { - mbuf_append(&nc->recv_mbuf, buf, len); - } else if (nc->recv_mbuf.len == 0) { - /* Adopt buf as recv_mbuf's backing store. */ - mbuf_free(&nc->recv_mbuf); - nc->recv_mbuf.buf = (char *) buf; - nc->recv_mbuf.size = nc->recv_mbuf.len = len; - } else { - mbuf_append(&nc->recv_mbuf, buf, len); - MG_FREE(buf); + if (n > 0) { + nc->recv_mbuf.len += n; + nc->last_io_time = (time_t) mg_time(); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV); + } +#endif + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n); + } else if (n < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; } - mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); -} - -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { - mg_recv_common(nc, buf, len, own); + return n; } -void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, - union socket_address *sa, size_t sa_len) { - assert(nc->flags & MG_F_UDP); - DBG(("%p %u", nc, (unsigned int) len)); +static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) { + int n = 0; + struct mg_connection *lc = nc; + union socket_address sa; + size_t sa_len = sizeof(sa); + n = nc->iface->vtable->udp_recv(lc, buf, len, &sa, &sa_len); + if (n < 0) { + lc->flags |= MG_F_CLOSE_IMMEDIATELY; + goto out; + } if (nc->flags & MG_F_LISTENING) { - struct mg_connection *lc = nc; /* * Do we have an existing connection for this source? * This is very inefficient for long connection lists. */ + lc = nc; for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { - if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { + if (memcmp(&nc->sa.sa, &sa.sa, sa_len) == 0 && nc->listener == lc) { break; } } @@ -2727,7 +2815,7 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, if (nc != NULL) { nc->sock = lc->sock; nc->listener = lc; - nc->sa = *sa; + nc->sa = sa; nc->proto_handler = lc->proto_handler; nc->user_data = lc->user_data; nc->recv_mbuf_limit = lc->recv_mbuf_limit; @@ -2746,18 +2834,88 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, nc->flags |= MG_F_SEND_AND_CLOSE; mg_add_conn(lc->mgr, nc); mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); - } else { - DBG(("OOM")); - /* No return here, we still need to drop on the floor */ } } } if (nc != NULL) { - mg_recv_common(nc, buf, len, 1); - } else { - /* Drop on the floor. */ - MG_FREE(buf); + DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (nc == lc) { + nc->recv_mbuf.len += n; + } else { + mbuf_append(&nc->recv_mbuf, buf, n); + } + mbuf_trim(&lc->recv_mbuf); + lc->last_io_time = nc->last_io_time = (time_t) mg_time(); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV); + } +#endif + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n); + } + +out: + mbuf_free(&lc->recv_mbuf); + return n; +} + +void mg_if_can_send_cb(struct mg_connection *nc) { + int n = 0; + const char *buf = nc->send_mbuf.buf; + size_t len = nc->send_mbuf.len; + + if (nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_CONNECTING)) { + return; + } + if (!(nc->flags & MG_F_UDP)) { + if (nc->flags & MG_F_LISTENING) return; + if (len > MG_TCP_IO_SIZE) len = MG_TCP_IO_SIZE; + } +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (len > 0) { + n = mg_ssl_if_write(nc, buf, len); + DBG(("%p -> %d bytes (SSL)", nc, n)); + } + if (n < 0) { + if (n == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + n = 0; + } else { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + nc->flags &= ~MG_F_WANT_WRITE; + } + } else { + mg_ssl_handshake(nc); + } + } else +#endif + { + if (nc->flags & MG_F_UDP) { + n = nc->iface->vtable->udp_send(nc, buf, len); + } else { + n = nc->iface->vtable->tcp_send(nc, buf, len); + } + DBG(("%p -> %d bytes", nc, n)); + } + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (n > 0 && nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_SEND); + } +#endif + if (n < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (n > 0) { + nc->last_io_time = (time_t) mg_time(); + mbuf_remove(&nc->send_mbuf, n); + mbuf_trim(&nc->send_mbuf); } + if (n != 0) mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &n); } /* @@ -2769,8 +2927,8 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, int proto, union socket_address *sa) { - DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", - inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); + LOG(LL_DEBUG, ("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); nc->flags |= MG_F_CONNECTING; if (proto == SOCK_DGRAM) { @@ -2783,12 +2941,21 @@ MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, } void mg_if_connect_cb(struct mg_connection *nc, int err) { - DBG(("%p connect, err=%d", nc, err)); + LOG(LL_DEBUG, + ("%p %s://%s:%hu -> %d", nc, (nc->flags & MG_F_UDP ? "udp" : "tcp"), + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port), err)); nc->flags &= ~MG_F_CONNECTING; if (err != 0) { nc->flags |= MG_F_CLOSE_IMMEDIATELY; } - mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); +#if MG_ENABLE_SSL + if (err == 0 && (nc->flags & MG_F_SSL)) { + mg_ssl_handshake(nc); + } else +#endif + { + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } } #if MG_ENABLE_ASYNC_RESOLVER @@ -2878,7 +3045,8 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, #endif #if MG_ENABLE_SSL - DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + LOG(LL_DEBUG, + ("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), (opts.ssl_key ? opts.ssl_key : "-"), (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); @@ -3124,7 +3292,7 @@ double mg_set_timer(struct mg_connection *c, double timestamp) { DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), (unsigned long) timestamp)); if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { - ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; + mg_set_timer((struct mg_connection *) c->priv_2, timestamp); } return result; } @@ -3278,14 +3446,8 @@ struct mg_iface *mg_find_iface(struct mg_mgr *mgr, /* Amalgamated: #include "mg_internal.h" */ /* Amalgamated: #include "mg_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); -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc); -#endif void mg_set_non_blocking_mode(sock_t sock) { #ifdef _WIN32 @@ -3354,27 +3516,48 @@ int mg_socket_if_listen_tcp(struct mg_connection *nc, return 0; } -int mg_socket_if_listen_udp(struct mg_connection *nc, - union socket_address *sa) { +static int mg_socket_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 (mg_get_errno() ? mg_get_errno() : 1); mg_sock_set(nc, sock); return 0; } -void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); + if (n < 0 && !mg_is_error()) n = 0; + return n; } -void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + if (n < 0 && !mg_is_error()) n = 0; + return n; } -void mg_socket_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; +static int mg_socket_if_tcp_recv(struct mg_connection *nc, void *buf, + size_t len) { + int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + nc->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && !mg_is_error()) { + n = 0; + } + return n; +} + +static int mg_socket_if_udp_recv(struct mg_connection *nc, void *buf, + size_t len, union socket_address *sa, + size_t *sa_len) { + socklen_t sa_len_st = *sa_len; + int n = recvfrom(nc->sock, buf, len, 0, &sa->sa, &sa_len_st); + *sa_len = sa_len_st; + if (n < 0 && !mg_is_error()) n = 0; + return n; } int mg_socket_if_create_conn(struct mg_connection *nc) { @@ -3411,14 +3594,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 MG_ENABLE_SSL - if (lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); - } else -#endif - { - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } + mg_if_accept_tcp_cb(nc, &sa, sa_len); return 1; } @@ -3469,207 +3645,42 @@ static sock_t mg_open_listening_socket(union socket_address *sa, int type, return sock; } -static void mg_write_to_socket(struct mg_connection *nc) { - struct mbuf *io = &nc->send_mbuf; - int n = 0; - -#if MG_LWIP - /* With LWIP we don't know if the socket is ready */ - if (io->len == 0) return; -#endif - - assert(io->len > 0); +#define _MG_F_FD_CAN_READ 1 +#define _MG_F_FD_CAN_WRITE 1 << 1 +#define _MG_F_FD_ERROR 1 << 2 - if (nc->flags & MG_F_UDP) { - int n = - 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, mg_get_errno(), - inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); - mg_if_sent_cb(nc, n); - return; +void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { + int worth_logging = + fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); + if (worth_logging) { + DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, + fd_flags, nc->flags, (int) nc->recv_mbuf.len, + (int) nc->send_mbuf.len)); } -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - n = mg_ssl_if_write(nc, io->buf, io->len); - DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); - if (n < 0) { - if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; + if (!mg_if_poll(nc, now)) return; + + if (nc->flags & MG_F_CONNECTING) { + if (fd_flags != 0) { + int err = 0; +#if !defined(MG_ESP8266) + if (!(nc->flags & MG_F_UDP)) { + socklen_t len = sizeof(err); + int ret = + getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + if (ret != 0) { + err = 1; + } else if (err == EAGAIN || err == EWOULDBLOCK) { + err = 0; } - return; - } else { - /* Successful SSL operation, clear off SSL wait flags */ - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); } - } else { - mg_ssl_begin(nc); - return; - } - } else +#else + /* + * On ESP8266 we use blocking connect. + */ + err = nc->err; #endif - { - n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); - DBG(("%p %d bytes -> %d", nc, n, nc->sock)); - } - - 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; - } - -#if MG_ENABLE_SSL - if (conn->flags & MG_F_SSL) { - if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { - /* SSL library may have more bytes ready to read than we ask to read. - * Therefore, read in a loop until we read everything. Without the loop, - * we skip to the next select() cycle which can just timeout. */ - while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { - DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - buf = NULL; - if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; - /* buf has been freed, we need a new one. */ - buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); - if (buf == NULL) break; - } - MG_FREE(buf); - if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } else { - MG_FREE(buf); - mg_ssl_begin(conn); - return; - } - } else -#endif - { - n = (int) MG_RECV_FUNC(conn->sock, buf, - recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); - DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); - if (n > 0) { - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - } else { - MG_FREE(buf); - } - if (n == 0) { - /* Orderly shutdown of the socket, try flushing output. */ - conn->flags |= MG_F_SEND_AND_CLOSE; - } else if (n < 0 && mg_is_error()) { - conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } -} - -static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, - socklen_t *sa_len, char **buf) { - int n; - *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); - if (*buf == NULL) { - DBG(("Out of memory")); - return -ENOMEM; - } - n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); - if (n <= 0) { - DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); - MG_FREE(*buf); - } - return n; -} - -static void mg_handle_udp_read(struct mg_connection *nc) { - char *buf = NULL; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - int n = mg_recvfrom(nc, &sa, &sa_len, &buf); - DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); -} - -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc) { - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); - DBG(("%p %d res %d", nc, server_side, res)); - - if (res == MG_SSL_OK) { - nc->flags |= MG_F_SSL_HANDSHAKE_DONE; - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); - - if (server_side) { - union socket_address sa; - socklen_t sa_len = sizeof(sa); - (void) getpeername(nc->sock, &sa.sa, &sa_len); - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } else { - mg_if_connect_cb(nc, 0); - } - } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { - if (!server_side) { - mg_if_connect_cb(nc, res); - } - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} -#endif /* MG_ENABLE_SSL */ - -#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) { - int worth_logging = - fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); - if (worth_logging) { - 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 (fd_flags != 0) { - int err = 0; -#if !defined(MG_ESP8266) - if (!(nc->flags & MG_F_UDP)) { - socklen_t len = sizeof(err); - int ret = - getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); - if (ret != 0) { - err = 1; - } else if (err == EAGAIN || err == EWOULDBLOCK) { - err = 0; - } - } -#else - /* - * On ESP8266 we use blocking connect. - */ - err = nc->err; -#endif -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && err == 0) { - mg_ssl_begin(nc); - } else { - mg_if_connect_cb(nc, err); - } -#else mg_if_connect_cb(nc, err); -#endif } else if (nc->err != 0) { mg_if_connect_cb(nc, nc->err); } @@ -3677,7 +3688,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { if (fd_flags & _MG_F_FD_CAN_READ) { if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); + mg_if_can_recv_cb(nc); } else { if (nc->flags & MG_F_LISTENING) { /* @@ -3687,22 +3698,16 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { */ mg_accept_conn(nc); } else { - mg_handle_tcp_read(nc); + mg_if_can_recv_cb(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); - } - mg_if_poll(nc, (time_t) now); - mg_if_timer(nc, now); - } + if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc); if (worth_logging) { - 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)); + DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, + nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } } @@ -3816,8 +3821,7 @@ time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { } #endif - if (!(nc->flags & MG_F_WANT_WRITE) && - nc->recv_mbuf.len < nc->recv_mbuf_limit && + if (nc->recv_mbuf.len < nc->recv_mbuf_limit && (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { mg_add_to_set(nc->sock, &read_set, &max_fd); } @@ -3888,14 +3892,6 @@ time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { 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 (time_t) now; } @@ -4004,7 +4000,8 @@ void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, mg_socket_if_connect_udp, \ mg_socket_if_tcp_send, \ mg_socket_if_udp_send, \ - mg_socket_if_recved, \ + mg_socket_if_tcp_recv, \ + mg_socket_if_udp_recv, \ mg_socket_if_create_conn, \ mg_socket_if_destroy_conn, \ mg_socket_if_sock_set, \ @@ -4032,18 +4029,35 @@ struct socksdata { char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ struct mg_connection *s; /* Respective connection to the server */ struct mg_connection *c; /* Connection to the client */ - struct mbuf tmp; /* Temporary buffer for sent data */ }; static void socks_if_disband(struct socksdata *d) { LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); - if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; - if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; - d->c = d->s = NULL; + if (d->c) { + d->c->flags |= MG_F_SEND_AND_CLOSE; + d->c->user_data = NULL; + d->c = NULL; + } + if (d->s) { + d->s->flags |= MG_F_SEND_AND_CLOSE; + d->s->user_data = NULL; + d->s = NULL; + } +} + +static void socks_if_relay(struct mg_connection *s) { + struct socksdata *d = (struct socksdata *) s->user_data; + if (d == NULL || d->c == NULL || !(s->flags & MG_SOCKS_CONNECT_DONE) || + d->s == NULL) { + return; + } + if (s->recv_mbuf.len > 0) mg_if_can_recv_cb(d->c); + if (d->c->send_mbuf.len > 0 && s->send_mbuf.len == 0) mg_if_can_send_cb(d->c); } static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { struct socksdata *d = (struct socksdata *) c->user_data; + if (d == NULL) return; if (ev == MG_EV_CONNECT) { int res = *(int *) ev_data; if (res == 0) { @@ -4076,6 +4090,7 @@ static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); mg_send(c, buf, sizeof(buf)); + LOG(LL_DEBUG, ("%p Sent connect request", c)); } /* Process connect request */ if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && @@ -4088,17 +4103,12 @@ static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { } mbuf_remove(&c->recv_mbuf, 10); c->flags |= MG_SOCKS_CONNECT_DONE; - /* Connected. Move sent data from client, if any, to server */ - if (d->s && d->c) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_free(&d->tmp); - } - } - /* All flags are set, we're in relay mode */ - if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { - mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); - mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); + LOG(LL_DEBUG, ("%p Connect done %p", c, d->c)); + mg_if_connect_cb(d->c, 0); } + socks_if_relay(c); + } else if (ev == MG_EV_SEND || ev == MG_EV_POLL) { + socks_if_relay(c); } } @@ -4108,7 +4118,7 @@ static void mg_socks_if_connect_tcp(struct mg_connection *c, d->c = c; d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); d->s->user_data = d; - LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); + LOG(LL_DEBUG, ("%p %s %p %p", c, d->proxy_addr, d, d->s)); (void) sa; } @@ -4130,29 +4140,44 @@ static int mg_socks_if_listen_udp(struct mg_connection *c, return -1; } -static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, - size_t len) { +static int mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, + size_t len) { + int res; struct socksdata *d = (struct socksdata *) c->iface->data; - LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); - if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_append(&d->s->send_mbuf, buf, len); - mbuf_free(&d->tmp); - } else { - mbuf_append(&d->tmp, buf, len); - } + if (d->s == NULL) return -1; + res = (int) mbuf_append(&d->s->send_mbuf, buf, len); + DBG(("%p -> %d -> %p", c, res, d->s)); + return res; } -static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, - size_t len) { +static int mg_socks_if_udp_send(struct mg_connection *c, const void *buf, + size_t len) { (void) c; (void) buf; (void) len; + return -1; +} + +int mg_socks_if_tcp_recv(struct mg_connection *c, void *buf, size_t len) { + struct socksdata *d = (struct socksdata *) c->iface->data; + if (d->s == NULL) return -1; + if (len > d->s->recv_mbuf.len) len = d->s->recv_mbuf.len; + if (len > 0) { + memcpy(buf, d->s->recv_mbuf.buf, len); + mbuf_remove(&d->s->recv_mbuf, len); + } + DBG(("%p <- %d <- %p", c, (int) len, d->s)); + return len; } -static void mg_socks_if_recved(struct mg_connection *c, size_t len) { +int mg_socks_if_udp_recv(struct mg_connection *c, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { (void) c; + (void) buf; (void) len; + (void) sa; + (void) sa_len; + return -1; } static int mg_socks_if_create_conn(struct mg_connection *c) { @@ -4181,7 +4206,6 @@ static void mg_socks_if_free(struct mg_iface *iface) { LOG(LL_DEBUG, ("%p", iface)); if (d != NULL) { socks_if_disband(d); - mbuf_free(&d->tmp); MG_FREE(d->proxy_addr); MG_FREE(d); iface->data = NULL; @@ -4212,14 +4236,15 @@ static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, } const struct mg_iface_vtable mg_socks_iface_vtable = { - mg_socks_if_init, mg_socks_if_free, - mg_socks_if_add_conn, mg_socks_if_remove_conn, - mg_socks_if_poll, mg_socks_if_listen_tcp, - mg_socks_if_listen_udp, mg_socks_if_connect_tcp, - mg_socks_if_connect_udp, mg_socks_if_tcp_send, - mg_socks_if_udp_send, mg_socks_if_recved, - mg_socks_if_create_conn, mg_socks_if_destroy_conn, - mg_socks_if_sock_set, mg_socks_if_get_conn_addr, + mg_socks_if_init, mg_socks_if_free, + mg_socks_if_add_conn, mg_socks_if_remove_conn, + mg_socks_if_poll, mg_socks_if_listen_tcp, + mg_socks_if_listen_udp, mg_socks_if_connect_tcp, + mg_socks_if_connect_udp, mg_socks_if_tcp_send, + mg_socks_if_udp_send, mg_socks_if_tcp_recv, + mg_socks_if_udp_recv, mg_socks_if_create_conn, + mg_socks_if_destroy_conn, mg_socks_if_sock_set, + mg_socks_if_get_conn_addr, }; struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { @@ -4639,9 +4664,12 @@ const char *mg_set_ssl(struct mg_connection *nc, const char *cert, #include #include +#include #include #include +#include #include +#include static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, const char *str) { @@ -4651,6 +4679,8 @@ static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, cs_level = LL_ERROR; break; case 2: + cs_level = LL_INFO; + break; case 3: cs_level = LL_DEBUG; break; @@ -4670,12 +4700,14 @@ struct mg_ssl_if_ctx { mbedtls_pk_context *key; mbedtls_x509_crt *ca_cert; struct mbuf cipher_suites; + size_t saved_len; }; /* Must be provided by the platform. ctx is struct mg_connection. */ extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); void mg_ssl_if_init() { + LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL)); } enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, @@ -4796,40 +4828,41 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( return MG_SSL_OK; } -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); -#else -static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { +static int mg_ssl_if_mbed_send(void *ctx, const unsigned char *buf, + size_t len) { struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); + int n = nc->iface->vtable->tcp_send(nc, buf, len); + if (n > 0) return n; + if (n == 0) return MBEDTLS_ERR_SSL_WANT_WRITE; + return MBEDTLS_ERR_NET_SEND_FAILED; } -static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { +static int mg_ssl_if_mbed_recv(void *ctx, unsigned char *buf, size_t len) { struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); + int n = nc->iface->vtable->tcp_recv(nc, buf, len); + if (n > 0) return n; + if (n == 0) return MBEDTLS_ERR_SSL_WANT_READ; + return MBEDTLS_ERR_NET_RECV_FAILED; } -#endif static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, int ret) { - if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; - if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; - if (ret != - MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ - LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); + enum mg_ssl_if_result res = MG_SSL_OK; + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + res = MG_SSL_WANT_READ; + } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + res = MG_SSL_WANT_WRITE; + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + LOG(LL_DEBUG, ("%p TLS connection closed by peer", nc)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + res = MG_SSL_OK; + } else { + LOG(LL_ERROR, ("%p mbedTLS error: -0x%04x", nc, -ret)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + res = MG_SSL_ERROR; } nc->err = ret; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return MG_SSL_ERROR; + return res; } static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { @@ -4860,7 +4893,8 @@ enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { int err; /* If bio is not yet set, do it now. */ if (ctx->ssl->p_bio == NULL) { - mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); + mbedtls_ssl_set_bio(ctx->ssl, nc, mg_ssl_if_mbed_send, mg_ssl_if_mbed_recv, + NULL); } err = mbedtls_ssl_handshake(ctx->ssl); if (err != 0) return mg_ssl_if_mbed_err(nc, err); @@ -4886,20 +4920,35 @@ enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { return MG_SSL_OK; } -int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t len) { struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); - DBG(("%p %d -> %d", nc, (int) buf_size, n)); + int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, len); + DBG(("%p %d -> %d", nc, (int) len, n)); if (n < 0) return mg_ssl_if_mbed_err(nc, n); if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; return n; } -int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { +int mg_ssl_if_write(struct mg_connection *nc, const void *buf, size_t len) { struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); - DBG(("%p %d -> %d", nc, (int) len, n)); - if (n < 0) return mg_ssl_if_mbed_err(nc, n); + /* Per mbedTLS docs, if write returns WANT_READ or WANT_WRITE, the operation + * should be retried with the same data and length. + * Here we assume that the data being pushed will remain the same but the + * amount may grow between calls so we save the length that was used and + * retry. The assumption being that the data itself won't change and won't + * be removed. */ + size_t l = len; + if (ctx->saved_len > 0 && ctx->saved_len < l) l = ctx->saved_len; + int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) buf, l); + DBG(("%p %d,%d,%d -> %d", nc, (int) len, (int) ctx->saved_len, (int) l, n)); + if (n < 0) { + if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { + ctx->saved_len = len; + } + return mg_ssl_if_mbed_err(nc, n); + } else if (n > 0) { + ctx->saved_len = 0; + } return n; } @@ -11557,7 +11606,6 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, time_t now = (time_t) mg_time(); struct mg_resolve_async_request *req; struct mg_dns_message *msg; - int first = 0; #if !MG_ENABLE_CALLBACK_USERDATA void *user_data = nc->user_data; #endif @@ -11571,17 +11619,16 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, } switch (ev) { - case MG_EV_CONNECT: - /* don't depend on timer not being at epoch for sending out first req */ - first = 1; - /* fallthrough */ case MG_EV_POLL: if (req->retries > req->max_retries) { req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; nc->flags |= MG_F_CLOSE_IMMEDIATELY; break; } - if (first || now - req->last_time >= req->timeout) { + if (nc->flags & MG_F_CONNECTING) break; + /* fallthrough */ + case MG_EV_CONNECT: + if (req->retries == 0 || now - req->last_time >= req->timeout) { mg_send_dns_query(nc, req->name, req->query); req->last_time = now; req->retries++; @@ -13752,7 +13799,7 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, union socket_address *sa, int type, int proto); -void mg_set_non_blocking_mode(sock_t sock) { +static void mg_set_non_blocking_mode(sock_t sock) { SlSockNonblocking_t opt; #if SL_MAJOR_VERSION_NUM < 2 opt.NonblockingEnabled = 1; @@ -13766,17 +13813,19 @@ static int mg_is_error(int n) { return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); } -void mg_sl_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { +static void mg_sl_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { int proto = 0; +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; +#endif sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); if (sock < 0) { nc->err = sock; goto out; } mg_sock_set(nc, sock); -#if MG_ENABLE_SSL +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK nc->err = sl_set_ssl_opts(sock, nc); if (nc->err != 0) goto out; #endif @@ -13786,7 +13835,7 @@ void mg_sl_if_connect_tcp(struct mg_connection *nc, ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); } -void mg_sl_if_connect_udp(struct mg_connection *nc) { +static void mg_sl_if_connect_udp(struct mg_connection *nc) { sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { nc->err = sock; @@ -13796,7 +13845,8 @@ void mg_sl_if_connect_udp(struct mg_connection *nc) { nc->err = 0; } -int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { +static int mg_sl_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(nc, sa, SOCK_STREAM, proto); @@ -13805,27 +13855,50 @@ int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { return 0; } -int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { +static int mg_sl_if_listen_udp(struct mg_connection *nc, + union socket_address *sa) { sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) return (errno ? errno : 1); mg_sock_set(nc, sock); return 0; } -void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = (int) sl_Send(nc->sock, buf, len, 0); + if (n < 0 && !mg_is_error(n)) n = 0; + return n; } -void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = sl_SendTo(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + if (n < 0 && !mg_is_error(n)) n = 0; + return n; } -void mg_sl_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; +static int mg_sl_if_tcp_recv(struct mg_connection *nc, void *buf, size_t len) { + int n = sl_Recv(nc->sock, buf, len, 0); + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + nc->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && !mg_is_error(n)) { + n = 0; + } + return n; } -int mg_sl_if_create_conn(struct mg_connection *nc) { +static int mg_sl_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { + SlSocklen_t sa_len_t = *sa_len; + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) sa, &sa_len_t); + *sa_len = sa_len_t; + if (n < 0 && !mg_is_error(n)) n = 0; + return n; +} + +static int mg_sl_if_create_conn(struct mg_connection *nc) { (void) nc; return 1; } @@ -13856,7 +13929,6 @@ 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 (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; mg_if_accept_tcp_cb(nc, &sa, sa_len); return 1; } @@ -13870,11 +13942,11 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, (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 sock; +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK + if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; +#endif if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; if (type != SOCK_DGRAM) { -#if MG_ENABLE_SSL - if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; -#endif if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; } mg_set_non_blocking_mode(sock); @@ -13886,86 +13958,18 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, 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) { - 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))); - } 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_if_sent_cb(nc, n); - } else if (n < 0 && mg_is_error(n)) { - /* Something went wrong, drop the connection. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -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, 1 /* own */); - } 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, + DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + if (!mg_if_poll(nc, now)) return; + if (nc->flags & MG_F_CONNECTING) { - if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { + if ((nc->flags & MG_F_UDP) || nc->err != SL_ERROR_BSD_EALREADY) { mg_if_connect_cb(nc, nc->err); } else { /* In SimpleLink, to get status of non-blocking connect() we need to wait @@ -13987,9 +13991,6 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { ) { 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); } } @@ -13999,28 +14000,21 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { if (fd_flags & _MG_F_FD_CAN_READ) { if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); + mg_if_can_recv_cb(nc); } else { if (nc->flags & MG_F_LISTENING) { mg_accept_conn(nc); } else { - mg_handle_tcp_read(nc); + mg_if_can_recv_cb(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); + if (fd_flags & _MG_F_FD_CAN_WRITE) { + mg_if_can_send_cb(nc); } - DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } @@ -14141,14 +14135,6 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { 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; } @@ -14199,7 +14185,8 @@ void sl_restart_cb(struct mg_mgr *mgr) { mg_sl_if_connect_udp, \ mg_sl_if_tcp_send, \ mg_sl_if_udp_send, \ - mg_sl_if_recved, \ + mg_sl_if_tcp_recv, \ + mg_sl_if_udp_recv, \ mg_sl_if_create_conn, \ mg_sl_if_destroy_conn, \ mg_sl_if_sock_set, \ @@ -14272,6 +14259,31 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( return MG_SSL_OK; } +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + /* SimpleLink does everything for us, nothing for us to do. */ + (void) nc; + (void) lc; + return MG_SSL_OK; +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + /* SimpleLink has already performed the handshake, nothing to do. */ + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t len) { + /* SimpelLink handles TLS, so this is just a pass-through. */ + int n = nc->iface->vtable->tcp_recv(nc, buf, len); + if (n == 0) nc->flags |= MG_F_WANT_READ; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *buf, size_t len) { + /* SimpelLink handles TLS, so this is just a pass-through. */ + return nc->iface->vtable->tcp_send(nc, buf, len); +} + void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { /* Nothing to do */ (void) nc; @@ -14372,57 +14384,60 @@ int sl_set_ssl_opts(int sock, struct mg_connection *nc) { const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; DBG(("%p ssl ctx: %p", nc, ctx)); - if (ctx != NULL) { - DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), - (ctx->ssl_key ? ctx->ssl_cert : "-"), - (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), - (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); - if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { - char *ssl_cert = sl_pem2der(ctx->ssl_cert); - char *ssl_key = sl_pem2der(ctx->ssl_key); - if (ssl_cert != NULL && ssl_key != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, - strlen(ssl_cert)); - LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + if (ctx == NULL) return 0; + DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), + (ctx->ssl_key ? ctx->ssl_cert : "-"), + (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), + (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); + if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { + char *ssl_cert = sl_pem2der(ctx->ssl_cert), *ssl_key = NULL; + if (ssl_cert != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, + strlen(ssl_cert)); + MG_FREE(ssl_cert); + LOG(LL_DEBUG, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + ssl_key = sl_pem2der(ctx->ssl_key); + if (ssl_key != NULL) { err = sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, strlen(ssl_key)); - LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); + MG_FREE(ssl_key); + LOG(LL_DEBUG, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); } else { err = -1; } - MG_FREE(ssl_cert); - MG_FREE(ssl_key); - if (err != 0) return err; - } - if (ctx->ssl_ca_cert != NULL) { - if (ctx->ssl_ca_cert[0] != '\0') { - char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); - if (ssl_ca_cert != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, - strlen(ssl_ca_cert)); - LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); - } else { - err = -1; - } - MG_FREE(ssl_ca_cert); - if (err != 0) return err; + } else { + err = -1; + } + if (err != 0) return err; + } + if (ctx->ssl_ca_cert != NULL) { + if (ctx->ssl_ca_cert[0] != '\0') { + char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); + if (ssl_ca_cert != NULL) { + err = + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_SECURE_FILES_CA_FILE_NAME, + ssl_ca_cert, strlen(ssl_ca_cert)); + LOG(LL_DEBUG, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); + } else { + err = -1; } - } - if (ctx->ssl_server_name != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, - ctx->ssl_server_name, strlen(ctx->ssl_server_name)); - DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); - /* Domain name verificationw as added in a NWP service pack, older - * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do - * about it, - * so we ignore the error. */ - if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; + MG_FREE(ssl_ca_cert); + if (err != 0) return err; } } + if (ctx->ssl_server_name != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, + ctx->ssl_server_name, strlen(ctx->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); + /* Domain name verificationw as added in a NWP service pack, older + * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do + * about it, + * so we ignore the error. */ + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; + } return 0; } @@ -14462,9 +14477,9 @@ struct mg_lwip_conn_state { /* Last SSL write size, for retries. */ int last_ssl_write_size; /* Whether MG_SIG_RECV is already pending for this connection */ - int recv_pending : 1; + int recv_pending; /* Whether the connection is about to close, just `rx_chain` needs to drain */ - int draining_rx_chain : 1; + int draining_rx_chain; }; enum mg_sig_type { @@ -14545,10 +14560,6 @@ void mg_lwip_mgr_schedule_poll(struct mg_mgr *mgr); typedef void (*tcpip_callback_fn)(void *arg); #endif -void mg_lwip_ssl_do_hs(struct mg_connection *nc); -void mg_lwip_ssl_send(struct mg_connection *nc); -void mg_lwip_ssl_recv(struct mg_connection *nc); - void mg_lwip_if_init(struct mg_iface *iface); void mg_lwip_if_free(struct mg_iface *iface); void mg_lwip_if_add_conn(struct mg_connection *nc); @@ -14620,10 +14631,12 @@ static void mg_lwip_tcp_error_cb(void *arg, err_t err) { static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); + struct mg_lwip_conn_state *cs = + (nc ? (struct mg_lwip_conn_state *) nc->sock : NULL); + DBG(("%p %p %p %p %u %d", nc, cs, tpcb, p, (p != NULL ? p->tot_len : 0), + err)); if (p == NULL) { if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; if (cs->rx_chain != NULL) { /* * rx_chain still contains non-consumed data, don't close the @@ -14641,7 +14654,6 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, tcp_abort(tpcb); return ERR_ARG; } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; /* * If we get a chain of more than one segment at once, we need to bump * refcount on the subsequent bufs to make them independent. @@ -14656,7 +14668,7 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, } else if (pbuf_clen(cs->rx_chain) >= 4) { /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX * will be completely blocked. We already have at least 4 in the chain, - * this one is, so we have to make a copy and release this one. */ + * this one is the last, so we have to make a copy and release this one. */ struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (np != NULL) { pbuf_copy(np, p); @@ -14664,58 +14676,9 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, p = np; } } - mgos_unlock(); mg_lwip_recv_common(nc, p); - return ERR_OK; -} - -static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->rx_chain == NULL) return; -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - return; - } -#endif - mgos_lock(); - while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { - struct pbuf *seg = cs->rx_chain; - - size_t seg_len = (seg->len - cs->rx_offset); - size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); - size_t len = MIN(seg_len, buf_avail); - - char *data = (char *) MG_MALLOC(len); - if (data == NULL) { - mgos_unlock(); - DBG(("OOM")); - return; - } - pbuf_copy_partial(seg, data, len, cs->rx_offset); - cs->rx_offset += len; - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); - mgos_lock(); - } mgos_unlock(); -} - -static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { - mg_lwip_consume_rx_chain_tcp(nc); - - if (nc->send_mbuf.len > 0) { - mg_lwip_mgr_schedule_poll(nc->mgr); - } + return ERR_OK; } static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, @@ -14801,13 +14764,14 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, /* Logic in the recv handler requires that there be exactly one data pbuf. */ p = pbuf_coalesce(p, PBUF_RAW); pbuf_chain(sap, p); + mgos_lock(); mg_lwip_recv_common(nc, sap); + mgos_unlock(); (void) pcb; } static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - mgos_lock(); if (cs->rx_chain == NULL) { cs->rx_chain = p; } else { @@ -14817,32 +14781,30 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { cs->recv_pending = 1; mg_lwip_post_signal(MG_SIG_RECV, nc); } - mgos_unlock(); } -static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; +static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { /* * For UDP, RX chain consists of interleaved address and packet bufs: * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). */ - while (cs->rx_chain != NULL) { - struct pbuf *sap = cs->rx_chain; - struct pbuf *p = sap->next; - cs->rx_chain = pbuf_dechain(p); - size_t data_len = p->len; - char *data = (char *) MG_MALLOC(data_len); - if (data != NULL) { - pbuf_copy_partial(p, data, data_len, 0); - pbuf_free(p); - mg_if_recv_udp_cb(nc, data, data_len, - (union socket_address *) sap->payload, sap->len); - pbuf_free(sap); - } else { - pbuf_free(p); - pbuf_free(sap); - } + int res = 0; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET) return -1; + mgos_lock(); + if (cs->rx_chain != NULL) { + struct pbuf *ap = cs->rx_chain; + struct pbuf *dp = ap->next; + cs->rx_chain = pbuf_dechain(dp); + res = MIN(dp->len, len); + pbuf_copy_partial(dp, buf, res, 0); + pbuf_free(dp); + pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0); + pbuf_free(ap); } + mgos_unlock(); + return res; } static void mg_lwip_if_connect_udp_tcpip(void *arg) { @@ -14864,13 +14826,6 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) { tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); } -void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { - union socket_address sa; - SET_ADDR(&sa, &tpcb->remote_ip); - sa.sin.sin_port = htons(tpcb->remote_port); - mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); -} - static void tcp_close_tcpip(void *arg) { tcp_close((struct tcp_pcb *) arg); } @@ -14878,17 +14833,11 @@ static void tcp_close_tcpip(void *arg) { void mg_lwip_handle_accept(struct mg_connection *nc) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; if (cs->pcb.tcp == NULL) return; -#if MG_ENABLE_SSL - if (cs->lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { - LOG(LL_ERROR, ("SSL error")); - tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); - } - } else -#endif - { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } + union socket_address sa; + struct tcp_pcb *tpcb = cs->pcb.tcp; + SET_ADDR(&sa, &tpcb->remote_ip); + sa.sin.sin_port = htons(tpcb->remote_port); + mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); } static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { @@ -15045,14 +14994,13 @@ static void mg_lwip_tcp_write_tcpip(void *arg) { ctx->ret = len; } -static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, - uint16_t len) { - struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; +int mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = buf, .len = len}; struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET) return -1; struct tcp_pcb *tpcb = cs->pcb.tcp; - if (tpcb == NULL) { - return -1; - } + if (tpcb == NULL) return -1; + if (tpcb->snd_buf <= 0) return 0; tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); return ctx.ret; } @@ -15070,18 +15018,10 @@ static void udp_sendto_tcpip(void *arg) { ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); } -static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, - uint16_t len) { +static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data, + size_t len) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->pcb.udp == NULL) { - /* - * In case of UDP, this usually means, what - * async DNS resolve is still in progress and connection - * is not ready yet - */ - DBG(("%p socket is not connected", nc)); - return -1; - } + if (nc->sock == INVALID_SOCKET || cs->pcb.udp == NULL) return -1; struct udp_pcb *upcb = cs->pcb.udp; struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); #if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 @@ -15090,46 +15030,28 @@ static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; #endif u16_t port = ntohs(nc->sa.sin.sin_port); - if (p == NULL) { - DBG(("OOM")); - return 0; - } + if (p == NULL) return 0; memcpy(p->payload, data, len); struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; tcpip_callback(udp_sendto_tcpip, &ctx); cs->err = ctx.ret; pbuf_free(p); - return (cs->err == ERR_OK ? len : -1); + return (cs->err == ERR_OK ? (int) len : -2); } -static void mg_lwip_send_more(struct mg_connection *nc) { - int num_sent = 0; - if (nc->sock == INVALID_SOCKET) return; - if (nc->flags & MG_F_UDP) { - num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); - } else { - num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); - } - if (num_sent == 0) return; - if (num_sent > 0) { - mg_if_sent_cb(nc, num_sent); - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); +static int mg_lwip_if_can_send(struct mg_connection *nc, + struct mg_lwip_conn_state *cs) { + int can_send = 0; + if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) { + /* We have stuff to send, but can we? */ + if (nc->flags & MG_F_UDP) { + /* UDP is always ready for sending. */ + can_send = (cs->pcb.udp != NULL); + } else { + can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); + } } -} - -void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); -} - -void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); + return can_send; } struct tcp_recved_ctx { @@ -15142,27 +15064,35 @@ void tcp_recved_tcpip(void *arg) { tcp_recved(ctx->tpcb, ctx->len); } -void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { - if (nc->flags & MG_F_UDP) return; +static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf, + size_t len) { + int res = 0; + char *bufp = buf; struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { - DBG(("%p invalid socket", nc)); - return; + if (nc->sock == INVALID_SOCKET) return -1; + mgos_lock(); + while (cs->rx_chain != NULL && len > 0) { + struct pbuf *seg = cs->rx_chain; + size_t seg_len = (seg->len - cs->rx_offset); + size_t copy_len = MIN(len, seg_len); + + pbuf_copy_partial(seg, bufp, copy_len, cs->rx_offset); + len -= copy_len; + res += copy_len; + bufp += copy_len; + cs->rx_offset += copy_len; + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } } - DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, - (cs->rx_chain ? cs->rx_chain->tot_len : 0))); - struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; -#if MG_ENABLE_SSL - if (!(nc->flags & MG_F_SSL)) { + mgos_unlock(); + if (res > 0) { + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = res}; tcpip_callback(tcp_recved_tcpip, &ctx); - } else { - /* Currently SSL acknowledges data immediately. - * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ } -#else - tcpip_callback(tcp_recved_tcpip, &ctx); -#endif - mbuf_trim(&nc->recv_mbuf); + return res; } int mg_lwip_if_create_conn(struct mg_connection *nc) { @@ -15251,7 +15181,8 @@ void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { mg_lwip_if_connect_udp, \ mg_lwip_if_tcp_send, \ mg_lwip_if_udp_send, \ - mg_lwip_if_recved, \ + mg_lwip_if_tcp_recv, \ + mg_lwip_if_udp_recv, \ mg_lwip_if_create_conn, \ mg_lwip_if_destroy_conn, \ mg_lwip_if_sock_set, \ @@ -15320,15 +15251,7 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { if (nc->iface == NULL || nc->mgr == NULL) continue; switch (sig) { case MG_SIG_CONNECT_RESULT: { -#if MG_ENABLE_SSL - if (cs->err == 0 && (nc->flags & MG_F_SSL) && - !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_lwip_ssl_do_hs(nc); - } else -#endif - { - mg_if_connect_cb(nc, cs->err); - } + mg_if_connect_cb(nc, cs->err); break; } case MG_SIG_CLOSE_CONN: { @@ -15338,11 +15261,8 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } case MG_SIG_RECV: { cs->recv_pending = 0; - if (nc->flags & MG_F_UDP) { - mg_lwip_handle_recv_udp(nc); - } else { - mg_lwip_handle_recv_tcp(nc); - } + mg_if_can_recv_cb(nc); + mbuf_trim(&nc->recv_mbuf); break; } case MG_SIG_TOMBSTONE: { @@ -15357,7 +15277,8 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } void mg_lwip_if_init(struct mg_iface *iface) { - LOG(LL_INFO, ("%p Mongoose init", iface)); + LOG(LL_INFO, ("Mongoose %s, LwIP %u.%u.%u", MG_VERSION, LWIP_VERSION_MAJOR, + LWIP_VERSION_MINOR, LWIP_VERSION_REVISION)); iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); } @@ -15396,42 +15317,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; tmp = nc->next; n++; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && - (nc->send_mbuf.len == 0))) { - mg_close_conn(nc); - continue; - } - mg_if_poll(nc, now); - mg_if_timer(nc, now); -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && - cs->pcb.tcp->state == ESTABLISHED) { - if (((nc->flags & MG_F_WANT_WRITE) || - ((nc->send_mbuf.len > 0) && - (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && - cs->pcb.tcp->snd_buf > 0) { - /* Can write more. */ - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - } else -#endif /* MG_ENABLE_SSL */ - { - if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { - mg_lwip_send_more(nc); - } - } + if (!mg_if_poll(nc, now)) continue; if (nc->sock != INVALID_SOCKET && !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && cs->pcb.tcp->unsent != NULL) { @@ -15445,14 +15331,17 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { } if (nc->sock != INVALID_SOCKET) { - /* Try to consume data from cs->rx_chain */ - mg_lwip_consume_rx_chain_tcp(nc); - - /* - * If the connection is about to close, and rx_chain is finally empty, - * send the MG_SIG_CLOSE_CONN signal - */ - if (cs->draining_rx_chain && cs->rx_chain == NULL) { + if (mg_lwip_if_can_send(nc, cs)) { + mg_if_can_send_cb(nc); + mbuf_trim(&nc->send_mbuf); + } + if (cs->rx_chain != NULL) { + mg_if_can_recv_cb(nc); + } else if (cs->draining_rx_chain) { + /* + * If the connection is about to close, and rx_chain is finally empty, + * send the MG_SIG_CLOSE_CONN signal + */ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } } @@ -15480,21 +15369,9 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { } num_timers++; } - if (nc->send_mbuf.len > 0 -#if MG_ENABLE_SSL - || (nc->flags & MG_F_WANT_WRITE) -#endif - ) { - int can_send = 0; - /* We have stuff to send, but can we? */ - if (nc->flags & MG_F_UDP) { - /* UDP is always ready for sending. */ - can_send = (cs->pcb.udp != NULL); - } else { - can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); - } - /* We want and can send, request a poll immediately. */ - if (can_send) return 0; + /* We want and can send data, request a poll immediately. */ + if (nc->sock != INVALID_SOCKET && mg_lwip_if_can_send(nc, cs)) { + return 0; } } uint32_t timeout_ms = ~0; @@ -15512,206 +15389,6 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { #endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ #ifdef MG_MODULE_LINES -#line 1 "common/platforms/lwip/mg_lwip_ssl_if.c" -#endif -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL - -/* Amalgamated: #include "common/mg_mem.h" */ -/* Amalgamated: #include "common/cs_dbg.h" */ - -#include -#include - -#ifndef MG_LWIP_SSL_IO_SIZE -#define MG_LWIP_SSL_IO_SIZE 1024 -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -void mg_lwip_ssl_do_hs(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res; - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; - res = mg_ssl_if_handshake(nc); - DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); - if (res != MG_SSL_OK) { - if (res == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - cs->err = 0; - } else if (res == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - } else { - cs->err = res; - if (server_side) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } - } else { - cs->err = 0; - nc->flags &= ~MG_F_WANT_WRITE; - /* - * Handshake is done. Schedule a read immediately to consume app data - * which may already be waiting. - */ - nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); - if (server_side) { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } -} - -void mg_lwip_ssl_send(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) { - DBG(("%p invalid socket", nc)); - return; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ - int len = cs->last_ssl_write_size; - if (len == 0) { - len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); - } - int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); - DBG(("%p SSL_write %u = %d", nc, len, ret)); - if (ret > 0) { - mg_if_sent_cb(nc, ret); - cs->last_ssl_write_size = 0; - } else if (ret < 0) { - /* This is tricky. We must remember the exact data we were sending to retry - * exactly the same send next time. */ - cs->last_ssl_write_size = len; - } - if (ret == len) { - nc->flags &= ~MG_F_WANT_WRITE; - } else if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } -} - -void mg_lwip_ssl_recv(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* Don't deliver data before connect callback */ - if (nc->flags & MG_F_CONNECTING) return; - while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { - char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); - if (buf == NULL) return; - int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); - DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); - if (ret <= 0) { - MG_FREE(buf); - if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - return; - } else if (ret == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - return; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - return; - } - } else { - mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); - } - } -} - -#ifdef KR_VERSION - -ssize_t kr_send(int fd, const void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); - if (ret == 0) ret = KR_IO_WOULDBLOCK; - return ret; -} - -ssize_t kr_recv(int fd, void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return KR_IO_WOULDBLOCK; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - return len; -} - -#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS - -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; - LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); - return ret; -} - -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return MBEDTLS_ERR_SSL_WANT_READ; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - mgos_lock(); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - /* TCP PCB may be NULL if connection has already been closed - * but we still have data to deliver to SSL. */ - if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); - return len; -} - -#endif - -#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ -#ifdef MG_MODULE_LINES #line 1 "common/platforms/wince/wince_libc.c" #endif /* diff --git a/mongoose.h b/mongoose.h index c668594a50..e9b1fb1718 100644 --- a/mongoose.h +++ b/mongoose.h @@ -3245,10 +3245,12 @@ struct mg_iface_vtable { void (*connect_udp)(struct mg_connection *nc); /* Send functions for TCP and UDP. Sent data is copied before return. */ - void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); - void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); + int (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); + int (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); - void (*recved)(struct mg_connection *nc, size_t len); + int (*tcp_recv)(struct mg_connection *nc, void *buf, size_t len); + int (*udp_recv)(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len); /* Perform interface-related connection initialization. Return 1 on ok. */ int (*create_conn)(struct mg_connection *nc); @@ -3289,19 +3291,15 @@ void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, /* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ void mg_if_connect_cb(struct mg_connection *nc, int err); -/* Callback that reports that data has been put on the wire. */ -void mg_if_sent_cb(struct mg_connection *nc, int num_sent); /* - * Receive callback. - * if `own` is true, buf must be heap-allocated and ownership is transferred - * to the core. - * Core will acknowledge consumption by calling iface::recved. + * Callback that tells the core that data can be received. + * Core will use tcp/udp_recv to retrieve the data. */ -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); +void mg_if_can_recv_cb(struct mg_connection *nc); +void mg_if_can_send_cb(struct mg_connection *nc); /* * Receive callback. * buf must be heap-allocated and ownership is transferred to the core. - * Core will acknowledge consumption by calling iface::recved. */ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, union socket_address *sa, size_t sa_len); @@ -3309,10 +3307,7 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, /* void mg_if_close_conn(struct mg_connection *nc); */ /* Deliver a POLL event to the connection. */ -void mg_if_poll(struct mg_connection *nc, time_t now); - -/* Deliver a TIMER event to the connection. */ -void mg_if_timer(struct mg_connection *c, double now); +int mg_if_poll(struct mg_connection *nc, double now); #ifdef __cplusplus } diff --git a/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/common/platforms/lwip/mg_lwip_ev_mgr.c index 5eea26f3cb..11a1bfc183 100644 --- a/src/common/platforms/lwip/mg_lwip_ev_mgr.c +++ b/src/common/platforms/lwip/mg_lwip_ev_mgr.c @@ -50,15 +50,7 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { if (nc->iface == NULL || nc->mgr == NULL) continue; switch (sig) { case MG_SIG_CONNECT_RESULT: { -#if MG_ENABLE_SSL - if (cs->err == 0 && (nc->flags & MG_F_SSL) && - !(nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_lwip_ssl_do_hs(nc); - } else -#endif - { - mg_if_connect_cb(nc, cs->err); - } + mg_if_connect_cb(nc, cs->err); break; } case MG_SIG_CLOSE_CONN: { @@ -68,11 +60,8 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } case MG_SIG_RECV: { cs->recv_pending = 0; - if (nc->flags & MG_F_UDP) { - mg_lwip_handle_recv_udp(nc); - } else { - mg_lwip_handle_recv_tcp(nc); - } + mg_if_can_recv_cb(nc); + mbuf_trim(&nc->recv_mbuf); break; } case MG_SIG_TOMBSTONE: { @@ -87,7 +76,8 @@ void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) { } void mg_lwip_if_init(struct mg_iface *iface) { - LOG(LL_INFO, ("%p Mongoose init", iface)); + LOG(LL_INFO, ("Mongoose %s, LwIP %u.%u.%u", MG_VERSION, LWIP_VERSION_MAJOR, + LWIP_VERSION_MINOR, LWIP_VERSION_REVISION)); iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data)); } @@ -126,42 +116,7 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; tmp = nc->next; n++; - if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || - ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) && - (nc->send_mbuf.len == 0))) { - mg_close_conn(nc); - continue; - } - mg_if_poll(nc, now); - mg_if_timer(nc, now); -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL && - cs->pcb.tcp->state == ESTABLISHED) { - if (((nc->flags & MG_F_WANT_WRITE) || - ((nc->send_mbuf.len > 0) && - (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) && - cs->pcb.tcp->snd_buf > 0) { - /* Can write more. */ - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - } - } else -#endif /* MG_ENABLE_SSL */ - { - if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) { - mg_lwip_send_more(nc); - } - } + if (!mg_if_poll(nc, now)) continue; if (nc->sock != INVALID_SOCKET && !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && cs->pcb.tcp->unsent != NULL) { @@ -175,14 +130,17 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { } if (nc->sock != INVALID_SOCKET) { - /* Try to consume data from cs->rx_chain */ - mg_lwip_consume_rx_chain_tcp(nc); - - /* - * If the connection is about to close, and rx_chain is finally empty, - * send the MG_SIG_CLOSE_CONN signal - */ - if (cs->draining_rx_chain && cs->rx_chain == NULL) { + if (mg_lwip_if_can_send(nc, cs)) { + mg_if_can_send_cb(nc); + mbuf_trim(&nc->send_mbuf); + } + if (cs->rx_chain != NULL) { + mg_if_can_recv_cb(nc); + } else if (cs->draining_rx_chain) { + /* + * If the connection is about to close, and rx_chain is finally empty, + * send the MG_SIG_CLOSE_CONN signal + */ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } } @@ -210,21 +168,9 @@ uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) { } num_timers++; } - if (nc->send_mbuf.len > 0 -#if MG_ENABLE_SSL - || (nc->flags & MG_F_WANT_WRITE) -#endif - ) { - int can_send = 0; - /* We have stuff to send, but can we? */ - if (nc->flags & MG_F_UDP) { - /* UDP is always ready for sending. */ - can_send = (cs->pcb.udp != NULL); - } else { - can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); - } - /* We want and can send, request a poll immediately. */ - if (can_send) return 0; + /* We want and can send data, request a poll immediately. */ + if (nc->sock != INVALID_SOCKET && mg_lwip_if_can_send(nc, cs)) { + return 0; } } uint32_t timeout_ms = ~0; diff --git a/src/common/platforms/lwip/mg_lwip_net_if.c b/src/common/platforms/lwip/mg_lwip_net_if.c index 460f8f5af1..b9042e837c 100644 --- a/src/common/platforms/lwip/mg_lwip_net_if.c +++ b/src/common/platforms/lwip/mg_lwip_net_if.c @@ -57,10 +57,6 @@ typedef void (*tcpip_callback_fn)(void *arg); #endif -void mg_lwip_ssl_do_hs(struct mg_connection *nc); -void mg_lwip_ssl_send(struct mg_connection *nc); -void mg_lwip_ssl_recv(struct mg_connection *nc); - void mg_lwip_if_init(struct mg_iface *iface); void mg_lwip_if_free(struct mg_iface *iface); void mg_lwip_if_add_conn(struct mg_connection *nc); @@ -132,10 +128,12 @@ static void mg_lwip_tcp_error_cb(void *arg, err_t err) { static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; - DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err)); + struct mg_lwip_conn_state *cs = + (nc ? (struct mg_lwip_conn_state *) nc->sock : NULL); + DBG(("%p %p %p %p %u %d", nc, cs, tpcb, p, (p != NULL ? p->tot_len : 0), + err)); if (p == NULL) { if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; if (cs->rx_chain != NULL) { /* * rx_chain still contains non-consumed data, don't close the @@ -153,7 +151,6 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, tcp_abort(tpcb); return ERR_ARG; } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; /* * If we get a chain of more than one segment at once, we need to bump * refcount on the subsequent bufs to make them independent. @@ -168,7 +165,7 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, } else if (pbuf_clen(cs->rx_chain) >= 4) { /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX * will be completely blocked. We already have at least 4 in the chain, - * this one is, so we have to make a copy and release this one. */ + * this one is the last, so we have to make a copy and release this one. */ struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (np != NULL) { pbuf_copy(np, p); @@ -176,58 +173,9 @@ static err_t mg_lwip_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, p = np; } } - mgos_unlock(); mg_lwip_recv_common(nc, p); - return ERR_OK; -} - -static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->rx_chain == NULL) return; -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - mg_lwip_ssl_recv(nc); - } else { - mg_lwip_ssl_do_hs(nc); - } - return; - } -#endif - mgos_lock(); - while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) { - struct pbuf *seg = cs->rx_chain; - - size_t seg_len = (seg->len - cs->rx_offset); - size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len); - size_t len = MIN(seg_len, buf_avail); - - char *data = (char *) MG_MALLOC(len); - if (data == NULL) { - mgos_unlock(); - DBG(("OOM")); - return; - } - pbuf_copy_partial(seg, data, len, cs->rx_offset); - cs->rx_offset += len; - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - mg_if_recv_tcp_cb(nc, data, len, 1 /* own */); - mgos_lock(); - } mgos_unlock(); -} - -static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) { - mg_lwip_consume_rx_chain_tcp(nc); - - if (nc->send_mbuf.len > 0) { - mg_lwip_mgr_schedule_poll(nc->mgr); - } + return ERR_OK; } static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, @@ -313,13 +261,14 @@ static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, /* Logic in the recv handler requires that there be exactly one data pbuf. */ p = pbuf_coalesce(p, PBUF_RAW); pbuf_chain(sap, p); + mgos_lock(); mg_lwip_recv_common(nc, sap); + mgos_unlock(); (void) pcb; } static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - mgos_lock(); if (cs->rx_chain == NULL) { cs->rx_chain = p; } else { @@ -329,32 +278,30 @@ static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) { cs->recv_pending = 1; mg_lwip_post_signal(MG_SIG_RECV, nc); } - mgos_unlock(); } -static void mg_lwip_handle_recv_udp(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; +static int mg_lwip_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { /* * For UDP, RX chain consists of interleaved address and packet bufs: * Address pbuf followed by exactly one data pbuf (recv_cb took care of that). */ - while (cs->rx_chain != NULL) { - struct pbuf *sap = cs->rx_chain; - struct pbuf *p = sap->next; - cs->rx_chain = pbuf_dechain(p); - size_t data_len = p->len; - char *data = (char *) MG_MALLOC(data_len); - if (data != NULL) { - pbuf_copy_partial(p, data, data_len, 0); - pbuf_free(p); - mg_if_recv_udp_cb(nc, data, data_len, - (union socket_address *) sap->payload, sap->len); - pbuf_free(sap); - } else { - pbuf_free(p); - pbuf_free(sap); - } + int res = 0; + struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET) return -1; + mgos_lock(); + if (cs->rx_chain != NULL) { + struct pbuf *ap = cs->rx_chain; + struct pbuf *dp = ap->next; + cs->rx_chain = pbuf_dechain(dp); + res = MIN(dp->len, len); + pbuf_copy_partial(dp, buf, res, 0); + pbuf_free(dp); + pbuf_copy_partial(ap, sa, MIN(*sa_len, ap->len), 0); + pbuf_free(ap); } + mgos_unlock(); + return res; } static void mg_lwip_if_connect_udp_tcpip(void *arg) { @@ -376,13 +323,6 @@ void mg_lwip_if_connect_udp(struct mg_connection *nc) { tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc); } -void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) { - union socket_address sa; - SET_ADDR(&sa, &tpcb->remote_ip); - sa.sin.sin_port = htons(tpcb->remote_port); - mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); -} - static void tcp_close_tcpip(void *arg) { tcp_close((struct tcp_pcb *) arg); } @@ -390,17 +330,11 @@ static void tcp_close_tcpip(void *arg) { void mg_lwip_handle_accept(struct mg_connection *nc) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; if (cs->pcb.tcp == NULL) return; -#if MG_ENABLE_SSL - if (cs->lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) { - LOG(LL_ERROR, ("SSL error")); - tcpip_callback(tcp_close_tcpip, cs->pcb.tcp); - } - } else -#endif - { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } + union socket_address sa; + struct tcp_pcb *tpcb = cs->pcb.tcp; + SET_ADDR(&sa, &tpcb->remote_ip); + sa.sin.sin_port = htons(tpcb->remote_port); + mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin)); } static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) { @@ -557,14 +491,13 @@ static void mg_lwip_tcp_write_tcpip(void *arg) { ctx->ret = len; } -static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data, - uint16_t len) { - struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len}; +int mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { + struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = buf, .len = len}; struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; + if (nc->sock == INVALID_SOCKET) return -1; struct tcp_pcb *tpcb = cs->pcb.tcp; - if (tpcb == NULL) { - return -1; - } + if (tpcb == NULL) return -1; + if (tpcb->snd_buf <= 0) return 0; tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx); return ctx.ret; } @@ -582,18 +515,10 @@ static void udp_sendto_tcpip(void *arg) { ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port); } -static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, - uint16_t len) { +static int mg_lwip_if_udp_send(struct mg_connection *nc, const void *data, + size_t len) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (cs->pcb.udp == NULL) { - /* - * In case of UDP, this usually means, what - * async DNS resolve is still in progress and connection - * is not ready yet - */ - DBG(("%p socket is not connected", nc)); - return -1; - } + if (nc->sock == INVALID_SOCKET || cs->pcb.udp == NULL) return -1; struct udp_pcb *upcb = cs->pcb.udp; struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); #if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6 @@ -602,46 +527,28 @@ static int mg_lwip_udp_send(struct mg_connection *nc, const void *data, ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr}; #endif u16_t port = ntohs(nc->sa.sin.sin_port); - if (p == NULL) { - DBG(("OOM")); - return 0; - } + if (p == NULL) return 0; memcpy(p->payload, data, len); struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port}; tcpip_callback(udp_sendto_tcpip, &ctx); cs->err = ctx.ret; pbuf_free(p); - return (cs->err == ERR_OK ? len : -1); + return (cs->err == ERR_OK ? (int) len : -2); } -static void mg_lwip_send_more(struct mg_connection *nc) { - int num_sent = 0; - if (nc->sock == INVALID_SOCKET) return; - if (nc->flags & MG_F_UDP) { - num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent)); - } else { - num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len); - DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent)); - } - if (num_sent == 0) return; - if (num_sent > 0) { - mg_if_sent_cb(nc, num_sent); - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); +static int mg_lwip_if_can_send(struct mg_connection *nc, + struct mg_lwip_conn_state *cs) { + int can_send = 0; + if (nc->send_mbuf.len > 0 || (nc->flags & MG_F_WANT_WRITE)) { + /* We have stuff to send, but can we? */ + if (nc->flags & MG_F_UDP) { + /* UDP is always ready for sending. */ + can_send = (cs->pcb.udp != NULL); + } else { + can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0); + } } -} - -void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); -} - -void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); - mg_lwip_mgr_schedule_poll(nc->mgr); + return can_send; } struct tcp_recved_ctx { @@ -654,27 +561,35 @@ void tcp_recved_tcpip(void *arg) { tcp_recved(ctx->tpcb, ctx->len); } -void mg_lwip_if_recved(struct mg_connection *nc, size_t len) { - if (nc->flags & MG_F_UDP) return; +static int mg_lwip_if_tcp_recv(struct mg_connection *nc, void *buf, + size_t len) { + int res = 0; + char *bufp = buf; struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) { - DBG(("%p invalid socket", nc)); - return; + if (nc->sock == INVALID_SOCKET) return -1; + mgos_lock(); + while (cs->rx_chain != NULL && len > 0) { + struct pbuf *seg = cs->rx_chain; + size_t seg_len = (seg->len - cs->rx_offset); + size_t copy_len = MIN(len, seg_len); + + pbuf_copy_partial(seg, bufp, copy_len, cs->rx_offset); + len -= copy_len; + res += copy_len; + bufp += copy_len; + cs->rx_offset += copy_len; + if (cs->rx_offset == cs->rx_chain->len) { + cs->rx_chain = pbuf_dechain(cs->rx_chain); + pbuf_free(seg); + cs->rx_offset = 0; + } } - DBG(("%p %p %u %u", nc, cs->pcb.tcp, len, - (cs->rx_chain ? cs->rx_chain->tot_len : 0))); - struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len}; -#if MG_ENABLE_SSL - if (!(nc->flags & MG_F_SSL)) { + mgos_unlock(); + if (res > 0) { + struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = res}; tcpip_callback(tcp_recved_tcpip, &ctx); - } else { - /* Currently SSL acknowledges data immediately. - * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */ } -#else - tcpip_callback(tcp_recved_tcpip, &ctx); -#endif - mbuf_trim(&nc->recv_mbuf); + return res; } int mg_lwip_if_create_conn(struct mg_connection *nc) { @@ -763,7 +678,8 @@ void mg_lwip_if_sock_set(struct mg_connection *nc, sock_t sock) { mg_lwip_if_connect_udp, \ mg_lwip_if_tcp_send, \ mg_lwip_if_udp_send, \ - mg_lwip_if_recved, \ + mg_lwip_if_tcp_recv, \ + mg_lwip_if_udp_recv, \ mg_lwip_if_create_conn, \ mg_lwip_if_destroy_conn, \ mg_lwip_if_sock_set, \ diff --git a/src/common/platforms/lwip/mg_lwip_net_if.h b/src/common/platforms/lwip/mg_lwip_net_if.h index d00fd2fdd0..d94cfdf6e4 100644 --- a/src/common/platforms/lwip/mg_lwip_net_if.h +++ b/src/common/platforms/lwip/mg_lwip_net_if.h @@ -30,9 +30,9 @@ struct mg_lwip_conn_state { /* Last SSL write size, for retries. */ int last_ssl_write_size; /* Whether MG_SIG_RECV is already pending for this connection */ - int recv_pending : 1; + int recv_pending; /* Whether the connection is about to close, just `rx_chain` needs to drain */ - int draining_rx_chain : 1; + int draining_rx_chain; }; enum mg_sig_type { diff --git a/src/common/platforms/lwip/mg_lwip_ssl_if.c b/src/common/platforms/lwip/mg_lwip_ssl_if.c deleted file mode 100644 index 04ed7784cc..0000000000 --- a/src/common/platforms/lwip/mg_lwip_ssl_if.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL - -#include "common/mg_mem.h" -#include "common/cs_dbg.h" - -#include -#include - -#ifndef MG_LWIP_SSL_IO_SIZE -#define MG_LWIP_SSL_IO_SIZE 1024 -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -void mg_lwip_ssl_do_hs(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res; - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return; - res = mg_ssl_if_handshake(nc); - DBG(("%p %lu %d %d", nc, nc->flags, server_side, res)); - if (res != MG_SSL_OK) { - if (res == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - cs->err = 0; - } else if (res == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - } else { - cs->err = res; - if (server_side) { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } - } else { - cs->err = 0; - nc->flags &= ~MG_F_WANT_WRITE; - /* - * Handshake is done. Schedule a read immediately to consume app data - * which may already be waiting. - */ - nc->flags |= (MG_F_SSL_HANDSHAKE_DONE | MG_F_WANT_READ); - if (server_side) { - mg_lwip_accept_conn(nc, cs->pcb.tcp); - } else { - mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); - } - } -} - -void mg_lwip_ssl_send(struct mg_connection *nc) { - if (nc->sock == INVALID_SOCKET) { - DBG(("%p invalid socket", nc)); - return; - } - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* It's ok if the buffer is empty. Return value of 0 may also be valid. */ - int len = cs->last_ssl_write_size; - if (len == 0) { - len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len); - } - int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len); - DBG(("%p SSL_write %u = %d", nc, len, ret)); - if (ret > 0) { - mg_if_sent_cb(nc, ret); - cs->last_ssl_write_size = 0; - } else if (ret < 0) { - /* This is tricky. We must remember the exact data we were sending to retry - * exactly the same send next time. */ - cs->last_ssl_write_size = len; - } - if (ret == len) { - nc->flags &= ~MG_F_WANT_WRITE; - } else if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - } -} - -void mg_lwip_ssl_recv(struct mg_connection *nc) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - /* Don't deliver data before connect callback */ - if (nc->flags & MG_F_CONNECTING) return; - while (nc->recv_mbuf.len < nc->recv_mbuf_limit) { - char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE); - if (buf == NULL) return; - int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE); - DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret)); - if (ret <= 0) { - MG_FREE(buf); - if (ret == MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_WANT_WRITE; - return; - } else if (ret == MG_SSL_WANT_READ) { - /* - * Nothing to do in particular, we are callback-driven. - * What we definitely do not need anymore is SSL reading (nothing left). - */ - nc->flags &= ~MG_F_WANT_READ; - cs->err = 0; - return; - } else { - mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); - return; - } - } else { - mg_if_recv_tcp_cb(nc, buf, ret, 1 /* own */); - } - } -} - -#ifdef KR_VERSION - -ssize_t kr_send(int fd, const void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret)); - if (ret == 0) ret = KR_IO_WOULDBLOCK; - return ret; -} - -ssize_t kr_recv(int fd, void *buf, size_t len) { - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) fd; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return KR_IO_WOULDBLOCK; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - return len; -} - -#elif MG_SSL_IF == MG_SSL_IF_MBEDTLS - -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - int ret = mg_lwip_tcp_write(cs->nc, buf, len); - if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE; - LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret)); - return ret; -} - -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { - struct mg_connection *nc = (struct mg_connection *) ctx; - struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; - struct pbuf *seg = cs->rx_chain; - if (seg == NULL) { - DBG(("%u - nothing to read", len)); - return MBEDTLS_ERR_SSL_WANT_READ; - } - size_t seg_len = (seg->len - cs->rx_offset); - DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len)); - mgos_lock(); - len = MIN(len, seg_len); - pbuf_copy_partial(seg, buf, len, cs->rx_offset); - cs->rx_offset += len; - /* TCP PCB may be NULL if connection has already been closed - * but we still have data to deliver to SSL. */ - if (cs->pcb.tcp != NULL) tcp_recved(cs->pcb.tcp, len); - if (cs->rx_offset == cs->rx_chain->len) { - cs->rx_chain = pbuf_dechain(cs->rx_chain); - pbuf_free(seg); - cs->rx_offset = 0; - } - mgos_unlock(); - LOG(LL_DEBUG, ("%p <- %d", nc, (int) len)); - return len; -} - -#endif - -#endif /* MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */ diff --git a/src/common/platforms/platform_cc3220.h b/src/common/platforms/platform_cc3220.h index a32264a70a..6992e142f8 100644 --- a/src/common/platforms/platform_cc3220.h +++ b/src/common/platforms/platform_cc3220.h @@ -22,7 +22,9 @@ #endif #define MG_NET_IF MG_NET_IF_SIMPLELINK +#ifndef MG_SSL_IF #define MG_SSL_IF MG_SSL_IF_SIMPLELINK +#endif /* Only SPIFFS supports directories, SLFS does not. */ #if defined(CC3220_FS_SPIFFS) && !defined(MG_ENABLE_DIRECTORY_LISTING) diff --git a/src/common/platforms/simplelink/sl_net_if.c b/src/common/platforms/simplelink/sl_net_if.c index 2a39d1405a..fcc5632b26 100644 --- a/src/common/platforms/simplelink/sl_net_if.c +++ b/src/common/platforms/simplelink/sl_net_if.c @@ -17,7 +17,7 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, union socket_address *sa, int type, int proto); -void mg_set_non_blocking_mode(sock_t sock) { +static void mg_set_non_blocking_mode(sock_t sock) { SlSockNonblocking_t opt; #if SL_MAJOR_VERSION_NUM < 2 opt.NonblockingEnabled = 1; @@ -31,17 +31,19 @@ static int mg_is_error(int n) { return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN); } -void mg_sl_if_connect_tcp(struct mg_connection *nc, - const union socket_address *sa) { +static void mg_sl_if_connect_tcp(struct mg_connection *nc, + const union socket_address *sa) { int proto = 0; +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET; +#endif sock_t sock = sl_Socket(AF_INET, SOCK_STREAM, proto); if (sock < 0) { nc->err = sock; goto out; } mg_sock_set(nc, sock); -#if MG_ENABLE_SSL +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK nc->err = sl_set_ssl_opts(sock, nc); if (nc->err != 0) goto out; #endif @@ -51,7 +53,7 @@ void mg_sl_if_connect_tcp(struct mg_connection *nc, ntohs(sa->sin.sin_port), nc->sock, proto, nc->err)); } -void mg_sl_if_connect_udp(struct mg_connection *nc) { +static void mg_sl_if_connect_udp(struct mg_connection *nc) { sock_t sock = sl_Socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { nc->err = sock; @@ -61,7 +63,8 @@ void mg_sl_if_connect_udp(struct mg_connection *nc) { nc->err = 0; } -int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { +static int mg_sl_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(nc, sa, SOCK_STREAM, proto); @@ -70,27 +73,50 @@ int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) { return 0; } -int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) { +static int mg_sl_if_listen_udp(struct mg_connection *nc, + union socket_address *sa) { sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) return (errno ? errno : 1); mg_sock_set(nc, sock); return 0; } -void mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_sl_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = (int) sl_Send(nc->sock, buf, len, 0); + if (n < 0 && !mg_is_error(n)) n = 0; + return n; } -void mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_sl_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = sl_SendTo(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + if (n < 0 && !mg_is_error(n)) n = 0; + return n; } -void mg_sl_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; +static int mg_sl_if_tcp_recv(struct mg_connection *nc, void *buf, size_t len) { + int n = sl_Recv(nc->sock, buf, len, 0); + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + nc->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && !mg_is_error(n)) { + n = 0; + } + return n; } -int mg_sl_if_create_conn(struct mg_connection *nc) { +static int mg_sl_if_udp_recv(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { + SlSocklen_t sa_len_t = *sa_len; + int n = sl_RecvFrom(nc->sock, buf, MG_UDP_RECV_BUFFER_SIZE, 0, + (SlSockAddr_t *) sa, &sa_len_t); + *sa_len = sa_len_t; + if (n < 0 && !mg_is_error(n)) n = 0; + return n; +} + +static int mg_sl_if_create_conn(struct mg_connection *nc) { (void) nc; return 1; } @@ -121,7 +147,6 @@ 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 (nc->flags & MG_F_SSL) nc->flags |= MG_F_SSL_HANDSHAKE_DONE; mg_if_accept_tcp_cb(nc, &sa, sa_len); return 1; } @@ -135,11 +160,11 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, (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 sock; +#if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK + if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; +#endif if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean; if (type != SOCK_DGRAM) { -#if MG_ENABLE_SSL - if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean; -#endif if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean; } mg_set_non_blocking_mode(sock); @@ -151,86 +176,18 @@ static sock_t mg_open_listening_socket(struct mg_connection *nc, 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) { - 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))); - } 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_if_sent_cb(nc, n); - } else if (n < 0 && mg_is_error(n)) { - /* Something went wrong, drop the connection. */ - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} - -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, 1 /* own */); - } 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, + DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); + if (!mg_if_poll(nc, now)) return; + if (nc->flags & MG_F_CONNECTING) { - if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) { + if ((nc->flags & MG_F_UDP) || nc->err != SL_ERROR_BSD_EALREADY) { mg_if_connect_cb(nc, nc->err); } else { /* In SimpleLink, to get status of non-blocking connect() we need to wait @@ -252,9 +209,6 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { ) { 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); } } @@ -264,28 +218,21 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { if (fd_flags & _MG_F_FD_CAN_READ) { if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); + mg_if_can_recv_cb(nc); } else { if (nc->flags & MG_F_LISTENING) { mg_accept_conn(nc); } else { - mg_handle_tcp_read(nc); + mg_if_can_recv_cb(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); + if (fd_flags & _MG_F_FD_CAN_WRITE) { + mg_if_can_send_cb(nc); } - DBG(("%p after fd=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, nc->flags, + DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } @@ -406,14 +353,6 @@ time_t mg_sl_if_poll(struct mg_iface *iface, int timeout_ms) { 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; } @@ -464,7 +403,8 @@ void sl_restart_cb(struct mg_mgr *mgr) { mg_sl_if_connect_udp, \ mg_sl_if_tcp_send, \ mg_sl_if_udp_send, \ - mg_sl_if_recved, \ + mg_sl_if_tcp_recv, \ + mg_sl_if_udp_recv, \ mg_sl_if_create_conn, \ mg_sl_if_destroy_conn, \ mg_sl_if_sock_set, \ diff --git a/src/common/platforms/simplelink/sl_ssl_if.c b/src/common/platforms/simplelink/sl_ssl_if.c index 99642b4bdf..0cd9c25b80 100644 --- a/src/common/platforms/simplelink/sl_ssl_if.c +++ b/src/common/platforms/simplelink/sl_ssl_if.c @@ -54,6 +54,31 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( return MG_SSL_OK; } +enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, + struct mg_connection *lc) { + /* SimpleLink does everything for us, nothing for us to do. */ + (void) nc; + (void) lc; + return MG_SSL_OK; +} + +enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { + /* SimpleLink has already performed the handshake, nothing to do. */ + return MG_SSL_OK; +} + +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t len) { + /* SimpelLink handles TLS, so this is just a pass-through. */ + int n = nc->iface->vtable->tcp_recv(nc, buf, len); + if (n == 0) nc->flags |= MG_F_WANT_READ; + return n; +} + +int mg_ssl_if_write(struct mg_connection *nc, const void *buf, size_t len) { + /* SimpelLink handles TLS, so this is just a pass-through. */ + return nc->iface->vtable->tcp_send(nc, buf, len); +} + void mg_ssl_if_conn_close_notify(struct mg_connection *nc) { /* Nothing to do */ (void) nc; @@ -154,56 +179,59 @@ int sl_set_ssl_opts(int sock, struct mg_connection *nc) { const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; DBG(("%p ssl ctx: %p", nc, ctx)); - if (ctx != NULL) { - DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), - (ctx->ssl_key ? ctx->ssl_cert : "-"), - (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), - (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); - if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { - char *ssl_cert = sl_pem2der(ctx->ssl_cert); - char *ssl_key = sl_pem2der(ctx->ssl_key); - if (ssl_cert != NULL && ssl_key != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, - strlen(ssl_cert)); - LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + if (ctx == NULL) return 0; + DBG(("%p %s,%s,%s,%s", nc, (ctx->ssl_cert ? ctx->ssl_cert : "-"), + (ctx->ssl_key ? ctx->ssl_cert : "-"), + (ctx->ssl_ca_cert ? ctx->ssl_ca_cert : "-"), + (ctx->ssl_server_name ? ctx->ssl_server_name : "-"))); + if (ctx->ssl_cert != NULL && ctx->ssl_key != NULL) { + char *ssl_cert = sl_pem2der(ctx->ssl_cert), *ssl_key = NULL; + if (ssl_cert != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert, + strlen(ssl_cert)); + MG_FREE(ssl_cert); + LOG(LL_DEBUG, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err)); + ssl_key = sl_pem2der(ctx->ssl_key); + if (ssl_key != NULL) { err = sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key, strlen(ssl_key)); - LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); + MG_FREE(ssl_key); + LOG(LL_DEBUG, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err)); } else { err = -1; } - MG_FREE(ssl_cert); - MG_FREE(ssl_key); - if (err != 0) return err; + } else { + err = -1; } - if (ctx->ssl_ca_cert != NULL) { - if (ctx->ssl_ca_cert[0] != '\0') { - char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); - if (ssl_ca_cert != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert, - strlen(ssl_ca_cert)); - LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); - } else { - err = -1; - } - MG_FREE(ssl_ca_cert); - if (err != 0) return err; + if (err != 0) return err; + } + if (ctx->ssl_ca_cert != NULL) { + if (ctx->ssl_ca_cert[0] != '\0') { + char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert); + if (ssl_ca_cert != NULL) { + err = + sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_SECURE_FILES_CA_FILE_NAME, + ssl_ca_cert, strlen(ssl_ca_cert)); + LOG(LL_DEBUG, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err)); + } else { + err = -1; } + MG_FREE(ssl_ca_cert); + if (err != 0) return err; } - if (ctx->ssl_server_name != NULL) { - err = sl_SetSockOpt(sock, SL_SOL_SOCKET, - SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, - ctx->ssl_server_name, strlen(ctx->ssl_server_name)); - DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); - /* Domain name verificationw as added in a NWP service pack, older - * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do - * about it, - * so we ignore the error. */ - if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; - } + } + if (ctx->ssl_server_name != NULL) { + err = sl_SetSockOpt(sock, SL_SOL_SOCKET, + SL_SO_SECURE_DOMAIN_NAME_VERIFICATION, + ctx->ssl_server_name, strlen(ctx->ssl_server_name)); + DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err)); + /* Domain name verificationw as added in a NWP service pack, older + * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do + * about it, + * so we ignore the error. */ + if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err; } return 0; } diff --git a/src/mg_modules.mk b/src/mg_modules.mk index 235e0d5ffd..bf3f585250 100644 --- a/src/mg_modules.mk +++ b/src/mg_modules.mk @@ -96,7 +96,6 @@ SOURCES = $(COMMON)/mg_mem.h \ $(COMMON)/platforms/lwip/mg_lwip_net_if.h \ $(COMMON)/platforms/lwip/mg_lwip_net_if.c \ $(COMMON)/platforms/lwip/mg_lwip_ev_mgr.c \ - $(COMMON)/platforms/lwip/mg_lwip_ssl_if.c \ $(COMMON)/platforms/wince/wince_libc.c \ $(COMMON)/platforms/pic32/pic32_net_if.h \ $(COMMON)/platforms/pic32/pic32_net_if.c \ diff --git a/src/mg_net.c b/src/mg_net.c index 70e2cc84e2..050ac791b8 100644 --- a/src/mg_net.c +++ b/src/mg_net.c @@ -24,6 +24,13 @@ #define MG_MAX_HOST_LEN 200 +#ifndef MG_TCP_IO_SIZE +#define MG_TCP_IO_SIZE 1460 +#endif +#ifndef MG_UDP_IO_SIZE +#define MG_UDP_IO_SIZE 1460 +#endif + #define MG_COPY_COMMON_CONNECTION_OPTIONS(dst, src) \ memcpy(dst, src, sizeof(*dst)); @@ -64,8 +71,6 @@ MG_INTERNAL void mg_remove_conn(struct mg_connection *conn) { MG_INTERNAL void mg_call(struct mg_connection *nc, mg_event_handler_t ev_handler, void *user_data, int ev, void *ev_data) { - static int nesting_level = 0; - nesting_level++; if (ev_handler == NULL) { /* * If protocol handler is specified, call it. Otherwise, call user-specified @@ -74,7 +79,7 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, ev_handler = nc->proto_handler ? nc->proto_handler : nc->handler; } if (ev != MG_EV_POLL) { - DBG(("%p %s ev=%d ev_data=%p flags=%lu rmbl=%d smbl=%d", nc, + DBG(("%p %s ev=%d ev_data=%p flags=0x%lx rmbl=%d smbl=%d", nc, ev_handler == nc->handler ? "user" : "proto", ev, ev_data, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } @@ -87,33 +92,24 @@ MG_INTERNAL void mg_call(struct mg_connection *nc, #endif if (ev_handler != NULL) { unsigned long flags_before = nc->flags; - size_t recv_mbuf_before = nc->recv_mbuf.len, recved; ev_handler(nc, ev, ev_data MG_UD_ARG(user_data)); - recved = (recv_mbuf_before - nc->recv_mbuf.len); /* Prevent user handler from fiddling with system flags. */ if (ev_handler == nc->handler && nc->flags != flags_before) { nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) | (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK); } - /* It's important to not double-count recved bytes, and since mg_call can be - * called recursively (e.g. proto_handler invokes user handler), we keep - * track of recursion and only report received bytes at the top level. */ - if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) { - nc->iface->vtable->recved(nc, recved); - } } if (ev != MG_EV_POLL) { - DBG(("%p after %s flags=%lu rmbl=%d smbl=%d", nc, + DBG(("%p after %s flags=0x%lx rmbl=%d smbl=%d", nc, ev_handler == nc->handler ? "user" : "proto", nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } - nesting_level--; #if !MG_ENABLE_CALLBACK_USERDATA (void) user_data; #endif } -void mg_if_timer(struct mg_connection *c, double now) { +MG_INTERNAL void mg_timer(struct mg_connection *c, double now) { if (c->ev_timer_time > 0 && now >= c->ev_timer_time) { double old_value = c->ev_timer_time; c->ev_timer_time = 0; @@ -121,13 +117,45 @@ void mg_if_timer(struct mg_connection *c, double now) { } } -void mg_if_poll(struct mg_connection *nc, time_t now) { - if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) { - mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now); +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 int mg_do_recv(struct mg_connection *nc); + +int mg_if_poll(struct mg_connection *nc, double now) { + if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) || + (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) { + mg_close_conn(nc); + return 0; } +#if MG_ENABLE_SSL + if ((nc->flags & (MG_F_SSL | MG_F_LISTENING | MG_F_CONNECTING)) == MG_F_SSL) { + /* SSL library may have data to be delivered to the app in its buffers, + * drain them. */ + int recved = 0; + do { + if (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)) break; + if (recv_avail_size(nc, MG_TCP_IO_SIZE) <= 0) break; + recved = mg_do_recv(nc); + } while (recved > 0); + } +#endif /* MG_ENABLE_SSL */ + mg_timer(nc, now); + { + time_t now_t = (time_t) now; + mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now_t); + } + return 1; } void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { + if (conn->sock != INVALID_SOCKET) { /* Don't print timer-only conns */ + LOG(LL_DEBUG, ("%p 0x%lx %d", conn, conn->flags, destroy_if)); + } if (destroy_if) conn->iface->vtable->destroy_conn(conn); if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) { conn->proto_data_destructor(conn->proto_data); @@ -143,7 +171,6 @@ void mg_destroy_conn(struct mg_connection *conn, int destroy_if) { } void mg_close_conn(struct mg_connection *conn) { - DBG(("%p %lu %d", conn, conn->flags, conn->sock)); #if MG_ENABLE_SSL if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { mg_ssl_if_conn_close_notify(conn); @@ -180,15 +207,6 @@ void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, signal(SIGPIPE, SIG_IGN); #endif -#if MG_ENABLE_SSL - { - static int init_done; - if (!init_done) { - mg_ssl_if_init(); - init_done++; - } - } -#endif { int i; if (opts.num_ifaces == 0) { @@ -211,6 +229,15 @@ void mg_mgr_init_opt(struct mg_mgr *m, void *user_data, } DBG(("==================================")); DBG(("init mgr=%p", m)); +#if MG_ENABLE_SSL + { + static int init_done; + if (!init_done) { + mg_ssl_if_init(); + init_done++; + } + } +#endif } void mg_mgr_free(struct mg_mgr *m) { @@ -453,6 +480,37 @@ MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa, return port < 0xffffUL && (ch == '\0' || ch == ',' || isspace(ch)) ? len : -1; } +#if MG_ENABLE_SSL +MG_INTERNAL void mg_ssl_handshake(struct mg_connection *nc) { + int err = 0; + int server_side = (nc->listener != NULL); + enum mg_ssl_if_result res; + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) return; + res = mg_ssl_if_handshake(nc); + LOG(LL_DEBUG, ("%p %d res %d", nc, server_side, res)); + + if (res == MG_SSL_OK) { + nc->flags |= MG_F_SSL_HANDSHAKE_DONE; + nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); + if (server_side) { + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } else { + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } + } else if (res == MG_SSL_WANT_READ) { + nc->flags |= MG_F_WANT_READ; + } else if (res == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + } else { + if (!server_side) { + err = res; + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } +} +#endif /* MG_ENABLE_SSL */ + struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { struct mg_add_sock_opts opts; struct mg_connection *nc; @@ -466,95 +524,125 @@ struct mg_connection *mg_if_accept_new_conn(struct mg_connection *lc) { nc->iface = lc->iface; 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)); + LOG(LL_DEBUG, ("%p %p %d %d", lc, nc, nc->sock, (int) nc->flags)); return nc; } void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, size_t sa_len) { - (void) sa_len; + LOG(LL_DEBUG, ("%p %s://%s:%hu", nc, (nc->flags & MG_F_UDP ? "udp" : "tcp"), + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); nc->sa = *sa; - mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); +#if MG_ENABLE_SSL + if (nc->listener->flags & MG_F_SSL) { + nc->flags |= MG_F_SSL; + if (mg_ssl_if_conn_accept(nc, nc->listener) == MG_SSL_OK) { + mg_ssl_handshake(nc); + } else { + mg_close_conn(nc); + } + } else +#endif + { + mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); + } + (void) sa_len; } void mg_send(struct mg_connection *nc, const void *buf, int len) { nc->last_io_time = (time_t) mg_time(); - if (nc->flags & MG_F_UDP) { - nc->iface->vtable->udp_send(nc, buf, len); - } else { - nc->iface->vtable->tcp_send(nc, buf, len); - } + mbuf_append(&nc->send_mbuf, buf, len); } -void mg_if_sent_cb(struct mg_connection *nc, int num_sent) { - DBG(("%p %d", nc, num_sent)); -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - char *buf = nc->send_mbuf.buf; - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND); +static int mg_recv_tcp(struct mg_connection *nc, char *buf, size_t len); +static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len); + +static int mg_do_recv(struct mg_connection *nc) { + int res = 0; + char *buf = NULL; + size_t len = (nc->flags & MG_F_UDP ? MG_UDP_IO_SIZE : MG_TCP_IO_SIZE); + if ((nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_CONNECTING)) || + ((nc->flags & MG_F_LISTENING) && !(nc->flags & MG_F_UDP))) { + return -1; } -#endif - if (num_sent < 0) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; + len = recv_avail_size(nc, len); + if (len == 0) return -2; + if (nc->recv_mbuf.size < nc->recv_mbuf.len + len) { + mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.len + len); + } + buf = nc->recv_mbuf.buf + nc->recv_mbuf.len; + len = nc->recv_mbuf.size - nc->recv_mbuf.len; + if (nc->flags & MG_F_UDP) { + res = mg_recv_udp(nc, buf, len); } else { - mbuf_remove(&nc->send_mbuf, num_sent); - mbuf_trim(&nc->send_mbuf); + res = mg_recv_tcp(nc, buf, len); } - mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent); + return res; } -MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len, - int own) { - DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len)); - -#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP - if (nc->mgr && nc->mgr->hexdump_file != NULL) { - mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV); - } -#endif +void mg_if_can_recv_cb(struct mg_connection *nc) { + mg_do_recv(nc); +} - if (nc->flags & MG_F_CLOSE_IMMEDIATELY) { - DBG(("%p discarded %d bytes", nc, len)); - /* - * This connection will not survive next poll. Do not deliver events, - * send data to /dev/null without acking. - */ - if (own) { - MG_FREE(buf); +static int mg_recv_tcp(struct mg_connection *nc, char *buf, size_t len) { + int n = 0; +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + n = mg_ssl_if_read(nc, buf, len); + DBG(("%p <- %d bytes (SSL)", nc, n)); + if (n < 0) { + if (n == MG_SSL_WANT_READ) { + nc->flags |= MG_F_WANT_READ; + n = 0; + } else { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else if (n > 0) { + nc->flags &= ~MG_F_WANT_READ; + } + } else { + mg_ssl_handshake(nc); } - return; + } else +#endif + { + n = nc->iface->vtable->tcp_recv(nc, buf, len); + DBG(("%p <- %d bytes", nc, n)); } - nc->last_io_time = (time_t) mg_time(); - if (!own) { - mbuf_append(&nc->recv_mbuf, buf, len); - } else if (nc->recv_mbuf.len == 0) { - /* Adopt buf as recv_mbuf's backing store. */ - mbuf_free(&nc->recv_mbuf); - nc->recv_mbuf.buf = (char *) buf; - nc->recv_mbuf.size = nc->recv_mbuf.len = len; - } else { - mbuf_append(&nc->recv_mbuf, buf, len); - MG_FREE(buf); + if (n > 0) { + nc->recv_mbuf.len += n; + nc->last_io_time = (time_t) mg_time(); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV); + } +#endif + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n); + } else if (n < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; } - mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len); -} - -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) { - mg_recv_common(nc, buf, len, own); + return n; } -void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, - union socket_address *sa, size_t sa_len) { - assert(nc->flags & MG_F_UDP); - DBG(("%p %u", nc, (unsigned int) len)); +static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) { + int n = 0; + struct mg_connection *lc = nc; + union socket_address sa; + size_t sa_len = sizeof(sa); + n = nc->iface->vtable->udp_recv(lc, buf, len, &sa, &sa_len); + if (n < 0) { + lc->flags |= MG_F_CLOSE_IMMEDIATELY; + goto out; + } if (nc->flags & MG_F_LISTENING) { - struct mg_connection *lc = nc; /* * Do we have an existing connection for this source? * This is very inefficient for long connection lists. */ + lc = nc; for (nc = mg_next(lc->mgr, NULL); nc != NULL; nc = mg_next(lc->mgr, nc)) { - if (memcmp(&nc->sa.sa, &sa->sa, sa_len) == 0 && nc->listener == lc) { + if (memcmp(&nc->sa.sa, &sa.sa, sa_len) == 0 && nc->listener == lc) { break; } } @@ -566,7 +654,7 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, if (nc != NULL) { nc->sock = lc->sock; nc->listener = lc; - nc->sa = *sa; + nc->sa = sa; nc->proto_handler = lc->proto_handler; nc->user_data = lc->user_data; nc->recv_mbuf_limit = lc->recv_mbuf_limit; @@ -585,18 +673,88 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, nc->flags |= MG_F_SEND_AND_CLOSE; mg_add_conn(lc->mgr, nc); mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa); - } else { - DBG(("OOM")); - /* No return here, we still need to drop on the floor */ } } } if (nc != NULL) { - mg_recv_common(nc, buf, len, 1); - } else { - /* Drop on the floor. */ - MG_FREE(buf); + DBG(("%p <- %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), + ntohs(nc->sa.sin.sin_port))); + if (nc == lc) { + nc->recv_mbuf.len += n; + } else { + mbuf_append(&nc->recv_mbuf, buf, n); + } + mbuf_trim(&lc->recv_mbuf); + lc->last_io_time = nc->last_io_time = (time_t) mg_time(); +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV); + } +#endif + mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n); } + +out: + mbuf_free(&lc->recv_mbuf); + return n; +} + +void mg_if_can_send_cb(struct mg_connection *nc) { + int n = 0; + const char *buf = nc->send_mbuf.buf; + size_t len = nc->send_mbuf.len; + + if (nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_CONNECTING)) { + return; + } + if (!(nc->flags & MG_F_UDP)) { + if (nc->flags & MG_F_LISTENING) return; + if (len > MG_TCP_IO_SIZE) len = MG_TCP_IO_SIZE; + } +#if MG_ENABLE_SSL + if (nc->flags & MG_F_SSL) { + if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { + if (len > 0) { + n = mg_ssl_if_write(nc, buf, len); + DBG(("%p -> %d bytes (SSL)", nc, n)); + } + if (n < 0) { + if (n == MG_SSL_WANT_WRITE) { + nc->flags |= MG_F_WANT_WRITE; + n = 0; + } else { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } + } else { + nc->flags &= ~MG_F_WANT_WRITE; + } + } else { + mg_ssl_handshake(nc); + } + } else +#endif + { + if (nc->flags & MG_F_UDP) { + n = nc->iface->vtable->udp_send(nc, buf, len); + } else { + n = nc->iface->vtable->tcp_send(nc, buf, len); + } + DBG(("%p -> %d bytes", nc, n)); + } + +#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP + if (n > 0 && nc->mgr && nc->mgr->hexdump_file != NULL) { + mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_SEND); + } +#endif + if (n < 0) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + } else if (n > 0) { + nc->last_io_time = (time_t) mg_time(); + mbuf_remove(&nc->send_mbuf, n); + mbuf_trim(&nc->send_mbuf); + } + if (n != 0) mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &n); } /* @@ -608,8 +766,8 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, int proto, union socket_address *sa) { - DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", - inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); + LOG(LL_DEBUG, ("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp", + inet_ntoa(sa->sin.sin_addr), ntohs(sa->sin.sin_port))); nc->flags |= MG_F_CONNECTING; if (proto == SOCK_DGRAM) { @@ -622,12 +780,21 @@ MG_INTERNAL struct mg_connection *mg_do_connect(struct mg_connection *nc, } void mg_if_connect_cb(struct mg_connection *nc, int err) { - DBG(("%p connect, err=%d", nc, err)); + LOG(LL_DEBUG, + ("%p %s://%s:%hu -> %d", nc, (nc->flags & MG_F_UDP ? "udp" : "tcp"), + inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port), err)); nc->flags &= ~MG_F_CONNECTING; if (err != 0) { nc->flags |= MG_F_CLOSE_IMMEDIATELY; } - mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); +#if MG_ENABLE_SSL + if (err == 0 && (nc->flags & MG_F_SSL)) { + mg_ssl_handshake(nc); + } else +#endif + { + mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err); + } } #if MG_ENABLE_ASYNC_RESOLVER @@ -717,7 +884,8 @@ struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address, #endif #if MG_ENABLE_SSL - DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), + LOG(LL_DEBUG, + ("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"), (opts.ssl_key ? opts.ssl_key : "-"), (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-"))); @@ -963,7 +1131,7 @@ double mg_set_timer(struct mg_connection *c, double timestamp) { DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0), (unsigned long) timestamp)); if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) { - ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp; + mg_set_timer((struct mg_connection *) c->priv_2, timestamp); } return result; } diff --git a/src/mg_net_if.h b/src/mg_net_if.h index 43eb2d994c..cba58fb95f 100644 --- a/src/mg_net_if.h +++ b/src/mg_net_if.h @@ -54,10 +54,12 @@ struct mg_iface_vtable { void (*connect_udp)(struct mg_connection *nc); /* Send functions for TCP and UDP. Sent data is copied before return. */ - void (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); - void (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); + int (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len); + int (*udp_send)(struct mg_connection *nc, const void *buf, size_t len); - void (*recved)(struct mg_connection *nc, size_t len); + int (*tcp_recv)(struct mg_connection *nc, void *buf, size_t len); + int (*udp_recv)(struct mg_connection *nc, void *buf, size_t len, + union socket_address *sa, size_t *sa_len); /* Perform interface-related connection initialization. Return 1 on ok. */ int (*create_conn)(struct mg_connection *nc); @@ -98,19 +100,15 @@ void mg_if_accept_tcp_cb(struct mg_connection *nc, union socket_address *sa, /* Callback invoked by connect methods. err = 0 -> ok, != 0 -> error. */ void mg_if_connect_cb(struct mg_connection *nc, int err); -/* Callback that reports that data has been put on the wire. */ -void mg_if_sent_cb(struct mg_connection *nc, int num_sent); /* - * Receive callback. - * if `own` is true, buf must be heap-allocated and ownership is transferred - * to the core. - * Core will acknowledge consumption by calling iface::recved. + * Callback that tells the core that data can be received. + * Core will use tcp/udp_recv to retrieve the data. */ -void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own); +void mg_if_can_recv_cb(struct mg_connection *nc); +void mg_if_can_send_cb(struct mg_connection *nc); /* * Receive callback. * buf must be heap-allocated and ownership is transferred to the core. - * Core will acknowledge consumption by calling iface::recved. */ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, union socket_address *sa, size_t sa_len); @@ -118,10 +116,7 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len, /* void mg_if_close_conn(struct mg_connection *nc); */ /* Deliver a POLL event to the connection. */ -void mg_if_poll(struct mg_connection *nc, time_t now); - -/* Deliver a TIMER event to the connection. */ -void mg_if_timer(struct mg_connection *c, double now); +int mg_if_poll(struct mg_connection *nc, double now); #ifdef __cplusplus } diff --git a/src/mg_net_if_socket.c b/src/mg_net_if_socket.c index c13c300a42..c713e84580 100644 --- a/src/mg_net_if_socket.c +++ b/src/mg_net_if_socket.c @@ -9,14 +9,8 @@ #include "mg_internal.h" #include "mg_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); -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc); -#endif void mg_set_non_blocking_mode(sock_t sock) { #ifdef _WIN32 @@ -85,27 +79,48 @@ int mg_socket_if_listen_tcp(struct mg_connection *nc, return 0; } -int mg_socket_if_listen_udp(struct mg_connection *nc, - union socket_address *sa) { +static int mg_socket_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 (mg_get_errno() ? mg_get_errno() : 1); mg_sock_set(nc, sock); return 0; } -void mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_socket_if_tcp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); + if (n < 0 && !mg_is_error()) n = 0; + return n; } -void mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, - size_t len) { - mbuf_append(&nc->send_mbuf, buf, len); +static int mg_socket_if_udp_send(struct mg_connection *nc, const void *buf, + size_t len) { + int n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + if (n < 0 && !mg_is_error()) n = 0; + return n; } -void mg_socket_if_recved(struct mg_connection *nc, size_t len) { - (void) nc; - (void) len; +static int mg_socket_if_tcp_recv(struct mg_connection *nc, void *buf, + size_t len) { + int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); + if (n == 0) { + /* Orderly shutdown of the socket, try flushing output. */ + nc->flags |= MG_F_SEND_AND_CLOSE; + } else if (n < 0 && !mg_is_error()) { + n = 0; + } + return n; +} + +static int mg_socket_if_udp_recv(struct mg_connection *nc, void *buf, + size_t len, union socket_address *sa, + size_t *sa_len) { + socklen_t sa_len_st = *sa_len; + int n = recvfrom(nc->sock, buf, len, 0, &sa->sa, &sa_len_st); + *sa_len = sa_len_st; + if (n < 0 && !mg_is_error()) n = 0; + return n; } int mg_socket_if_create_conn(struct mg_connection *nc) { @@ -142,14 +157,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 MG_ENABLE_SSL - if (lc->flags & MG_F_SSL) { - if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) mg_close_conn(nc); - } else -#endif - { - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } + mg_if_accept_tcp_cb(nc, &sa, sa_len); return 1; } @@ -200,165 +208,6 @@ static sock_t mg_open_listening_socket(union socket_address *sa, int type, return sock; } -static void mg_write_to_socket(struct mg_connection *nc) { - struct mbuf *io = &nc->send_mbuf; - int n = 0; - -#if MG_LWIP - /* With LWIP we don't know if the socket is ready */ - if (io->len == 0) return; -#endif - - assert(io->len > 0); - - if (nc->flags & MG_F_UDP) { - int n = - 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, mg_get_errno(), - inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port))); - mg_if_sent_cb(nc, n); - return; - } - -#if MG_ENABLE_SSL - if (nc->flags & MG_F_SSL) { - if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { - n = mg_ssl_if_write(nc, io->buf, io->len); - DBG(("%p %d bytes -> %d (SSL)", nc, n, nc->sock)); - if (n < 0) { - if (n != MG_SSL_WANT_READ && n != MG_SSL_WANT_WRITE) { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } - return; - } else { - /* Successful SSL operation, clear off SSL wait flags */ - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); - } - } else { - mg_ssl_begin(nc); - return; - } - } else -#endif - { - n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0); - DBG(("%p %d bytes -> %d", nc, n, nc->sock)); - } - - 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; - } - -#if MG_ENABLE_SSL - if (conn->flags & MG_F_SSL) { - if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) { - /* SSL library may have more bytes ready to read than we ask to read. - * Therefore, read in a loop until we read everything. Without the loop, - * we skip to the next select() cycle which can just timeout. */ - while ((n = mg_ssl_if_read(conn, buf, MG_TCP_RECV_BUFFER_SIZE)) > 0) { - DBG(("%p %d bytes <- %d (SSL)", conn, n, conn->sock)); - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - buf = NULL; - if (conn->flags & MG_F_CLOSE_IMMEDIATELY) break; - /* buf has been freed, we need a new one. */ - buf = (char *) MG_MALLOC(MG_TCP_RECV_BUFFER_SIZE); - if (buf == NULL) break; - } - MG_FREE(buf); - if (n < 0 && n != MG_SSL_WANT_READ) conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } else { - MG_FREE(buf); - mg_ssl_begin(conn); - return; - } - } else -#endif - { - n = (int) MG_RECV_FUNC(conn->sock, buf, - recv_avail_size(conn, MG_TCP_RECV_BUFFER_SIZE), 0); - DBG(("%p %d bytes (PLAIN) <- %d", conn, n, conn->sock)); - if (n > 0) { - mg_if_recv_tcp_cb(conn, buf, n, 1 /* own */); - } else { - MG_FREE(buf); - } - if (n == 0) { - /* Orderly shutdown of the socket, try flushing output. */ - conn->flags |= MG_F_SEND_AND_CLOSE; - } else if (n < 0 && mg_is_error()) { - conn->flags |= MG_F_CLOSE_IMMEDIATELY; - } - } -} - -static int mg_recvfrom(struct mg_connection *nc, union socket_address *sa, - socklen_t *sa_len, char **buf) { - int n; - *buf = (char *) MG_MALLOC(MG_UDP_RECV_BUFFER_SIZE); - if (*buf == NULL) { - DBG(("Out of memory")); - return -ENOMEM; - } - n = recvfrom(nc->sock, *buf, MG_UDP_RECV_BUFFER_SIZE, 0, &sa->sa, sa_len); - if (n <= 0) { - DBG(("%p recvfrom: %s", nc, strerror(mg_get_errno()))); - MG_FREE(*buf); - } - return n; -} - -static void mg_handle_udp_read(struct mg_connection *nc) { - char *buf = NULL; - union socket_address sa; - socklen_t sa_len = sizeof(sa); - int n = mg_recvfrom(nc, &sa, &sa_len, &buf); - DBG(("%p %d bytes from %s:%d", nc, n, inet_ntoa(nc->sa.sin.sin_addr), - ntohs(nc->sa.sin.sin_port))); - mg_if_recv_udp_cb(nc, buf, n, &sa, sa_len); -} - -#if MG_ENABLE_SSL -static void mg_ssl_begin(struct mg_connection *nc) { - int server_side = (nc->listener != NULL); - enum mg_ssl_if_result res = mg_ssl_if_handshake(nc); - DBG(("%p %d res %d", nc, server_side, res)); - - if (res == MG_SSL_OK) { - nc->flags |= MG_F_SSL_HANDSHAKE_DONE; - nc->flags &= ~(MG_F_WANT_READ | MG_F_WANT_WRITE); - - if (server_side) { - union socket_address sa; - socklen_t sa_len = sizeof(sa); - (void) getpeername(nc->sock, &sa.sa, &sa_len); - mg_if_accept_tcp_cb(nc, &sa, sa_len); - } else { - mg_if_connect_cb(nc, 0); - } - } else if (res != MG_SSL_WANT_READ && res != MG_SSL_WANT_WRITE) { - if (!server_side) { - mg_if_connect_cb(nc, res); - } - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - } -} -#endif /* MG_ENABLE_SSL */ - #define _MG_F_FD_CAN_READ 1 #define _MG_F_FD_CAN_WRITE 1 << 1 #define _MG_F_FD_ERROR 1 << 2 @@ -367,11 +216,13 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { int worth_logging = fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE)); if (worth_logging) { - DBG(("%p fd=%d fd_flags=%d nc_flags=%lu rmbl=%d smbl=%d", nc, nc->sock, + DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } + if (!mg_if_poll(nc, now)) return; + if (nc->flags & MG_F_CONNECTING) { if (fd_flags != 0) { int err = 0; @@ -392,15 +243,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { */ err = nc->err; #endif -#if MG_ENABLE_SSL - if ((nc->flags & MG_F_SSL) && err == 0) { - mg_ssl_begin(nc); - } else { - mg_if_connect_cb(nc, err); - } -#else mg_if_connect_cb(nc, err); -#endif } else if (nc->err != 0) { mg_if_connect_cb(nc, nc->err); } @@ -408,7 +251,7 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { if (fd_flags & _MG_F_FD_CAN_READ) { if (nc->flags & MG_F_UDP) { - mg_handle_udp_read(nc); + mg_if_can_recv_cb(nc); } else { if (nc->flags & MG_F_LISTENING) { /* @@ -418,22 +261,16 @@ void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) { */ mg_accept_conn(nc); } else { - mg_handle_tcp_read(nc); + mg_if_can_recv_cb(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); - } - mg_if_poll(nc, (time_t) now); - mg_if_timer(nc, now); - } + if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc); if (worth_logging) { - 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)); + DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock, + nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len)); } } @@ -547,8 +384,7 @@ time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { } #endif - if (!(nc->flags & MG_F_WANT_WRITE) && - nc->recv_mbuf.len < nc->recv_mbuf_limit && + if (nc->recv_mbuf.len < nc->recv_mbuf_limit && (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) { mg_add_to_set(nc->sock, &read_set, &max_fd); } @@ -619,14 +455,6 @@ time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) { 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 (time_t) now; } @@ -735,7 +563,8 @@ void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote, mg_socket_if_connect_udp, \ mg_socket_if_tcp_send, \ mg_socket_if_udp_send, \ - mg_socket_if_recved, \ + mg_socket_if_tcp_recv, \ + mg_socket_if_udp_recv, \ mg_socket_if_create_conn, \ mg_socket_if_destroy_conn, \ mg_socket_if_sock_set, \ diff --git a/src/mg_net_if_socks.c b/src/mg_net_if_socks.c index c5661e97ca..2a42142687 100644 --- a/src/mg_net_if_socks.c +++ b/src/mg_net_if_socks.c @@ -9,18 +9,35 @@ struct socksdata { char *proxy_addr; /* HOST:PORT of the socks5 proxy server */ struct mg_connection *s; /* Respective connection to the server */ struct mg_connection *c; /* Connection to the client */ - struct mbuf tmp; /* Temporary buffer for sent data */ }; static void socks_if_disband(struct socksdata *d) { LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s)); - if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE; - if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE; - d->c = d->s = NULL; + if (d->c) { + d->c->flags |= MG_F_SEND_AND_CLOSE; + d->c->user_data = NULL; + d->c = NULL; + } + if (d->s) { + d->s->flags |= MG_F_SEND_AND_CLOSE; + d->s->user_data = NULL; + d->s = NULL; + } +} + +static void socks_if_relay(struct mg_connection *s) { + struct socksdata *d = (struct socksdata *) s->user_data; + if (d == NULL || d->c == NULL || !(s->flags & MG_SOCKS_CONNECT_DONE) || + d->s == NULL) { + return; + } + if (s->recv_mbuf.len > 0) mg_if_can_recv_cb(d->c); + if (d->c->send_mbuf.len > 0 && s->send_mbuf.len == 0) mg_if_can_send_cb(d->c); } static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { struct socksdata *d = (struct socksdata *) c->user_data; + if (d == NULL) return; if (ev == MG_EV_CONNECT) { int res = *(int *) ev_data; if (res == 0) { @@ -53,6 +70,7 @@ static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4); memcpy(buf + 8, &d->c->sa.sin.sin_port, 2); mg_send(c, buf, sizeof(buf)); + LOG(LL_DEBUG, ("%p Sent connect request", c)); } /* Process connect request */ if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) && @@ -65,17 +83,12 @@ static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) { } mbuf_remove(&c->recv_mbuf, 10); c->flags |= MG_SOCKS_CONNECT_DONE; - /* Connected. Move sent data from client, if any, to server */ - if (d->s && d->c) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_free(&d->tmp); - } - } - /* All flags are set, we're in relay mode */ - if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) { - mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len); - mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len); + LOG(LL_DEBUG, ("%p Connect done %p", c, d->c)); + mg_if_connect_cb(d->c, 0); } + socks_if_relay(c); + } else if (ev == MG_EV_SEND || ev == MG_EV_POLL) { + socks_if_relay(c); } } @@ -85,7 +98,7 @@ static void mg_socks_if_connect_tcp(struct mg_connection *c, d->c = c; d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler); d->s->user_data = d; - LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr)); + LOG(LL_DEBUG, ("%p %s %p %p", c, d->proxy_addr, d, d->s)); (void) sa; } @@ -107,29 +120,44 @@ static int mg_socks_if_listen_udp(struct mg_connection *c, return -1; } -static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, - size_t len) { +static int mg_socks_if_tcp_send(struct mg_connection *c, const void *buf, + size_t len) { + int res; struct socksdata *d = (struct socksdata *) c->iface->data; - LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len)); - if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) { - mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len); - mbuf_append(&d->s->send_mbuf, buf, len); - mbuf_free(&d->tmp); - } else { - mbuf_append(&d->tmp, buf, len); - } + if (d->s == NULL) return -1; + res = (int) mbuf_append(&d->s->send_mbuf, buf, len); + DBG(("%p -> %d -> %p", c, res, d->s)); + return res; } -static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf, - size_t len) { +static int mg_socks_if_udp_send(struct mg_connection *c, const void *buf, + size_t len) { (void) c; (void) buf; (void) len; + return -1; +} + +int mg_socks_if_tcp_recv(struct mg_connection *c, void *buf, size_t len) { + struct socksdata *d = (struct socksdata *) c->iface->data; + if (d->s == NULL) return -1; + if (len > d->s->recv_mbuf.len) len = d->s->recv_mbuf.len; + if (len > 0) { + memcpy(buf, d->s->recv_mbuf.buf, len); + mbuf_remove(&d->s->recv_mbuf, len); + } + DBG(("%p <- %d <- %p", c, (int) len, d->s)); + return len; } -static void mg_socks_if_recved(struct mg_connection *c, size_t len) { +int mg_socks_if_udp_recv(struct mg_connection *c, void *buf, size_t len, + union socket_address *sa, size_t *sa_len) { (void) c; + (void) buf; (void) len; + (void) sa; + (void) sa_len; + return -1; } static int mg_socks_if_create_conn(struct mg_connection *c) { @@ -158,7 +186,6 @@ static void mg_socks_if_free(struct mg_iface *iface) { LOG(LL_DEBUG, ("%p", iface)); if (d != NULL) { socks_if_disband(d); - mbuf_free(&d->tmp); MG_FREE(d->proxy_addr); MG_FREE(d); iface->data = NULL; @@ -189,14 +216,15 @@ static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote, } const struct mg_iface_vtable mg_socks_iface_vtable = { - mg_socks_if_init, mg_socks_if_free, - mg_socks_if_add_conn, mg_socks_if_remove_conn, - mg_socks_if_poll, mg_socks_if_listen_tcp, - mg_socks_if_listen_udp, mg_socks_if_connect_tcp, - mg_socks_if_connect_udp, mg_socks_if_tcp_send, - mg_socks_if_udp_send, mg_socks_if_recved, - mg_socks_if_create_conn, mg_socks_if_destroy_conn, - mg_socks_if_sock_set, mg_socks_if_get_conn_addr, + mg_socks_if_init, mg_socks_if_free, + mg_socks_if_add_conn, mg_socks_if_remove_conn, + mg_socks_if_poll, mg_socks_if_listen_tcp, + mg_socks_if_listen_udp, mg_socks_if_connect_tcp, + mg_socks_if_connect_udp, mg_socks_if_tcp_send, + mg_socks_if_udp_send, mg_socks_if_tcp_recv, + mg_socks_if_udp_recv, mg_socks_if_create_conn, + mg_socks_if_destroy_conn, mg_socks_if_sock_set, + mg_socks_if_get_conn_addr, }; struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) { diff --git a/src/mg_resolv.c b/src/mg_resolv.c index e445ac50e4..b8b73a994a 100644 --- a/src/mg_resolv.c +++ b/src/mg_resolv.c @@ -148,7 +148,6 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, time_t now = (time_t) mg_time(); struct mg_resolve_async_request *req; struct mg_dns_message *msg; - int first = 0; #if !MG_ENABLE_CALLBACK_USERDATA void *user_data = nc->user_data; #endif @@ -162,17 +161,16 @@ static void mg_resolve_async_eh(struct mg_connection *nc, int ev, } switch (ev) { - case MG_EV_CONNECT: - /* don't depend on timer not being at epoch for sending out first req */ - first = 1; - /* fallthrough */ case MG_EV_POLL: if (req->retries > req->max_retries) { req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT; nc->flags |= MG_F_CLOSE_IMMEDIATELY; break; } - if (first || now - req->last_time >= req->timeout) { + if (nc->flags & MG_F_CONNECTING) break; + /* fallthrough */ + case MG_EV_CONNECT: + if (req->retries == 0 || now - req->last_time >= req->timeout) { mg_send_dns_query(nc, req->name, req->query); req->last_time = now; req->retries++; diff --git a/src/mg_ssl_if_mbedtls.c b/src/mg_ssl_if_mbedtls.c index 9bf07fa952..38ba1869ba 100644 --- a/src/mg_ssl_if_mbedtls.c +++ b/src/mg_ssl_if_mbedtls.c @@ -7,9 +7,12 @@ #include #include +#include #include #include +#include #include +#include static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, const char *str) { @@ -19,6 +22,8 @@ static void mg_ssl_mbed_log(void *ctx, int level, const char *file, int line, cs_level = LL_ERROR; break; case 2: + cs_level = LL_INFO; + break; case 3: cs_level = LL_DEBUG; break; @@ -38,12 +43,14 @@ struct mg_ssl_if_ctx { mbedtls_pk_context *key; mbedtls_x509_crt *ca_cert; struct mbuf cipher_suites; + size_t saved_len; }; /* Must be provided by the platform. ctx is struct mg_connection. */ extern int mg_ssl_if_mbed_random(void *ctx, unsigned char *buf, size_t len); void mg_ssl_if_init() { + LOG(LL_INFO, ("%s", MBEDTLS_VERSION_STRING_FULL)); } enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc, @@ -164,40 +171,41 @@ enum mg_ssl_if_result mg_ssl_if_conn_init( return MG_SSL_OK; } -#if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL -int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len); -int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len); -#else -static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) { +static int mg_ssl_if_mbed_send(void *ctx, const unsigned char *buf, + size_t len) { struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1); + int n = nc->iface->vtable->tcp_send(nc, buf, len); + if (n > 0) return n; + if (n == 0) return MBEDTLS_ERR_SSL_WANT_WRITE; + return MBEDTLS_ERR_NET_SEND_FAILED; } -static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) { +static int mg_ssl_if_mbed_recv(void *ctx, unsigned char *buf, size_t len) { struct mg_connection *nc = (struct mg_connection *) ctx; - int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0); - LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n)); - if (n >= 0) return n; - n = mg_get_errno(); - return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1); + int n = nc->iface->vtable->tcp_recv(nc, buf, len); + if (n > 0) return n; + if (n == 0) return MBEDTLS_ERR_SSL_WANT_READ; + return MBEDTLS_ERR_NET_RECV_FAILED; } -#endif static enum mg_ssl_if_result mg_ssl_if_mbed_err(struct mg_connection *nc, int ret) { - if (ret == MBEDTLS_ERR_SSL_WANT_READ) return MG_SSL_WANT_READ; - if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_SSL_WANT_WRITE; - if (ret != - MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { /* CLOSE_NOTIFY = Normal shutdown */ - LOG(LL_ERROR, ("%p SSL error: %d", nc, ret)); + enum mg_ssl_if_result res = MG_SSL_OK; + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + res = MG_SSL_WANT_READ; + } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + res = MG_SSL_WANT_WRITE; + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + LOG(LL_DEBUG, ("%p TLS connection closed by peer", nc)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + res = MG_SSL_OK; + } else { + LOG(LL_ERROR, ("%p mbedTLS error: -0x%04x", nc, -ret)); + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + res = MG_SSL_ERROR; } nc->err = ret; - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - return MG_SSL_ERROR; + return res; } static void mg_ssl_if_mbed_free_certs_and_keys(struct mg_ssl_if_ctx *ctx) { @@ -228,7 +236,8 @@ enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { int err; /* If bio is not yet set, do it now. */ if (ctx->ssl->p_bio == NULL) { - mbedtls_ssl_set_bio(ctx->ssl, nc, ssl_socket_send, ssl_socket_recv, NULL); + mbedtls_ssl_set_bio(ctx->ssl, nc, mg_ssl_if_mbed_send, mg_ssl_if_mbed_recv, + NULL); } err = mbedtls_ssl_handshake(ctx->ssl); if (err != 0) return mg_ssl_if_mbed_err(nc, err); @@ -254,20 +263,35 @@ enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc) { return MG_SSL_OK; } -int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) { +int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t len) { struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size); - DBG(("%p %d -> %d", nc, (int) buf_size, n)); + int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, len); + DBG(("%p %d -> %d", nc, (int) len, n)); if (n < 0) return mg_ssl_if_mbed_err(nc, n); if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY; return n; } -int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) { +int mg_ssl_if_write(struct mg_connection *nc, const void *buf, size_t len) { struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data; - int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len); - DBG(("%p %d -> %d", nc, (int) len, n)); - if (n < 0) return mg_ssl_if_mbed_err(nc, n); + /* Per mbedTLS docs, if write returns WANT_READ or WANT_WRITE, the operation + * should be retried with the same data and length. + * Here we assume that the data being pushed will remain the same but the + * amount may grow between calls so we save the length that was used and + * retry. The assumption being that the data itself won't change and won't + * be removed. */ + size_t l = len; + if (ctx->saved_len > 0 && ctx->saved_len < l) l = ctx->saved_len; + int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) buf, l); + DBG(("%p %d,%d,%d -> %d", nc, (int) len, (int) ctx->saved_len, (int) l, n)); + if (n < 0) { + if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) { + ctx->saved_len = len; + } + return mg_ssl_if_mbed_err(nc, n); + } else if (n > 0) { + ctx->saved_len = 0; + } return n; } diff --git a/test/unit_test.c b/test/unit_test.c index b6f95989a7..446217b1ce 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1145,7 +1145,7 @@ static const char *test_timer(void) { ASSERT((c = mg_connect(&m, "awful.sad:1234", ev_timer_handler)) != NULL); c->user_data = &n; mg_set_timer(c, 1); - mg_mgr_poll(&m, 1); + poll_until(&m, 1, c_int_eq, &n, (void *) 101); ASSERT_EQ(n, 101); mg_mgr_free(&m); @@ -2019,6 +2019,7 @@ static const char *test_http(void) { ASSERT((nc = mg_connect(&mgr, local_addr, cb7)) != NULL); mg_set_protocol_http_websocket(nc); nc->user_data = status; + mbuf_resize(&nc->recv_mbuf, 10000000); /* Wine and GDB set argv0 to full path: strip the dir component */ if ((this_binary = strrchr(g_argv_0, '\\')) != NULL) { @@ -5454,12 +5455,14 @@ static const char *test_socks(void) { this_binary = g_argv_0; } mg_printf(c, "GET /%s HTTP/1.0\n\n", this_binary); + mbuf_resize(&c->recv_mbuf, 10000000); /* Run event loop. Use more cycles to let file download complete. */ - poll_until(&mgr, 5, c_str_ne, status, (void *) ""); - mg_mgr_free(&mgr); + poll_until(&mgr, 10, c_str_ne, status, (void *) ""); ASSERT_STREQ(status, "success"); + mg_mgr_free(&mgr); + return NULL; } #endif