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); int ret = server_side ? SSL_accept(nc->ssl) : SSL_connect(nc->ssl); int err = SSL_get_error(nc->ssl, ret); DBG(("%s %d %d", (server_side ? "SSL_accept" : "SSL_connect"), ret, err)); if (ret <= 0) { if (err == SSL_ERROR_WANT_WRITE) { nc->flags |= MG_F_WANT_WRITE; cs->err = 0; } else if (err == SSL_ERROR_WANT_READ) { /* Nothing, we are callback-driven. */ cs->err = 0; } else { cs->err = err; LOG(LL_ERROR, ("SSL handshake error: %d", cs->err)); 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; nc->flags |= MG_F_SSL_HANDSHAKE_DONE; if (server_side) { mg_lwip_accept_conn(nc, cs->pcb.tcp); } else { mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); } } }
void mg_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; if (nc->sock == INVALID_SOCKET || 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; } struct udp_pcb *upcb = cs->pcb.udp; struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); ip_addr_t *ip = (ip_addr_t *) &nc->sa.sin.sin_addr.s_addr; u16_t port = ntohs(nc->sa.sin.sin_port); memcpy(p->payload, buf, len); cs->err = udp_sendto(upcb, p, (ip_addr_t *) ip, port); DBG(("%p udp_sendto = %d", nc, cs->err)); pbuf_free(p); if (cs->err != ERR_OK) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } else { cs->num_sent += len; mg_lwip_post_signal(MG_SIG_SENT_CB, nc); } }
static void mg_lwip_tcp_error_cb(void *arg, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; DBG(("%p conn error %d", nc, err)); if (nc == NULL) return; struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; cs->pcb.tcp = NULL; /* Has already been deallocated */ if (nc->flags & MG_F_CONNECTING) { cs->err = err; mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); } else { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, 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 < MG_LWIP_SSL_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 */); } } }
static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t num_sent) { struct mg_connection *nc = (struct mg_connection *) arg; DBG(("%p %p %u", nc, tpcb, num_sent)); if (nc == NULL) { tcp_abort(tpcb); return ERR_ABRT; } struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; cs->num_sent += num_sent; cs->sent_up_to += num_sent; if (cs->send_started_bytes > 0 && cs->sent_up_to >= cs->send_started_bytes) { uint32_t now = system_get_time(); uint32_t send_time_micros = time_left_micros(cs->send_started_micros, now); cs->rtt_samples_micros[cs->rtt_sample_index] = send_time_micros; cs->rtt_sample_index = (cs->rtt_sample_index + 1) % MG_TCP_RTT_NUM_SAMPLES; cs->send_started_bytes = cs->send_started_micros = 0; } if (tpcb->unacked == NULL) { cs->next_rexmit_ts_micros = cs->rexmit_timeout_micros = 0; } mg_lwip_post_signal(MG_SIG_SENT_CB, nc); return ERR_OK; }
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 < MG_LWIP_SSL_RECV_MBUF_LIMIT) { char *buf = (char *) malloc(MG_LWIP_SSL_IO_SIZE); if (buf == NULL) return; int ret = SSL_read(nc->ssl, buf, MG_LWIP_SSL_IO_SIZE); int err = SSL_get_error(nc->ssl, ret); DBG(("%p SSL_read %u = %d, %d", nc, MG_LWIP_SSL_IO_SIZE, ret, err)); if (ret <= 0) { free(buf); if (err == SSL_ERROR_WANT_WRITE) { nc->flags |= MG_F_WANT_WRITE; return; } else if (err == SSL_ERROR_WANT_READ) { /* Nothing, we are callback-driven. */ cs->err = 0; return; } else { LOG(LL_ERROR, ("SSL read error: %d", err)); mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } } else { mg_if_recv_tcp_cb(nc, buf, ret); /* callee takes over data */ } } if (nc->recv_mbuf.len >= MG_LWIP_SSL_RECV_MBUF_LIMIT) { nc->flags |= MG_F_WANT_READ; } else { nc->flags &= ~MG_F_WANT_READ; } }
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 = SSL_write(nc->ssl, nc->send_mbuf.buf, len); int err = SSL_get_error(nc->ssl, ret); DBG(("%p SSL_write %u = %d, %d", nc, len, ret, err)); if (ret > 0) { mbuf_remove(&nc->send_mbuf, ret); mbuf_trim(&nc->send_mbuf); 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 (err == SSL_ERROR_NONE) { nc->flags &= ~MG_F_WANT_WRITE; } else if (err == SSL_ERROR_WANT_WRITE) { nc->flags |= MG_F_WANT_WRITE; } else { LOG(LL_ERROR, ("SSL write error: %d", err)); mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } }
void mg_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; struct udp_pcb *upcb = cs->pcb.udp; struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); ip_addr_t *ip = (ip_addr_t *) &nc->sa.sin.sin_addr.s_addr; u16_t port = ntohs(nc->sa.sin.sin_port); memcpy(p->payload, buf, len); cs->err = udp_sendto(upcb, p, (ip_addr_t *) ip, port); DBG(("%p udp_sendto = %d", nc, cs->err)); pbuf_free(p); if (cs->err != ERR_OK) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } else { cs->num_sent += len; mg_lwip_post_signal(MG_SIG_SENT_CB, nc); } }
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_if_connect_udp(struct mg_connection *nc) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; struct udp_pcb *upcb = udp_new(); cs->err = udp_bind(upcb, IP_ADDR_ANY, 0 /* any port */); DBG(("%p udp_bind %p = %d", nc, upcb, cs->err)); if (cs->err == ERR_OK) { udp_recv(upcb, mg_lwip_udp_recv_cb, nc); cs->pcb.udp = upcb; } else { udp_remove(upcb); } mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); }
static void mg_lwip_send_more(struct mg_connection *nc) { 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; } int num_written = 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_written)); if (num_written == 0) return; if (num_written < 0) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } mbuf_remove(&nc->send_mbuf, num_written); mbuf_trim(&nc->send_mbuf); }
void mg_if_connect_tcp(struct mg_connection *nc, const union socket_address *sa) { struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; struct tcp_pcb *tpcb = tcp_new(); cs->pcb.tcp = tpcb; ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr; u16_t port = ntohs(sa->sin.sin_port); tcp_arg(tpcb, nc); tcp_err(tpcb, mg_lwip_tcp_error_cb); tcp_sent(tpcb, mg_lwip_tcp_sent_cb); tcp_recv(tpcb, mg_lwip_tcp_recv_cb); cs->err = tcp_bind(tpcb, IP_ADDR_ANY, 0 /* any port */); DBG(("%p tcp_bind = %d", nc, cs->err)); if (cs->err != ERR_OK) { mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); return; } cs->err = tcp_connect(tpcb, ip, port, mg_lwip_tcp_conn_cb); DBG(("%p tcp_connect %p = %d", nc, tpcb, cs->err)); if (cs->err != ERR_OK) { mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); return; } }
static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) { struct mg_connection *nc = (struct mg_connection *) arg; DBG(("%p connect to %s:%u = %d", nc, ipaddr_ntoa(&tpcb->remote_ip), tpcb->remote_port, err)); if (nc == NULL) { tcp_abort(tpcb); return ERR_ARG; } struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock; cs->err = err; #ifdef SSL_KRYPTON if (err == 0 && nc->ssl != NULL) { SSL_set_fd(nc->ssl, (intptr_t) nc); mg_lwip_ssl_do_hs(nc); } else #endif { mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc); } return ERR_OK; }
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)); if (p == NULL) { if (nc != NULL) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } else { /* Tombstoned connection, do nothing. */ } return ERR_OK; } else if (nc == NULL) { 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. */ if (p->next != NULL) { struct pbuf *q = p->next; for (; q != NULL; q = q->next) pbuf_ref(q); } if (cs->rx_chain == NULL) { cs->rx_chain = p; cs->rx_offset = 0; } 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. */ struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (np != NULL) { pbuf_copy(np, p); pbuf_free(p); p = np; } } pbuf_chain(cs->rx_chain, p); } #ifdef SSL_KRYPTON if (nc->ssl != NULL) { if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) { mg_lwip_ssl_recv(nc); } else { mg_lwip_ssl_do_hs(nc); } return ERR_OK; } #endif while (cs->rx_chain != NULL) { struct pbuf *seg = cs->rx_chain; size_t len = (seg->len - cs->rx_offset); char *data = (char *) malloc(len); if (data == NULL) { DBG(("OOM")); return ERR_MEM; } pbuf_copy_partial(seg, data, len, cs->rx_offset); mg_if_recv_tcp_cb(nc, data, len); /* callee takes over data */ 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; } } if (nc->send_mbuf.len > 0) { mg_lwip_mgr_schedule_poll(nc->mgr); } return ERR_OK; }
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) { struct mg_mgr *mgr = iface->mgr; int n = 0; double now = mg_time(); struct mg_connection *nc, *tmp; double min_timer = 0; int num_timers = 0; #if 0 DBG(("begin poll @%u", (unsigned int) (now * 1000))); #endif mg_ev_mgr_lwip_process_signals(mgr); for (nc = mgr->active_connections; nc != NULL; nc = tmp) { 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 (nc->sock != INVALID_SOCKET && !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL && cs->pcb.tcp->unsent != NULL) { tcpip_callback(tcp_output_tcpip, cs->pcb.tcp); } if (nc->ev_timer_time > 0) { if (num_timers == 0 || nc->ev_timer_time < min_timer) { min_timer = nc->ev_timer_time; } num_timers++; } if (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) { mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc); } } } #if 0 DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms", (unsigned int) (now * 1000), n, num_timers, (unsigned int) (min_timer * 1000), timeout_ms)); #endif (void) timeout_ms; return now; }