From 99af12a67e663d2f44fe4c34742b09b9bab7d59b Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 2 Oct 2024 23:29:23 +0200 Subject: [PATCH] Upd: mongoose to current master #1344 --- CHANGELOG.md | 1 + dist/mongoose/mongoose.c | 2638 +++++++++++++++++++++++++++++--------- dist/mongoose/mongoose.h | 486 ++++--- 3 files changed, 2399 insertions(+), 726 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4562c30c6..8a375167c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This is a small bug fix release. ### Changelog - Upd: translations +- Upd: Mongoose to current master *** diff --git a/dist/mongoose/mongoose.c b/dist/mongoose/mongoose.c index 8d2a896f8..6099c3a7f 100644 --- a/dist/mongoose/mongoose.c +++ b/dist/mongoose/mongoose.c @@ -2401,7 +2401,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, } static bool isok(uint8_t c) { - return c == '\n' || c == '\r' || c >= ' '; + return c == '\n' || c == '\r' || c == '\t' || c >= ' '; } int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { @@ -2463,9 +2463,11 @@ static bool mg_http_parse_headers(const char *s, const char *end, if (s >= end || clen(s, end) == 0) return false; // Invalid UTF-8 if (*s++ != ':') return false; // Invalid, not followed by : // if (clen(s, end) == 0) return false; // Invalid UTF-8 - while (s < end && s[0] == ' ') s++; // Skip spaces + while (s < end && (s[0] == ' ' || s[0] == '\t')) s++; // Skip spaces if ((s = skiptorn(s, end, &v)) == NULL) return false; - while (v.len > 0 && v.buf[v.len - 1] == ' ') v.len--; // Trim spaces + while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) { + v.len--; // Trim spaces + } // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf)); h[i].name = k, h[i].value = v; // Success. Assign values } @@ -2735,7 +2737,7 @@ static struct mg_str s_known_types[] = { // clang-format on static struct mg_str guess_content_type(struct mg_str path, const char *extra) { - struct mg_str entry, k, v, s = mg_str(extra); + struct mg_str entry, k, v, s = mg_str(extra), asterisk = mg_str_n("*", 1); size_t i = 0; // Shrink path to its extension only @@ -2745,7 +2747,9 @@ static struct mg_str guess_content_type(struct mg_str path, const char *extra) { // Process user-provided mime type overrides, if any while (mg_span(s, &entry, &s, ',')) { - if (mg_span(entry, &k, &v, '=') && mg_strcmp(path, k) == 0) return v; + if (mg_span(entry, &k, &v, '=') && + (mg_strcmp(asterisk, k) == 0 || mg_strcmp(path, k) == 0)) + return v; } // Process built-in mime types @@ -2851,7 +2855,6 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "", range, opts->extra_headers ? opts->extra_headers : ""); if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) { - c->is_draining = 1; c->is_resp = 0; mg_fs_close(fd); } else { @@ -3212,7 +3215,9 @@ static int skip_chunk(const char *buf, int len, int *pl, int *dl) { } static void http_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { + if (ev == MG_EV_READ || ev == MG_EV_CLOSE || + (ev == MG_EV_POLL && c->is_accepted && !c->is_draining && + c->recv.len > 0)) { // see #2796 struct mg_http_message hm; size_t ofs = 0; // Parsing offset while (c->is_resp == 0 && ofs < c->recv.len) { @@ -3220,6 +3225,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { int n = mg_http_parse(buf, c->recv.len - ofs, &hm); struct mg_str *te; // Transfer - encoding header bool is_chunked = false; + size_t old_len = c->recv.len; if (n < 0) { // We don't use mg_error() here, to avoid closing pipelined requests // prematurely, see #2592 @@ -3231,6 +3237,12 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { } if (n == 0) break; // Request is not buffered yet mg_call(c, MG_EV_HTTP_HDRS, &hm); // Got all HTTP headers + if (c->recv.len != old_len) { + // User manipulated received data. Wash our hands + MG_DEBUG(("%lu detaching HTTP handler", c->id)); + c->pfn = NULL; + return; + } if (ev == MG_EV_CLOSE) { // If client did not set Content-Length hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf); @@ -3253,6 +3265,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { // contain a Content-length header. Other requests can also contain a // body, but their content has no defined semantics (RFC 7231) require_content_len = true; + ofs += (size_t) n; // this request has been processed } else if (is_response) { // HTTP spec 7.2 Entity body: All other responses must include a body // or Content-Length header field defined with a value of 0. @@ -3295,6 +3308,13 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { if (c->is_accepted) c->is_resp = 1; // Start generating response mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp + if (c->is_accepted) { + struct mg_str *cc = mg_http_get_header(&hm, "Connection"); + if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) { + c->is_draining = 1; // honor "Connection: close" + break; + } + } } if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data } @@ -4703,7 +4723,7 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { if ((str.buf[i] >= '0' && str.buf[i] <= '9') || (str.buf[i] >= 'a' && str.buf[i] <= 'f') || (str.buf[i] >= 'A' && str.buf[i] <= 'F')) { - unsigned long val; // TODO(): This loops on chars, refactor + unsigned long val = 0; // TODO(): This loops on chars, refactor if (i > j + 3) return false; // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j])); mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val)); @@ -4909,7 +4929,7 @@ void mg_mgr_init(struct mg_mgr *mgr) { #endif #define MIP_TCP_ACK_MS 150 // Timeout for ACKing -#define MIP_TCP_ARP_MS 100 // Timeout for ARP response +#define MIP_ARP_RESP_MS 100 // Timeout for ARP response #define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment #define MIP_TCP_FIN_MS 1000 // Timeout for closing connection #define MIP_TCP_WIN 6000 // TCP window size @@ -5038,6 +5058,10 @@ struct pkt { struct dhcp *dhcp; }; +static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) { + if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data); +} + static void send_syn(struct mg_connection *c); static void mkpay(struct pkt *pkt, void *p) { @@ -5066,7 +5090,7 @@ static void settmout(struct mg_connection *c, uint8_t type) { struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; struct connstate *s = (struct connstate *) (c + 1); unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS - : type == MIP_TTYPE_ARP ? MIP_TCP_ARP_MS + : type == MIP_TTYPE_ARP ? MIP_ARP_RESP_MS : type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS : type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS : MIP_TCP_KEEPALIVE_MS; @@ -5100,13 +5124,14 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - arp_ask(ifp, ifp->gw); + arp_ask(ifp, ifp->gw); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); srand((unsigned int) mg_millis()); } else if (ifp->state == MG_TCPIP_STATE_DOWN) { MG_ERROR(("Link down")); } + mg_tcpip_call(ifp, MG_TCPIP_EV_ST_CHG, &ifp->state); } static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst, @@ -5167,20 +5192,25 @@ static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; -// RFC-2131 #4.3.6, #4.4.1 +// RFC-2131 #4.3.6, #4.4.1; RFC-2132 #9.8 static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req, uint32_t ip_srv) { uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 55, 2, 1, 3, // GW and mask - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 54, 4, 0, 0, 0, 0, // DHCP server ID - 50, 4, 0, 0, 0, 0, // Requested IP - 255 // End of options + 53, 1, 3, // Type: DHCP request + 12, 3, 'm', 'i', 'p', // Host name: "mip" + 54, 4, 0, 0, 0, 0, // DHCP server ID + 50, 4, 0, 0, 0, 0, // Requested IP + 55, 2, 1, 3, 255, 255, // GW, mask [DNS] [SNTP] + 255 // End of options }; - memcpy(opts + 14, &ip_srv, sizeof(ip_srv)); - memcpy(opts + 20, &ip_req, sizeof(ip_req)); - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); + uint8_t addopts = 0; + memcpy(opts + 10, &ip_srv, sizeof(ip_srv)); + memcpy(opts + 16, &ip_req, sizeof(ip_req)); + if (ifp->enable_req_dns) opts[24 + addopts++] = 6; // DNS + if (ifp->enable_req_sntp) opts[24 + addopts++] = 42; // SNTP + opts[21] += addopts; + tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, + sizeof(opts) + addopts - 2, false); MG_DEBUG(("DHCP req sent")); } @@ -5220,6 +5250,8 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, return c; } +static void mac_resolved(struct mg_connection *c); + static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { // ARP request. Make a response, then send @@ -5252,8 +5284,7 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, c->rem.ip, mg_print_mac, s->mac)); c->is_arplooking = 0; - send_syn(c); - settmout(c, MIP_TTYPE_SYN); + mac_resolved(c); } } } @@ -5276,7 +5307,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint32_t ip = 0, gw = 0, mask = 0, lease = 0; + uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0; uint8_t msgtype = 0, state = ifp->state; // perform size check first, then access fields uint8_t *p = pkt->dhcp->options, @@ -5289,6 +5320,12 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW memcpy(&gw, p + 2, sizeof(gw)); ip = pkt->dhcp->yiaddr; + } else if (ifp->enable_req_dns && p[0] == 6 && p[1] == sizeof(dns) && + p + 6 < end) { // DNS + memcpy(&dns, p + 2, sizeof(dns)); + } else if (ifp->enable_req_sntp && p[0] == 42 && p[1] == sizeof(sntp) && + p + 6 < end) { // SNTP + memcpy(&sntp, p + 2, sizeof(sntp)); } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease memcpy(&lease, p + 2, sizeof(lease)); lease = mg_ntohl(lease); @@ -5317,6 +5354,10 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); + if (ifp->enable_req_dns && dns != 0) + mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); + if (ifp->enable_req_sntp && sntp != 0) + mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp); } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew ifp->lease_expire = ifp->now + lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); @@ -5624,6 +5665,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { c->is_connecting = 0; // Client connected settmout(c, MIP_TTYPE_KEEPALIVE); mg_call(c, MG_EV_CONNECT, NULL); // Let user know + if (c->is_tls_hs) mg_tls_handshake(c); } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { // mg_hexdump(pkt->raw.buf, pkt->raw.len); tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); @@ -5826,18 +5868,20 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { // Process timeouts for (c = ifp->mgr->conns; c != NULL; c = c->next) { - if (c->is_udp || c->is_listening || c->is_resolving) continue; + if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) continue; struct connstate *s = (struct connstate *) (c + 1); uint32_t rem_ip; memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); if (now > s->timer) { - if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { + if (s->ttype == MIP_TTYPE_ARP) { + mg_error(c, "ARP timeout"); + } else if (c->is_udp) { + continue; + } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); s->acked = s->ack; - } else if (s->ttype == MIP_TTYPE_ARP) { - mg_error(c, "ARP timeout"); } else if (s->ttype == MIP_TTYPE_SYN) { mg_error(c, "Connection timeout"); } else if (s->ttype == MIP_TTYPE_FIN) { @@ -5918,6 +5962,16 @@ static void send_syn(struct mg_connection *c) { 0); } +static void mac_resolved(struct mg_connection *c) { + if (c->is_udp) { + c->is_connecting = 0; + mg_call(c, MG_EV_CONNECT, NULL); + } else { + send_syn(c); + settmout(c, MIP_TTYPE_SYN); + } +} + void mg_connect_resolved(struct mg_connection *c) { struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; uint32_t rem_ip; @@ -5929,32 +5983,29 @@ void mg_connect_resolved(struct mg_connection *c) { MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, &c->rem)); mg_call(c, MG_EV_RESOLVE, NULL); + c->is_connecting = 1; if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { struct connstate *s = (struct connstate *) (c + 1); memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast - } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { + mac_resolved(c); + } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && + rem_ip != ifp->gw) { // skip if gw (onstatechange -> READY -> ARP) // If we're in the same LAN, fire an ARP lookup. MG_DEBUG(("%lu ARP lookup...", c->id)); arp_ask(ifp, rem_ip); settmout(c, MIP_TTYPE_ARP); c->is_arplooking = 1; - c->is_connecting = 1; } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group memcpy(s->mac, mcastp, 3); memcpy(s->mac + 3, ((uint8_t *) &rem_ip) + 1, 3); // 23 LSb s->mac[3] &= 0x7F; + mac_resolved(c); } else { struct connstate *s = (struct connstate *) (c + 1); memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); - if (c->is_udp) { - mg_call(c, MG_EV_CONNECT, NULL); - } else { - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - c->is_connecting = 1; - } + mac_resolved(c); } } @@ -6030,6 +6081,9 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) { memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { mg_error(c, "net down"); + } else if (c->is_udp && (c->is_arplooking || c->is_resolving)) { + // Fail to send, no target MAC or IP + MG_VERBOSE(("still resolving...")); } else if (c->is_udp) { struct connstate *s = (struct connstate *) (c + 1); len = trim_len(c, len); // Trimming length if necessary @@ -6631,8 +6685,7 @@ void mg_rpc_add(struct mg_rpc **head, struct mg_str method, void (*fn)(struct mg_rpc_req *), void *fn_data) { struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc)); if (rpc != NULL) { - rpc->method.buf = mg_mprintf("%.*s", method.len, method.buf); - rpc->method.len = method.len; + rpc->method = mg_strdup(method); rpc->fn = fn; rpc->fn_data = fn_data; rpc->next = *head, *head = rpc; @@ -7124,6 +7177,12 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, #define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds #define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 +static uint64_t s_boot_timestamp = 0; // Updated by SNTP + +uint64_t mg_now(void) { + return mg_millis() + s_boot_timestamp; +} + static int64_t gettimestamp(const uint32_t *data) { uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); if (sec) sec -= SNTP_TIME_OFFSET; @@ -7131,7 +7190,7 @@ static int64_t gettimestamp(const uint32_t *data) { } int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { - int64_t res = -1; + int64_t epoch_milliseconds = -1; int mode = len > 0 ? buf[0] & 7 : 0; int version = len > 0 ? (buf[0] >> 3) & 7 : 0; if (len < 48) { @@ -7142,31 +7201,36 @@ int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { MG_ERROR(("%s", "server sent a kiss of death")); } else if (version == 4 || version == 3) { // int64_t ref = gettimestamp((uint32_t *) &buf[16]); - int64_t t0 = gettimestamp((uint32_t *) &buf[24]); - int64_t t1 = gettimestamp((uint32_t *) &buf[32]); - int64_t t2 = gettimestamp((uint32_t *) &buf[40]); - int64_t t3 = (int64_t) mg_millis(); - int64_t delta = (t3 - t0) - (t2 - t1); - MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta)); - res = t2 + delta / 2; + int64_t origin_time = gettimestamp((uint32_t *) &buf[24]); + int64_t receive_time = gettimestamp((uint32_t *) &buf[32]); + int64_t transmit_time = gettimestamp((uint32_t *) &buf[40]); + int64_t now = (int64_t) mg_millis(); + int64_t latency = (now - origin_time) - (transmit_time - receive_time); + epoch_milliseconds = transmit_time + latency / 2; + s_boot_timestamp = (uint64_t) (epoch_milliseconds - now); } else { MG_ERROR(("unexpected version: %d", version)); } - return res; + return epoch_milliseconds; } static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { + uint64_t *expiration_time = (uint64_t *) c->data; + if (ev == MG_EV_OPEN) { + *expiration_time = mg_millis() + 3000; // Store expiration time in 3s + } else if (ev == MG_EV_CONNECT) { + mg_sntp_request(c); + } else if (ev == MG_EV_READ) { int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); if (milliseconds > 0) { - MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); + s_boot_timestamp = (uint64_t) milliseconds - mg_millis(); mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); - MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000), - (unsigned) (milliseconds % 1000))); + MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); } - mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer - } else if (ev == MG_EV_CONNECT) { - mg_sntp_request(c); + // mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer + c->is_closing = 1; + } else if (ev == MG_EV_POLL) { + if (mg_millis() > *expiration_time) c->is_closing = 1; } else if (ev == MG_EV_CLOSE) { } (void) ev_data; @@ -7191,7 +7255,10 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fnd) { struct mg_connection *c = NULL; if (url == NULL) url = "udp://time.google.com:123"; - if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) c->pfn = sntp_cb; + if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) { + c->pfn = sntp_cb; + sntp_cb(c, MG_EV_OPEN, (void *) url); + } return c; } @@ -7476,17 +7543,30 @@ static void read_conn(struct mg_connection *c) { size_t len = c->recv.size - c->recv.len; long n = -1; if (c->is_tls) { - if (!ioalloc(c, &c->rtls)) return; - n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], - c->rtls.size - c->rtls.len); - if (n == MG_IO_ERR && c->rtls.len == 0) { - // Close only if we have fully drained both raw (rtls) and TLS buffers - c->is_closing = 1; - } else { + // Do not read to the raw TLS buffer if it already has enough. + // This is to prevent overflowing c->rtls if our reads are slow + long m; + if (c->rtls.len < 16 * 1024 + 40) { // TLS record, header, MAC, padding + if (!ioalloc(c, &c->rtls)) return; + n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], + c->rtls.size - c->rtls.len); if (n > 0) c->rtls.len += (size_t) n; - if (c->is_tls_hs) mg_tls_handshake(c); - n = c->is_tls_hs ? (long) MG_IO_WAIT : mg_tls_recv(c, buf, len); } + // there can still be > 16K from last iteration, always mg_tls_recv() + m = c->is_tls_hs ? (long) MG_IO_WAIT : mg_tls_recv(c, buf, len); + if (n == MG_IO_ERR) { + if (c->rtls.len == 0 || m < 0) { + // Close only when we have fully drained both rtls and TLS buffers + c->is_closing = 1; // or there's nothing we can do about it. + m = -1; + } else { // see #2885 + // TLS buffer is capped to max record size, even though, there can + // be more than one record, give TLS a chance to process them. + } + } else if (c->is_tls_hs) { + mg_tls_handshake(c); + } + n = m; } else { n = recv_raw(c, buf, len); } @@ -7553,8 +7633,9 @@ static void setsockopts(struct mg_connection *c) { void mg_connect_resolved(struct mg_connection *c) { int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; + int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP - c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket + c->fd = S2PTR(socket(af, type, proto)); // Create outbound socket c->is_resolving = 0; // Clear resolving flag if (FD(c) == MG_INVALID_SOCKET) { mg_error(c, "socket(): %d", MG_SOCK_ERR(-1)); @@ -7707,15 +7788,15 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { n = 0; for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { c->is_readable = c->is_writable = 0; + if (c->is_closing) ms = 1; if (skip_iotest(c)) { // Socket not valid, ignore - } else if (c->rtls.len > 0 || mg_tls_pending(c) > 0) { - ms = 1; // Don't wait if TLS is ready } else { + // Don't wait if TLS is ready + if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1; fds[n].fd = FD(c); if (can_read(c)) fds[n].events |= POLLIN; if (can_write(c)) fds[n].events |= POLLOUT; - if (c->is_closing) ms = 1; n++; } } @@ -7731,8 +7812,6 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { if (skip_iotest(c)) { // Socket not valid, ignore - } else if (c->rtls.len > 0 || mg_tls_pending(c) > 0) { - c->is_readable = 1; } else { if (fds[n].revents & POLLERR) { mg_error(c, "socket error"); @@ -7764,7 +7843,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { if (can_write(c)) FD_SET(FD(c), &wset); if (c->rtls.len > 0 || mg_tls_pending(c) > 0) tvp = &tv_zero; if (FD(c) > maxfd) maxfd = FD(c); - if (c->is_closing) ms = 1; + if (c->is_closing) tvp = &tv_zero; } if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { @@ -7800,8 +7879,8 @@ static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) { *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1 usa[1] = usa[0]; - if ((sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && - (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && + if ((sp[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && + (sp[1] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && bind(sp[0], &usa[0].sa, n) == 0 && // bind(sp[1], &usa[1].sa, n) == 0 && // getsockname(sp[0], &usa[0].sa, &n) == 0 && // @@ -7944,7 +8023,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) { if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { buf[len++] = (char) (ch & 0xff); buf[len] = '\0'; - if (sscanf(buf, "