diff --git a/mongoose.c b/mongoose.c index de6bf32b..8326cdce 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1793,14 +1793,14 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, va_list *ap) { size_t len = c->send.len; - mg_send(c, " \r\n", 10); + if (!mg_send(c, " \r\n", 10)) mg_error(c, "OOM"); mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); if (c->send.len >= len + 10) { mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10); c->send.buf[len + 8] = '\r'; if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker } - mg_send(c, "\r\n", 2); + if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM"); } void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { @@ -1812,8 +1812,7 @@ void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { mg_printf(c, "%lx\r\n", (unsigned long) len); - mg_send(c, buf, len); - mg_send(c, "\r\n", 2); + if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM"); if (len == 0) c->is_resp = 0; } @@ -2590,8 +2589,8 @@ static size_t roundup(size_t size, size_t align) { return align == 0 ? size : (size + align - 1) / align * align; } -int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { - int ok = 1; +bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { + bool ok = true; new_size = roundup(new_size, io->align); if (new_size == 0) { mg_bzero(io->buf, io->size); @@ -2610,14 +2609,14 @@ int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { io->size = new_size; io->len = len; } else { - ok = 0; + ok = false; MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size)); } } return ok; } -int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) { +bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) { io->buf = NULL; io->align = align; io->size = io->len = 0; @@ -3353,8 +3352,8 @@ static const struct mg_mqtt_pmap s_prop_map[] = { {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE}, {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}}; -void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, - uint32_t len) { +static bool mqtt_send_header(struct mg_connection *c, uint8_t cmd, + uint8_t flags, uint32_t len) { uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; buf[0] = (uint8_t) ((cmd << 4) | flags); do { @@ -3363,15 +3362,20 @@ void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, if (len > 0) *vlen |= 0x80; vlen++; } while (len > 0 && vlen < &buf[sizeof(buf)]); - mg_send(c, buf, (size_t) (vlen - buf)); + return mg_send(c, buf, (size_t) (vlen - buf)); } -static void mg_send_u16(struct mg_connection *c, uint16_t value) { - mg_send(c, &value, sizeof(value)); +void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, + uint32_t len) { + if (!mqtt_send_header(c, cmd, flags, len)) mg_error(c, "OOM"); } -static void mg_send_u32(struct mg_connection *c, uint32_t value) { - mg_send(c, &value, sizeof(value)); +static bool mg_send_u16(struct mg_connection *c, uint16_t value) { + return mg_send(c, &value, sizeof(value)); +} + +static bool mg_send_u32(struct mg_connection *c, uint32_t value) { + return mg_send(c, &value, sizeof(value)); } static uint8_t varint_size(size_t length) { @@ -3464,46 +3468,50 @@ static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) { return size; } -static void mg_send_mqtt_properties(struct mg_connection *c, +static bool mg_send_mqtt_properties(struct mg_connection *c, struct mg_mqtt_prop *props, size_t nprops) { size_t total_size = get_properties_length(props, nprops); uint8_t buf_v[4] = {0, 0, 0, 0}; uint8_t buf[4] = {0, 0, 0, 0}; size_t i, len = encode_varint(buf, total_size); - mg_send(c, buf, (size_t) len); + if (!mg_send(c, buf, (size_t) len)) return false; for (i = 0; i < nprops; i++) { - mg_send(c, &props[i].id, sizeof(props[i].id)); + if (!mg_send(c, &props[i].id, sizeof(props[i].id))) return false; switch (mqtt_prop_type_by_id(props[i].id)) { case MQTT_PROP_TYPE_STRING_PAIR: - mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)); - mg_send(c, props[i].key.buf, props[i].key.len); - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); + if (!mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)) || + !mg_send(c, props[i].key.buf, props[i].key.len) || + !mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) || + !mg_send(c, props[i].val.buf, props[i].val.len)) + return false; break; case MQTT_PROP_TYPE_BYTE: - mg_send(c, &props[i].iv, sizeof(uint8_t)); + if (!mg_send(c, &props[i].iv, sizeof(uint8_t))) return false; break; case MQTT_PROP_TYPE_SHORT: - mg_send_u16(c, mg_htons((uint16_t) props[i].iv)); + if (!mg_send_u16(c, mg_htons((uint16_t) props[i].iv))) return false; break; case MQTT_PROP_TYPE_INT: - mg_send_u32(c, mg_htonl((uint32_t) props[i].iv)); + if (!mg_send_u32(c, mg_htonl((uint32_t) props[i].iv))) return false; break; case MQTT_PROP_TYPE_STRING: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); + if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) || + !mg_send(c, props[i].val.buf, props[i].val.len)) + return false; break; case MQTT_PROP_TYPE_BINARY_DATA: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); + if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) || + !mg_send(c, props[i].val.buf, props[i].val.len)) + return false; break; case MQTT_PROP_TYPE_VARIABLE_INT: len = encode_varint(buf_v, props[i].iv); - mg_send(c, buf_v, (size_t) len); + if (!mg_send(c, buf_v, (size_t) len)) return false; break; } } + return true; } size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop, @@ -3596,33 +3604,41 @@ void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) { total_len += get_props_size(opts->will_props, opts->num_will_props); } - mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len); - mg_send(c, hdr, sizeof(hdr)); // keepalive == 0 means "do not disconnect us!" - mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)); + if (!mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len) || + !mg_send(c, hdr, sizeof(hdr)) || + !mg_send_u16(c, mg_htons((uint16_t) opts->keepalive))) + goto fail; - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); + if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props)) + goto fail; - mg_send_u16(c, mg_htons((uint16_t) cid.len)); - mg_send(c, cid.buf, cid.len); + if (!mg_send_u16(c, mg_htons((uint16_t) cid.len)) || + !mg_send(c, cid.buf, cid.len)) + goto fail; if (hdr[7] & MQTT_HAS_WILL) { - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props); + if (c->is_mqtt5 && + !mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props)) + goto fail; - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send_u16(c, mg_htons((uint16_t) opts->message.len)); - mg_send(c, opts->message.buf, opts->message.len); - } - if (opts->user.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->user.len)); - mg_send(c, opts->user.buf, opts->user.len); - } - if (opts->pass.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)); - mg_send(c, opts->pass.buf, opts->pass.len); + if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) || + !mg_send(c, opts->topic.buf, opts->topic.len) || + !mg_send_u16(c, mg_htons((uint16_t) opts->message.len)) || + !mg_send(c, opts->message.buf, opts->message.len)) + goto fail; } + if (opts->user.len > 0 && + (!mg_send_u16(c, mg_htons((uint16_t) opts->user.len)) || + !mg_send(c, opts->user.buf, opts->user.len))) + goto fail; + if (opts->pass.len > 0 && + (!mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)) || + !mg_send(c, opts->pass.buf, opts->pass.len))) + goto fail; + return; +fail: + mg_error(c, "OOM"); } uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { @@ -3637,20 +3653,28 @@ uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props); if (opts->qos > 0 && id != 0) flags |= 1 << 3; - mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len); - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); + if (!mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len) || + !mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) || + !mg_send(c, opts->topic.buf, opts->topic.len)) + goto fail; if (opts->qos > 0) { // need to send 'id' field if (id == 0) { // generate new one if not resending if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; id = c->mgr->mqtt_id; } - mg_send_u16(c, mg_htons(id)); + if (!mg_send_u16(c, mg_htons(id))) goto fail; } - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); + if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props)) + goto fail; - if (opts->message.len > 0) mg_send(c, opts->message.buf, opts->message.len); + if (opts->message.len > 0 && + !mg_send(c, opts->message.buf, opts->message.len)) + goto fail; + return id; + +fail: + mg_error(c, "OOM"); return id; } @@ -3659,14 +3683,20 @@ void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; size_t len = 2 + opts->topic.len + 2 + 1 + plen; - mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len); + if (!mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len)) goto fail; if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; - mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); + if (!mg_send_u16(c, mg_htons(c->mgr->mqtt_id))) goto fail; - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send(c, &qos_, sizeof(qos_)); + if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props)) + goto fail; + + if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) || + !mg_send(c, opts->topic.buf, opts->topic.len) || + !mg_send(c, &qos_, sizeof(qos_))) + goto fail; + return; +fail: + mg_error(c, "OOM"); } int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version, @@ -3773,15 +3803,16 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { uint32_t remaining_len = sizeof(id); if (c->is_mqtt5) remaining_len += 2; // 3.4.2 - mg_mqtt_send_header( - c, - (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK), - 0, remaining_len); - mg_send(c, &id, sizeof(id)); + if (!mqtt_send_header(c, + (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC + : MQTT_CMD_PUBACK), + 0, remaining_len) || + !mg_send(c, &id, sizeof(id))) + goto fail; if (c->is_mqtt5) { uint16_t zero = 0; - mg_send(c, &zero, sizeof(zero)); + if (!mg_send(c, &zero, sizeof(zero))) goto fail; } } mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff @@ -3790,15 +3821,18 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc uint16_t id = mg_ntohs(mm.id); uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len); - mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2 + if (!mqtt_send_header(c, MQTT_CMD_PUBREL, 2, + remaining_len) // MQTT5 3.6.1-1, flags = 2 + || !mg_send(c, &id, sizeof(id))) + goto fail; break; } case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc uint16_t id = mg_ntohs(mm.id); uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len); - mg_send(c, &id, sizeof(id)); + if (!mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len) || + !mg_send(c, &id, sizeof(id))) + goto fail; break; } } @@ -3810,6 +3844,9 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { } } (void) ev_data; + return; +fail: + mg_error(c, "OOM"); } void mg_mqtt_ping(struct mg_connection *nc) { @@ -3824,19 +3861,24 @@ void mg_mqtt_disconnect(struct mg_connection *c, const struct mg_mqtt_opts *opts) { size_t len = 0; if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props); - mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len); + if (!mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len)) goto fail; if (c->is_mqtt5) { uint8_t zero = 0; - mg_send(c, &zero, sizeof(zero)); // reason code - mg_send_mqtt_properties(c, opts->props, opts->num_props); + if (!mg_send(c, &zero, sizeof(zero)) // reason code + || !mg_send_mqtt_properties(c, opts->props, opts->num_props)) + goto fail; } + return; +fail: + mg_error(c, "OOM"); } struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url, const struct mg_mqtt_opts *opts, mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_connect_svc(mgr, url, fn, fn_data, mqtt_cb, NULL); + struct mg_connection *c = + mg_connect_svc(mgr, url, fn, fn_data, mqtt_cb, NULL); if (c != NULL) { struct mg_mqtt_opts empty; memset(&empty, 0, sizeof(empty)); @@ -6088,6 +6130,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) { static void write_conn(struct mg_connection *c) { long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) : mg_io_send(c, c->send.buf, c->send.len); + // TODO(): mg_tls_send() may return 0 forever on steady OOM if (len == MG_IO_ERR) { mg_error(c, "tx err"); } else if (len > 0) { @@ -6167,7 +6210,9 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) { len = trim_len(c, len); // Trimming length if necessary res = tx_udp(ifp, s->mac, &c->loc, &c->rem, buf, len); } else { - res = mg_iobuf_add(&c->send, c->send.len, buf, len); + res = (bool) mg_iobuf_add(&c->send, c->send.len, buf, len); + // res == 0 means an OOM condition (iobuf couldn't resize), yet this is so + // far recoverable, let the caller decide } return res; } @@ -9522,7 +9567,9 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) { iolog(c, (char *) buf, n, false); return n > 0; } else { - return mg_iobuf_add(&c->send, c->send.len, buf, len); + return (bool) mg_iobuf_add(&c->send, c->send.len, buf, len); + // returning 0 means an OOM condition (iobuf couldn't resize), yet this is + // so far recoverable, let the caller decide } } @@ -9544,7 +9591,7 @@ static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) { #elif MG_ARCH == MG_ARCH_THREADX // NetxDuo fails to send large blocks of data to the non-blocking sockets (void) fd; - //fcntl(fd, F_SETFL, O_NONBLOCK); + // fcntl(fd, F_SETFL, O_NONBLOCK); #elif MG_ARCH == MG_ARCH_TIRTOS int val = 0; setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val)); @@ -9576,9 +9623,10 @@ void mg_multicast_add(struct mg_connection *c, char *ip) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(ip); mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY); - setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)); -#endif // !Zephyr -#endif // !lwIP + setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, + sizeof(mreq)); +#endif // !Zephyr +#endif // !lwIP #endif } @@ -9696,9 +9744,9 @@ static void read_conn(struct mg_connection *c) { if (n == MG_IO_ERR || n == MG_IO_RESET) { // Windows, see #3031 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. - if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104 - } else { // see #2885 + c->is_closing = 1; // or there's nothing we can do about it. + if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104 + } 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. } @@ -9719,6 +9767,7 @@ static void write_conn(struct mg_connection *c) { char *buf = (char *) c->send.buf; size_t len = c->send.len; long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len); + // TODO(): mg_tls_send() may return 0 forever on steady OOM MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, (long) c->send.len, (long) c->send.size, (long) c->recv.len, (long) c->recv.size, n, MG_SOCK_ERR(n))); @@ -9748,7 +9797,7 @@ static void connect_conn(struct mg_connection *c) { mg_call(c, MG_EV_CONNECT, NULL); MG_EPOLL_MOD(c, 0); if (c->is_tls_hs) mg_tls_handshake(c); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() + if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() } else { mg_error(c, "socket error"); } @@ -9800,9 +9849,9 @@ void mg_connect_resolved(struct mg_connection *c) { rc = connect(FD(c), &usa.sa, slen); // Attempt to connect if (rc == 0) { // Success setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() - } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake + mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user + if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() + } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem)); c->is_connecting = 1; } else { @@ -9862,7 +9911,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) { &c->rem, mg_print_ip_port, &c->loc)); mg_call(c, MG_EV_OPEN, NULL); mg_call(c, MG_EV_ACCEPT, NULL); - if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() + if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init() } } @@ -10178,31 +10227,35 @@ static char *mg_ssi(const char *path, const char *root, int depth) { mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg); if (depth < MG_MAX_SSI_DEPTH && (data = mg_ssi(tmp, root, depth + 1)) != NULL) { - mg_iobuf_add(&b, b.len, data, strlen(data)); + size_t datalen = strlen(data); + size_t ret = mg_iobuf_add(&b, b.len, data, datalen); mg_free(data); + if (datalen > 0 && ret == 0) goto fail; } else { MG_ERROR(("%s: file=%s error or too deep", path, arg)); - } + } // TODO(): or OOM at recursive call } else if (sscanf(buf, "