Add mbedtls support to MIP

This commit is contained in:
cpq 2022-09-09 11:11:53 +01:00
parent f76a7e36e8
commit 124ac12150
16 changed files with 446 additions and 303 deletions

View File

@ -1,5 +1,5 @@
name: build name: build
on: [push, pull_request] on: [push, pull_request_target]
jobs: jobs:
linux: linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -7,7 +7,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
cc: [gcc, clang] cc: [gcc, clang]
target: [test, test++, valgrind] target: [test, mip_test, test++, valgrind]
ssl: [MBEDTLS, OPENSSL] ssl: [MBEDTLS, OPENSSL]
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }} name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}
env: env:
@ -36,6 +36,7 @@ jobs:
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl
- run: make test++ upload-coverage IPV6=0 ASAN_OPTIONS= - run: make test++ upload-coverage IPV6=0 ASAN_OPTIONS=
- run: make test SSL=OPENSSL IPV6=0 ASAN_OPTIONS= OPENSSL=`echo /usr/local/Cellar/openssl*/*` - run: make test SSL=OPENSSL IPV6=0 ASAN_OPTIONS= OPENSSL=`echo /usr/local/Cellar/openssl*/*`
- run: make mip_test ASAN_OPTIONS=
- run: make mg_prefix - run: make mg_prefix
windows: windows:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -3,7 +3,9 @@
#include "mongoose.h" #include "mongoose.h"
#if !defined(MQTT_SERVER)
#define MQTT_SERVER "mqtt://broker.hivemq.com:1883" #define MQTT_SERVER "mqtt://broker.hivemq.com:1883"
#endif
#define MQTT_PUBLISH_TOPIC "mg/my_device" #define MQTT_PUBLISH_TOPIC "mg/my_device"
#define MQTT_SUBSCRIBE_TOPIC "mg/#" #define MQTT_SUBSCRIBE_TOPIC "mg/#"
@ -111,7 +113,7 @@ static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data, void *fnd) {
"qos", (int) mm->qos); "qos", (int) mm->qos);
} else if (ev == MG_EV_MQTT_CMD) { } else if (ev == MG_EV_MQTT_CMD) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_DEBUG(("cmd %d qos %d", mm->cmd, mm->qos)); MG_DEBUG(("%lu cmd %d qos %d", c->id, mm->cmd, mm->qos));
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
s_mqtt = NULL; s_mqtt = NULL;
if (s_connected) { if (s_connected) {
@ -136,7 +138,6 @@ static void timer_mqtt_fn(void *param) {
static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_SNTP_TIME) { if (ev == MG_EV_SNTP_TIME) {
uint64_t t = *(uint64_t *) ev_data; uint64_t t = *(uint64_t *) ev_data;
MG_INFO(("%lu SNTP: %lld ms from epoch", c->id, t));
s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000); s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000);
c->is_closing = 1; c->is_closing = 1;
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
@ -211,7 +212,8 @@ void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
#endif #endif
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
MG_DEBUG(("%.*s %.*s -> %.*s", (int) hm->method.len, hm->method.ptr, MG_DEBUG(("%lu %.*s %.*s -> %.*s", c->id, (int) hm->method.len,
(int) hm->uri.len, hm->uri.ptr, (int) 3, &c->send.buf[9])); hm->method.ptr, (int) hm->uri.len, hm->uri.ptr, (int) 3,
&c->send.buf[9]));
} }
} }

View File

@ -1,9 +1,17 @@
PROG ?= example PROG ?= example
DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_MIP=1 -DMG_ENABLE_SOCKET=0 -DMG_ENABLE_WINSOCK=0 -DMG_ENABLE_PACKED_FS=1 -I../.. DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_MIP=1 -DMG_ENABLE_SOCKET=0 -DMG_ENABLE_PACKED_FS=1 -I../..
CFLAGS ?= -W -Wall CFLAGS ?= -W -Wall $(EXTRA_CFLAGS)
LIBS ?= -lpcap LIBS ?= -lpcap
SOURCES = main.c ../../mongoose.c ../device-dashboard/net.c ../device-dashboard/packed_fs.c SOURCES = main.c ../../mongoose.c ../device-dashboard/net.c ../device-dashboard/packed_fs.c
ifeq "$(SSL)" "MBEDTLS"
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509 -I$(MBEDTLS)/include -L$(MBEDTLS)/lib
endif
ifeq "$(SSL)" "OPENSSL"
CFLAGS += -DMG_ENABLE_OPENSSL=1 -lssl -lcrypto -I$(OPENSSL)/include -L$(OPENSSL)/lib
endif
all: $(PROG) all: $(PROG)
$(RUN) ./$(PROG) $(ARGS) $(RUN) ./$(PROG) $(ARGS)

1
examples/mip-pcap/ca.pem Symbolic link
View File

@ -0,0 +1 @@
../../test/data/ca.pem

View File

@ -30,19 +30,11 @@ static size_t pcap_rx(void *buf, size_t len, void *userdata) {
size_t received = 0; size_t received = 0;
struct pcap_pkthdr *hdr = NULL; struct pcap_pkthdr *hdr = NULL;
const unsigned char *pkt = NULL; const unsigned char *pkt = NULL;
usleep(1); // This is to avoid 100% CPU
// To avoid busy-loop and 100% CPU time, wait on pcap for some time if (pcap_next_ex((pcap_t *) userdata, &hdr, &pkt) == 1) { // Yes, read
int fd = pcap_get_selectable_fd((pcap_t *) userdata); // Pcap file descriptor
struct timeval tv = {0, 50000}; // 50 ms
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
if (select(fd + 1, &rset, 0, 0, &tv) == 1 && // Have data ?
pcap_next_ex((pcap_t *) userdata, &hdr, &pkt) == 1) { // Yes, read
received = hdr->len < len ? hdr->len : len; received = hdr->len < len ? hdr->len : len;
memcpy(buf, pkt, received); memcpy(buf, pkt, received);
} }
return received; return received;
} }
@ -60,6 +52,8 @@ int main(int argc, char *argv[]) {
mac = argv[++i]; mac = argv[++i];
} else if (strcmp(argv[i], "-bpf") == 0 && i + 1 < argc) { } else if (strcmp(argv[i], "-bpf") == 0 && i + 1 < argc) {
bpf = argv[++i]; bpf = argv[++i];
} else if (strcmp(argv[i], "-v") == 0 && i + 1 < argc) {
mg_log_set(atoi(argv[++i]));
} else { } else {
MG_ERROR(("unknown option %s", argv[i])); MG_ERROR(("unknown option %s", argv[i]));
return EXIT_FAILURE; return EXIT_FAILURE;
@ -96,7 +90,6 @@ int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
mg_log_set(MG_LL_DEBUG); // Set debug log level
struct mip_cfg c = {.ip = 0, .mask = 0, .gw = 0}; struct mip_cfg c = {.ip = 0, .mask = 0, .gw = 0};
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &c.mac[0], &c.mac[1], &c.mac[2], sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &c.mac[0], &c.mac[1], &c.mac[2],

View File

@ -34,9 +34,12 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
if (n > 0) { if (n > 0) {
uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
n = w5500_r2(s, W5500_RX0, ptr); // Read frame length n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
if (n <= len + 2) r = n - 2, w5500_rn(s, W5500_RX0, ptr + 2, buf, r); if (n <= len + 2 && n > 1) {
w5500_w2(s, W5500_S0, 0x28, ptr + n); // Advance read pointer r = (uint16_t) (n - 2);
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
}
w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
// printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
} }
return r; return r;
@ -45,11 +48,11 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
static size_t w5500_tx(const void *buf, size_t buflen, void *data) { static size_t w5500_tx(const void *buf, size_t buflen, void *data) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) data;
uint16_t n = 0, len = (uint16_t) buflen; uint16_t n = 0, len = (uint16_t) buflen;
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
w5500_w2(s, W5500_S0, 0x24, ptr + len); // Advance write pointer w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
for (int i = 0; i < 40; i++) { for (int i = 0; i < 40; i++) {
uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
if (ir == 0) continue; if (ir == 0) continue;

220
mip/mip.c
View File

@ -15,15 +15,23 @@
#ifndef MIP_ARP_ENTRIES #ifndef MIP_ARP_ENTRIES
#define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 #define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21
#endif #endif
#ifndef MIP_QSIZE
#define MIP_QSIZE (16 * 1024) // Queue size
#endif
#define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size #define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size
#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms #define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
#define MIP_TCP_ACK_MS 150 // Timeout for ACKing #define MIP_TCP_ACK_MS 150 // Timeout for ACKing
struct connstate { struct connstate {
uint32_t seq, ack; // TCP seq/ack counters uint32_t seq, ack; // TCP seq/ack counters
uint64_t timer; // TCP keep-alive / ACK timer uint64_t timer; // TCP keep-alive / ACK timer
uint8_t mac[6]; // Peer MAC address uint8_t mac[6]; // Peer MAC address
uint8_t ttype; // Timer type. 0: ack, 1: keep-alive uint8_t ttype; // Timer type. 0: ack, 1: keep-alive
#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
struct mg_iobuf raw; // For TLS only. Incoming raw data
}; };
struct str { struct str {
@ -184,7 +192,7 @@ static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) {
static bool q_write(struct queue *q, const void *buf, size_t len) { static bool q_write(struct queue *q, const void *buf, size_t len) {
bool success = false; bool success = false;
size_t left = (q->len - q->head + q->tail -1) % q->len; size_t left = (q->len - q->head + q->tail - 1) % q->len;
if (len + sizeof(size_t) <= left) { if (len + sizeof(size_t) <= left) {
q_copyin(q, (uint8_t *) &len, sizeof(len), q->head); q_copyin(q, (uint8_t *) &len, sizeof(len), q->head);
q_copyin(q, (uint8_t *) buf, len, (q->head + sizeof(size_t)) % q->len); q_copyin(q, (uint8_t *) buf, len, (q->head + sizeof(size_t)) % q->len);
@ -529,6 +537,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
struct mg_connection *c = mg_alloc_conn(lsn->mgr); struct mg_connection *c = mg_alloc_conn(lsn->mgr);
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
s->timer = ((struct mip_if *) c->mgr->priv)->now + MIP_TCP_KEEPALIVE_MS;
c->rem.ip = pkt->ip->src; c->rem.ip = pkt->ip->src;
c->rem.port = pkt->tcp->sport; c->rem.port = pkt->tcp->sport;
MG_DEBUG(("%lu accepted %lx:%hx", c->id, mg_ntohl(c->rem.ip), c->rem.port)); MG_DEBUG(("%lu accepted %lx:%hx", c->id, mg_ntohl(c->rem.ip), c->rem.port));
@ -545,26 +554,92 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
return c; return c;
} }
static void read_conn(struct mg_connection *c, struct pkt *pkt) { static void settmout(struct mg_connection *c, uint8_t type) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS;
s->timer = ifp->now + n;
s->ttype = type;
MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
}
long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1);
size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
if (tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
s->seq += (uint32_t) len;
if (s->ttype == MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
} else {
return MG_IO_ERR;
}
return (long) len;
}
long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
struct connstate *s = (struct connstate *) (c + 1);
if (s->raw.len == 0) return MG_IO_WAIT;
if (len > s->raw.len) len = s->raw.len;
memcpy(buf, s->raw.buf, len);
mg_iobuf_del(&s->raw, 0, len);
MG_DEBUG(("%lu", len));
return (long) len;
}
static void read_conn(struct mg_connection *c, struct pkt *pkt) {
struct connstate *s = (struct connstate *) (c + 1);
struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
s->raw.align = c->recv.align;
if (pkt->tcp->flags & TH_FIN) { if (pkt->tcp->flags & TH_FIN) {
s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack); s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
c->is_closing = 1; c->is_closing = 1;
} else if (pkt->pay.len == 0) { } else if (pkt->pay.len == 0) {
} else if (c->recv.size - c->recv.len < pkt->pay.len && // TODO(cpq): handle this peer's ACK
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
mg_error(c, "oom");
} else if (mg_ntohl(pkt->tcp->seq) != s->ack) { } else if (mg_ntohl(pkt->tcp->seq) != s->ack) {
mg_error(c, "oob: %x %x", mg_ntohl(pkt->tcp->seq), s->ack); // TODO(cpq): peer sent us SEQ which we don't expect. Retransmit rather
// than close this connection
mg_error(c, "SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack);
} else if (io->size - io->len < pkt->pay.len &&
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
mg_error(c, "oom");
} else { } else {
// Copy TCP payload into the IO buffer. If the connection is plain text, we
// copy to c->recv. If the connection is TLS, this data is encrypted,
// therefore we copy that encrypted data to the s->raw iobuffer instead,
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
// call back mg_io_recv() which grabs raw data from s->raw
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
io->len += pkt->pay.len;
// Advance ACK counter and setup a timer to send an ACK back
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); settmout(c, MIP_TTYPE_ACK);
c->recv.len += pkt->pay.len;
struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); if (c->is_tls) {
mg_call(c, MG_EV_READ, &evd); // TLS connection. Make room for decrypted data in c->recv
s->timer = ifp->now + MIP_TCP_ACK_MS; // Don't send an ACK immediately io = &c->recv;
s->ttype = 0; // Set ACK timeout instead if (io->size - io->len < pkt->pay.len &&
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
mg_error(c, "oom");
} else {
// Decrypt data directly into c->recv
long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len);
if (n == MG_IO_ERR) {
mg_error(c, "TLS recv error");
} else if (n > 0) {
// Decrypted successfully - trigger MG_EV_READ
io->len += (size_t) n;
struct mg_str evd =
mg_str_n((char *) &io->buf[io->len - (size_t) n], (size_t) n);
mg_call(c, MG_EV_READ, &evd);
}
}
} else {
// Plain text connection, data is already in c->recv, trigger MG_EV_READ
struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len);
mg_call(c, MG_EV_READ, &evd);
}
} }
} }
@ -572,9 +647,8 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, false); struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
if (c != NULL) { if (c != NULL && s->ttype == MIP_TTYPE_KEEPALIVE) {
s->timer = ifp->now + MIP_TCP_KEEPALIVE_MS; // Shift next keep-alive settmout(c, MIP_TTYPE_KEEPALIVE);
s->ttype = 1; // ping to the future
} }
#if 0 #if 0
MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
@ -582,7 +656,8 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) {
if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) { if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) {
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
c->is_connecting = 0; // Client connected c->is_connecting = 0; // Client connected
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
} else if (c != NULL && c->is_connecting) { } else if (c != NULL && c->is_connecting) {
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
} else if (c != NULL) { } else if (c != NULL) {
@ -729,19 +804,19 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Process timeouts // Process timeouts
for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) { for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
if (c->is_udp || c->is_listening) continue; if (c->is_udp || c->is_listening) continue;
if (c->is_connecting || c->is_resolving) continue;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
if (uptime_ms > s->timer) { if (uptime_ms > s->timer) {
if (s->ttype == 0) { if (s->ttype == MIP_TTYPE_ACK) {
MG_DEBUG(("%lu sending ack", c->id)); MG_DEBUG(("%lu ack", c->id));
tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0); mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
} else { } else {
MG_DEBUG(("%lu sending keepalive", c->id)); MG_DEBUG(("%lu keepalive", c->id));
tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0); mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
} }
s->timer = uptime_ms + MIP_TCP_KEEPALIVE_MS; settmout(c, MIP_TTYPE_KEEPALIVE);
s->ttype = 1;
} }
} }
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
@ -757,7 +832,8 @@ static void on_rx(void *buf, size_t len, void *userdata) {
#ifndef MIP_QPROFILE #ifndef MIP_QPROFILE
if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len)); if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len));
#else #else
qp_mark(q_write(&ifp->queue, buf, len) ? QP_FRAMEPUSHED:QP_FRAMEDROPPED, (int) len); qp_mark(q_write(&ifp->queue, buf, len) ? QP_FRAMEPUSHED : QP_FRAMEDROPPED,
(int) len);
#endif #endif
} }
@ -766,7 +842,7 @@ void mip_init(struct mg_mgr *mgr, struct mip_cfg *ipcfg,
if (driver->init && !driver->init(ipcfg->mac, driver_data)) { if (driver->init && !driver->init(ipcfg->mac, driver_data)) {
MG_ERROR(("driver init failed")); MG_ERROR(("driver init failed"));
} else { } else {
size_t maxpktsize = 1518, qlen = driver->setrx ? 1024 * 16 : 0; size_t maxpktsize = 1518, qlen = driver->setrx ? MIP_QSIZE : 0;
struct mip_if *ifp = struct mip_if *ifp =
(struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen); (struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen);
memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac)); memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac));
@ -811,7 +887,7 @@ void mg_connect_resolved(struct mg_connection *c) {
if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT; if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT;
c->loc.ip = ifp->ip; c->loc.ip = ifp->ip;
c->loc.port = mg_htons(ifp->eport++); c->loc.port = mg_htons(ifp->eport++);
MG_DEBUG(("%lu %08lx.%hu->%08lx.%hu", c->id, mg_ntohl(c->loc.ip), MG_DEBUG(("%lu %08lx:%hu->%08lx:%hu", c->id, mg_ntohl(c->loc.ip),
mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), mg_ntohs(c->rem.port))); mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), mg_ntohs(c->rem.port)));
mg_call(c, MG_EV_RESOLVE, NULL); mg_call(c, MG_EV_RESOLVE, NULL);
if (c->is_udp) { if (c->is_udp) {
@ -829,29 +905,28 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
} }
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
struct connstate *s = (struct connstate *) (c + 1); : mg_io_send(c, c->send.buf, c->send.len);
size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; if (len > 0) {
if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; mg_iobuf_del(&c->send, 0, (size_t) len);
sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, mg_call(c, MG_EV_WRITE, &len);
mg_htonl(s->seq), mg_htonl(s->ack), c->send.buf, n);
if (sent > 0) {
mg_iobuf_del(&c->send, 0, n);
s->seq += (uint32_t) n;
mg_call(c, MG_EV_WRITE, &n);
} }
s->ttype = 1, s->timer = ifp->now + MIP_TCP_KEEPALIVE_MS; // Clear ACK timer
} }
static void fin_conn(struct mg_connection *c) { static void close_conn(struct mg_connection *c) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_iobuf_free(&s->raw); // For TLS connections, release raw data
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); if (c->is_udp == false && c->is_listening == false) { // For TCP conns,
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; // send TCP FIN
tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
}
mg_close_conn(c);
} }
static bool can_write(struct mg_connection *c) { static bool can_write(struct mg_connection *c) {
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0; return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
c->is_tls_hs == 0;
} }
void mg_mgr_poll(struct mg_mgr *mgr, int ms) { void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
@ -861,12 +936,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_timer_poll(&mgr->timers, now); mg_timer_poll(&mgr->timers, now);
for (c = mgr->conns; c != NULL; c = tmp) { for (c = mgr->conns; c != NULL; c = tmp) {
tmp = c->next; tmp = c->next;
if (c->is_tls_hs) mg_tls_handshake(c);
if (can_write(c)) write_conn(c); if (can_write(c)) write_conn(c);
if (c->is_draining && c->send.len == 0) c->is_closing = 1; if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_closing) { if (c->is_closing) close_conn(c);
if (c->is_udp == false && c->is_listening == false) fin_conn(c);
mg_close_conn(c);
}
} }
(void) ms; (void) ms;
} }
@ -880,54 +953,49 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
res = true; res = true;
} else { } else {
// tx_tdp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); res = mg_iobuf_add(&c->send, c->send.len, buf, len);
return mg_iobuf_add(&c->send, c->send.len, buf, len);
} }
return res; return res;
} }
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
#pragma pack(push, 1) #pragma pack(push, 1)
struct qpentry { struct qpentry {
uint64_t timestamp; uint64_t timestamp;
uint16_t type; uint16_t type;
uint16_t len; uint16_t len;
}; };
#pragma pack(pop) #pragma pack(pop)
static struct queue qp; static struct queue qp;
void qp_mark(unsigned int type, int len) void qp_mark(unsigned int type, int len) {
{ static bool ovf = false;
static bool ovf = false; struct qpentry e = {.timestamp = mg_millis(),
struct qpentry e = { .type = ovf ? (uint16_t) QP_QUEUEOVF : (uint16_t) type,
.timestamp = mg_millis(), .len = (uint16_t) len};
.type = ovf ? (uint16_t)QP_QUEUEOVF : (uint16_t)type,
.len = (uint16_t) len
};
ovf = !q_write(&qp, &e, sizeof(e)); ovf = !q_write(&qp, &e, sizeof(e));
} }
void qp_log(void) void qp_log(void) {
{ struct qpentry e;
struct qpentry e;
for(int i=0 ; i < 10 ; i++) for (int i = 0; i < 10; i++)
if(q_read(&qp, &e)) MG_INFO(("%llu, %u, %u", e.timestamp, e.type, e.len)); if (q_read(&qp, &e))
else break; MG_INFO(("%llu, %u, %u", e.timestamp, e.type, e.len));
else
break;
} }
void qp_init(void) void qp_init(void) {
{ unsigned int qlen = 500 * (sizeof(size_t) + sizeof(struct qpentry));
unsigned int qlen = 500 * (sizeof(size_t) + sizeof(struct qpentry));
qp.buf = calloc(1, qlen); qp.buf = calloc(1, qlen);
qp.len = qlen; qp.len = qlen;
// THERE IS NO FREE // THERE IS NO FREE
} }
#endif // MIP_QPROFILE #endif // MIP_QPROFILE
#endif // MG_ENABLE_MIP #endif // MG_ENABLE_MIP

View File

@ -263,7 +263,7 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
MG_ERROR(("Unexpected DNS response:")); MG_ERROR(("Unexpected DNS response:"));
mg_hexdump(c->recv.buf, c->recv.len); mg_hexdump(c->recv.buf, c->recv.len);
} else { } else {
MG_VERBOSE(("%s %d", dm.name, dm.resolved)); // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL; for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) { d = tmp) {
tmp = d->next; tmp = d->next;
@ -3198,7 +3198,7 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
c->is_closing = 1; c->is_closing = 1;
break; break;
} else if (rc == MQTT_OK) { } else if (rc == MQTT_OK) {
MG_VERBOSE(("%p MQTT CMD %d len %d [%.*s]", c->fd, mm.cmd, MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
(int) mm.dgram.len, (int) mm.data.len, mm.data.ptr)); (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr));
switch (mm.cmd) { switch (mm.cmd) {
case MQTT_CMD_CONNACK: case MQTT_CMD_CONNACK:
@ -3900,7 +3900,7 @@ int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
int64_t t2 = gettimestamp((uint32_t *) &buf[40]); int64_t t2 = gettimestamp((uint32_t *) &buf[40]);
int64_t t3 = (int64_t) mg_millis(); int64_t t3 = (int64_t) mg_millis();
int64_t delta = (t3 - t0) - (t2 - t1); int64_t delta = (t3 - t0) - (t2 - t1);
MG_DEBUG(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta)); MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta));
res = t2 + delta / 2; res = t2 + delta / 2;
} else { } else {
MG_ERROR(("unexpected version: %d", version)); MG_ERROR(("unexpected version: %d", version));
@ -3912,6 +3912,7 @@ static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
if (milliseconds > 0) { if (milliseconds > 0) {
MG_INFO(("%lu got time: %lld ms from epoch", c->id, milliseconds));
mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000), MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
(unsigned) (milliseconds % 1000))); (unsigned) (milliseconds % 1000)));
@ -4037,8 +4038,7 @@ static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
#endif #endif
} }
bool mg_sock_would_block(void); static bool mg_sock_would_block(void) {
bool mg_sock_would_block(void) {
int err = MG_SOCK_ERRNO; int err = MG_SOCK_ERRNO;
return err == EINPROGRESS || err == EWOULDBLOCK return err == EINPROGRESS || err == EWOULDBLOCK
#ifndef WINCE #ifndef WINCE
@ -4050,8 +4050,7 @@ bool mg_sock_would_block(void) {
; ;
} }
bool mg_sock_conn_reset(void); static bool mg_sock_conn_reset(void) {
bool mg_sock_conn_reset(void) {
int err = MG_SOCK_ERRNO; int err = MG_SOCK_ERRNO;
#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK #if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
return err == WSAECONNRESET; return err == WSAECONNRESET;
@ -4069,9 +4068,9 @@ static void setlocaddr(SOCKET fd, struct mg_addr *addr) {
} }
static void iolog(struct mg_connection *c, char *buf, long n, bool r) { static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
if (n == 0) { if (n == MG_IO_WAIT) {
// Do nothing // Do nothing
} else if (n < 0) { } else if (n <= 0) {
c->is_closing = 1; // Termination. Don't call mg_error(): #1529 c->is_closing = 1; // Termination. Don't call mg_error(): #1529
} else if (n > 0) { } else if (n > 0) {
if (c->is_hexdumping) { if (c->is_hexdumping) {
@ -4104,7 +4103,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
} }
} }
static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) { long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
long n; long n;
if (c->is_udp) { if (c->is_udp) {
union usa usa; union usa usa;
@ -4114,15 +4113,18 @@ static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) {
} else { } else {
n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
#if MG_ARCH == MG_ARCH_RTX #if MG_ARCH == MG_ARCH_RTX
if (n == BSD_EWOULDBLOCK) return 0; if (n == BSD_EWOULDBLOCK) return MG_IO_WAIT;
#endif #endif
} }
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n; if (n < 0 && mg_sock_would_block()) return MG_IO_WAIT;
if (n < 0 && mg_sock_conn_reset()) return MG_IO_RESET;
if (n <= 0) return MG_IO_ERR;
return n;
} }
bool mg_send(struct mg_connection *c, const void *buf, size_t len) { bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
if (c->is_udp) { if (c->is_udp) {
long n = mg_sock_send(c, buf, len); long n = mg_io_send(c, buf, len);
MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len, MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len,
(int) c->recv.len, n, MG_SOCK_ERRNO)); (int) c->recv.len, n, MG_SOCK_ERRNO));
iolog(c, (char *) buf, n, false); iolog(c, (char *) buf, n, false);
@ -4223,7 +4225,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
return success; return success;
} }
static long mg_sock_recv(struct mg_connection *c, void *buf, size_t len) { long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
long n = 0; long n = 0;
if (c->is_udp) { if (c->is_udp) {
union usa usa; union usa usa;
@ -4233,7 +4235,10 @@ static long mg_sock_recv(struct mg_connection *c, void *buf, size_t len) {
} else { } else {
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n; if (n < 0 && mg_sock_would_block()) return MG_IO_WAIT;
if (n < 0 && mg_sock_conn_reset()) return MG_IO_RESET;
if (n <= 0) return MG_IO_ERR;
return n;
} }
// NOTE(lsm): do only one iteration of reads, cause some systems // NOTE(lsm): do only one iteration of reads, cause some systems
@ -4248,7 +4253,7 @@ static void read_conn(struct mg_connection *c) {
} else { } else {
char *buf = (char *) &c->recv.buf[c->recv.len]; char *buf = (char *) &c->recv.buf[c->recv.len];
size_t len = c->recv.size - c->recv.len; size_t len = c->recv.size - c->recv.len;
n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_sock_recv(c, buf, len); n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len);
MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, MG_DEBUG(("%lu %p 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->send.len, (long) c->send.size, (long) c->recv.len,
(long) c->recv.size, n, MG_SOCK_ERRNO)); (long) c->recv.size, n, MG_SOCK_ERRNO));
@ -4259,7 +4264,7 @@ static void read_conn(struct mg_connection *c) {
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
char *buf = (char *) c->send.buf; char *buf = (char *) c->send.buf;
size_t len = c->send.len; size_t len = c->send.len;
long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_sock_send(c, buf, len); long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len);
MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, MG_DEBUG(("%lu %p 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->send.len, (long) c->send.size, (long) c->recv.len,
(long) c->recv.size, n, MG_SOCK_ERRNO)); (long) c->recv.size, n, MG_SOCK_ERRNO));
@ -4311,12 +4316,10 @@ static void setsockopts(struct mg_connection *c) {
} }
void mg_connect_resolved(struct mg_connection *c) { void mg_connect_resolved(struct mg_connection *c) {
// char buf[40];
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP
// mg_straddr(&c->rem, buf, sizeof(buf)); c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket
c->fd = S2PTR(socket(af, type, 0)); c->is_resolving = 0; // Clear resolving flag
c->is_resolving = 0;
if (FD(c) == INVALID_SOCKET) { if (FD(c) == INVALID_SOCKET) {
mg_error(c, "socket(): %d", MG_SOCK_ERRNO); mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
} else if (c->is_udp) { } else if (c->is_udp) {
@ -5075,46 +5078,27 @@ void mg_tls_free(struct mg_connection *c) {
} }
} }
bool mg_sock_would_block(void);
bool mg_sock_conn_reset(void);
#if MG_ENABLE_MIP
#else
static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) { static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
struct mg_connection *c = (struct mg_connection *) ctx; long n = mg_io_send((struct mg_connection *) ctx, buf, len);
int fd = (int) (size_t) c->fd; MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
int n = (int) send(fd, buf, len, 0); if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
MG_VERBOSE(("%lu n=%d, errno=%d", c->id, n, errno)); if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
if (n < 0) { if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
if (mg_sock_would_block()) return MBEDTLS_ERR_SSL_WANT_WRITE; return (int) n;
if (mg_sock_conn_reset()) return MBEDTLS_ERR_NET_CONN_RESET;
return MBEDTLS_ERR_NET_SEND_FAILED;
}
return n;
} }
static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) { static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
struct mg_connection *c = (struct mg_connection *) ctx; long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
int n, fd = (int) (size_t) c->fd; MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
n = (int) recv(fd, buf, len, 0); if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
MG_VERBOSE(("%lu n=%d, errno=%d", c->id, n, errno)); if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
if (n < 0) { if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
if (mg_sock_would_block()) return MBEDTLS_ERR_SSL_WANT_READ; return (int) n;
if (mg_sock_conn_reset()) return MBEDTLS_ERR_NET_CONN_RESET;
return MBEDTLS_ERR_NET_RECV_FAILED;
}
return n;
} }
#endif
void mg_tls_handshake(struct mg_connection *c) { void mg_tls_handshake(struct mg_connection *c) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int rc; int rc = mbedtls_ssl_handshake(&tls->ssl);
#if MG_ENABLE_MIP
#else
mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
#endif
rc = mbedtls_ssl_handshake(&tls->ssl);
if (rc == 0) { // Success if (rc == 0) { // Success
MG_DEBUG(("%lu success", c->id)); MG_DEBUG(("%lu success", c->id));
c->is_tls_hs = 0; c->is_tls_hs = 0;
@ -5238,6 +5222,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
c->tls = tls; c->tls = tls;
c->is_tls = 1; c->is_tls = 1;
c->is_tls_hs = 1; c->is_tls_hs = 1;
mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
mg_tls_handshake(c); mg_tls_handshake(c);
} }
@ -5254,13 +5239,17 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_READ ? 0 : n; if (n == MBEDTLS_ERR_SSL_WANT_READ) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_WRITE ? 0 : n; if (n == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
#endif #endif
@ -5408,13 +5397,17 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int n = SSL_read(tls->ssl, buf, (int) len); int n = SSL_read(tls->ssl, buf, (int) len);
return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int n = SSL_write(tls->ssl, buf, (int) len); int n = SSL_write(tls->ssl, buf, (int) len);
return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
#endif #endif
@ -6250,9 +6243,12 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
if (n > 0) { if (n > 0) {
uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
n = w5500_r2(s, W5500_RX0, ptr); // Read frame length n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
if (n <= len + 2) r = n - 2, w5500_rn(s, W5500_RX0, ptr + 2, buf, r); if (n <= len + 2 && n > 1) {
w5500_w2(s, W5500_S0, 0x28, ptr + n); // Advance read pointer r = (uint16_t) (n - 2);
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
}
w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
// printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
} }
return r; return r;
@ -6261,11 +6257,11 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
static size_t w5500_tx(const void *buf, size_t buflen, void *data) { static size_t w5500_tx(const void *buf, size_t buflen, void *data) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) data;
uint16_t n = 0, len = (uint16_t) buflen; uint16_t n = 0, len = (uint16_t) buflen;
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
w5500_w2(s, W5500_S0, 0x24, ptr + len); // Advance write pointer w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
for (int i = 0; i < 40; i++) { for (int i = 0; i < 40; i++) {
uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
if (ir == 0) continue; if (ir == 0) continue;
@ -6321,15 +6317,23 @@ struct mip_driver mip_driver_w5500 = {
#ifndef MIP_ARP_ENTRIES #ifndef MIP_ARP_ENTRIES
#define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 #define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21
#endif #endif
#ifndef MIP_QSIZE
#define MIP_QSIZE (16 * 1024) // Queue size
#endif
#define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size #define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size
#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms #define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
#define MIP_TCP_ACK_MS 150 // Timeout for ACKing #define MIP_TCP_ACK_MS 150 // Timeout for ACKing
struct connstate { struct connstate {
uint32_t seq, ack; // TCP seq/ack counters uint32_t seq, ack; // TCP seq/ack counters
uint64_t timer; // TCP keep-alive / ACK timer uint64_t timer; // TCP keep-alive / ACK timer
uint8_t mac[6]; // Peer MAC address uint8_t mac[6]; // Peer MAC address
uint8_t ttype; // Timer type. 0: ack, 1: keep-alive uint8_t ttype; // Timer type. 0: ack, 1: keep-alive
#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
struct mg_iobuf raw; // For TLS only. Incoming raw data
}; };
struct str { struct str {
@ -6490,7 +6494,7 @@ static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) {
static bool q_write(struct queue *q, const void *buf, size_t len) { static bool q_write(struct queue *q, const void *buf, size_t len) {
bool success = false; bool success = false;
size_t left = (q->len - q->head + q->tail -1) % q->len; size_t left = (q->len - q->head + q->tail - 1) % q->len;
if (len + sizeof(size_t) <= left) { if (len + sizeof(size_t) <= left) {
q_copyin(q, (uint8_t *) &len, sizeof(len), q->head); q_copyin(q, (uint8_t *) &len, sizeof(len), q->head);
q_copyin(q, (uint8_t *) buf, len, (q->head + sizeof(size_t)) % q->len); q_copyin(q, (uint8_t *) buf, len, (q->head + sizeof(size_t)) % q->len);
@ -6835,6 +6839,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
struct mg_connection *c = mg_alloc_conn(lsn->mgr); struct mg_connection *c = mg_alloc_conn(lsn->mgr);
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
s->timer = ((struct mip_if *) c->mgr->priv)->now + MIP_TCP_KEEPALIVE_MS;
c->rem.ip = pkt->ip->src; c->rem.ip = pkt->ip->src;
c->rem.port = pkt->tcp->sport; c->rem.port = pkt->tcp->sport;
MG_DEBUG(("%lu accepted %lx:%hx", c->id, mg_ntohl(c->rem.ip), c->rem.port)); MG_DEBUG(("%lu accepted %lx:%hx", c->id, mg_ntohl(c->rem.ip), c->rem.port));
@ -6851,26 +6856,92 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
return c; return c;
} }
static void read_conn(struct mg_connection *c, struct pkt *pkt) { static void settmout(struct mg_connection *c, uint8_t type) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS;
s->timer = ifp->now + n;
s->ttype = type;
MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
}
long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1);
size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
if (tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
s->seq += (uint32_t) len;
if (s->ttype == MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
} else {
return MG_IO_ERR;
}
return (long) len;
}
long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
struct connstate *s = (struct connstate *) (c + 1);
if (s->raw.len == 0) return MG_IO_WAIT;
if (len > s->raw.len) len = s->raw.len;
memcpy(buf, s->raw.buf, len);
mg_iobuf_del(&s->raw, 0, len);
MG_DEBUG(("%lu", len));
return (long) len;
}
static void read_conn(struct mg_connection *c, struct pkt *pkt) {
struct connstate *s = (struct connstate *) (c + 1);
struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
s->raw.align = c->recv.align;
if (pkt->tcp->flags & TH_FIN) { if (pkt->tcp->flags & TH_FIN) {
s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack); s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
c->is_closing = 1; c->is_closing = 1;
} else if (pkt->pay.len == 0) { } else if (pkt->pay.len == 0) {
} else if (c->recv.size - c->recv.len < pkt->pay.len && // TODO(cpq): handle this peer's ACK
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
mg_error(c, "oom");
} else if (mg_ntohl(pkt->tcp->seq) != s->ack) { } else if (mg_ntohl(pkt->tcp->seq) != s->ack) {
mg_error(c, "oob: %x %x", mg_ntohl(pkt->tcp->seq), s->ack); // TODO(cpq): peer sent us SEQ which we don't expect. Retransmit rather
// than close this connection
mg_error(c, "SEQ != ACK: %x %x", mg_ntohl(pkt->tcp->seq), s->ack);
} else if (io->size - io->len < pkt->pay.len &&
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
mg_error(c, "oom");
} else { } else {
// Copy TCP payload into the IO buffer. If the connection is plain text, we
// copy to c->recv. If the connection is TLS, this data is encrypted,
// therefore we copy that encrypted data to the s->raw iobuffer instead,
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
// call back mg_io_recv() which grabs raw data from s->raw
memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
io->len += pkt->pay.len;
// Advance ACK counter and setup a timer to send an ACK back
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); settmout(c, MIP_TTYPE_ACK);
c->recv.len += pkt->pay.len;
struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); if (c->is_tls) {
mg_call(c, MG_EV_READ, &evd); // TLS connection. Make room for decrypted data in c->recv
s->timer = ifp->now + MIP_TCP_ACK_MS; // Don't send an ACK immediately io = &c->recv;
s->ttype = 0; // Set ACK timeout instead if (io->size - io->len < pkt->pay.len &&
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
mg_error(c, "oom");
} else {
// Decrypt data directly into c->recv
long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len);
if (n == MG_IO_ERR) {
mg_error(c, "TLS recv error");
} else if (n > 0) {
// Decrypted successfully - trigger MG_EV_READ
io->len += (size_t) n;
struct mg_str evd =
mg_str_n((char *) &io->buf[io->len - (size_t) n], (size_t) n);
mg_call(c, MG_EV_READ, &evd);
}
}
} else {
// Plain text connection, data is already in c->recv, trigger MG_EV_READ
struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len);
mg_call(c, MG_EV_READ, &evd);
}
} }
} }
@ -6878,9 +6949,8 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) {
struct mg_connection *c = getpeer(ifp->mgr, pkt, false); struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
if (c != NULL) { if (c != NULL && s->ttype == MIP_TTYPE_KEEPALIVE) {
s->timer = ifp->now + MIP_TCP_KEEPALIVE_MS; // Shift next keep-alive settmout(c, MIP_TTYPE_KEEPALIVE);
s->ttype = 1; // ping to the future
} }
#if 0 #if 0
MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
@ -6888,7 +6958,8 @@ static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) {
if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) { if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) {
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
c->is_connecting = 0; // Client connected c->is_connecting = 0; // Client connected
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
} else if (c != NULL && c->is_connecting) { } else if (c != NULL && c->is_connecting) {
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
} else if (c != NULL) { } else if (c != NULL) {
@ -7035,19 +7106,19 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Process timeouts // Process timeouts
for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) { for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
if (c->is_udp || c->is_listening) continue; if (c->is_udp || c->is_listening) continue;
if (c->is_connecting || c->is_resolving) continue;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
if (uptime_ms > s->timer) { if (uptime_ms > s->timer) {
if (s->ttype == 0) { if (s->ttype == MIP_TTYPE_ACK) {
MG_DEBUG(("%lu sending ack", c->id)); MG_DEBUG(("%lu ack", c->id));
tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), "", 0); mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
} else { } else {
MG_DEBUG(("%lu sending keepalive", c->id)); MG_DEBUG(("%lu keepalive", c->id));
tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0); mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
} }
s->timer = uptime_ms + MIP_TCP_KEEPALIVE_MS; settmout(c, MIP_TTYPE_KEEPALIVE);
s->ttype = 1;
} }
} }
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
@ -7063,7 +7134,8 @@ static void on_rx(void *buf, size_t len, void *userdata) {
#ifndef MIP_QPROFILE #ifndef MIP_QPROFILE
if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len)); if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len));
#else #else
qp_mark(q_write(&ifp->queue, buf, len) ? QP_FRAMEPUSHED:QP_FRAMEDROPPED, (int) len); qp_mark(q_write(&ifp->queue, buf, len) ? QP_FRAMEPUSHED : QP_FRAMEDROPPED,
(int) len);
#endif #endif
} }
@ -7072,7 +7144,7 @@ void mip_init(struct mg_mgr *mgr, struct mip_cfg *ipcfg,
if (driver->init && !driver->init(ipcfg->mac, driver_data)) { if (driver->init && !driver->init(ipcfg->mac, driver_data)) {
MG_ERROR(("driver init failed")); MG_ERROR(("driver init failed"));
} else { } else {
size_t maxpktsize = 1518, qlen = driver->setrx ? 1024 * 16 : 0; size_t maxpktsize = 1518, qlen = driver->setrx ? MIP_QSIZE : 0;
struct mip_if *ifp = struct mip_if *ifp =
(struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen); (struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen);
memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac)); memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac));
@ -7117,7 +7189,7 @@ void mg_connect_resolved(struct mg_connection *c) {
if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT; if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT;
c->loc.ip = ifp->ip; c->loc.ip = ifp->ip;
c->loc.port = mg_htons(ifp->eport++); c->loc.port = mg_htons(ifp->eport++);
MG_DEBUG(("%lu %08lx.%hu->%08lx.%hu", c->id, mg_ntohl(c->loc.ip), MG_DEBUG(("%lu %08lx:%hu->%08lx:%hu", c->id, mg_ntohl(c->loc.ip),
mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), mg_ntohs(c->rem.port))); mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), mg_ntohs(c->rem.port)));
mg_call(c, MG_EV_RESOLVE, NULL); mg_call(c, MG_EV_RESOLVE, NULL);
if (c->is_udp) { if (c->is_udp) {
@ -7135,29 +7207,28 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
} }
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
struct connstate *s = (struct connstate *) (c + 1); : mg_io_send(c, c->send.buf, c->send.len);
size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; if (len > 0) {
if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; mg_iobuf_del(&c->send, 0, (size_t) len);
sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, mg_call(c, MG_EV_WRITE, &len);
mg_htonl(s->seq), mg_htonl(s->ack), c->send.buf, n);
if (sent > 0) {
mg_iobuf_del(&c->send, 0, n);
s->seq += (uint32_t) n;
mg_call(c, MG_EV_WRITE, &n);
} }
s->ttype = 1, s->timer = ifp->now + MIP_TCP_KEEPALIVE_MS; // Clear ACK timer
} }
static void fin_conn(struct mg_connection *c) { static void close_conn(struct mg_connection *c) {
struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, mg_iobuf_free(&s->raw); // For TLS connections, release raw data
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); if (c->is_udp == false && c->is_listening == false) { // For TCP conns,
struct mip_if *ifp = (struct mip_if *) c->mgr->priv; // send TCP FIN
tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
}
mg_close_conn(c);
} }
static bool can_write(struct mg_connection *c) { static bool can_write(struct mg_connection *c) {
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0; return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
c->is_tls_hs == 0;
} }
void mg_mgr_poll(struct mg_mgr *mgr, int ms) { void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
@ -7167,12 +7238,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_timer_poll(&mgr->timers, now); mg_timer_poll(&mgr->timers, now);
for (c = mgr->conns; c != NULL; c = tmp) { for (c = mgr->conns; c != NULL; c = tmp) {
tmp = c->next; tmp = c->next;
if (c->is_tls_hs) mg_tls_handshake(c);
if (can_write(c)) write_conn(c); if (can_write(c)) write_conn(c);
if (c->is_draining && c->send.len == 0) c->is_closing = 1; if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_closing) { if (c->is_closing) close_conn(c);
if (c->is_udp == false && c->is_listening == false) fin_conn(c);
mg_close_conn(c);
}
} }
(void) ms; (void) ms;
} }
@ -7186,54 +7255,49 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
res = true; res = true;
} else { } else {
// tx_tdp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); res = mg_iobuf_add(&c->send, c->send.len, buf, len);
return mg_iobuf_add(&c->send, c->send.len, buf, len);
} }
return res; return res;
} }
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
#pragma pack(push, 1) #pragma pack(push, 1)
struct qpentry { struct qpentry {
uint64_t timestamp; uint64_t timestamp;
uint16_t type; uint16_t type;
uint16_t len; uint16_t len;
}; };
#pragma pack(pop) #pragma pack(pop)
static struct queue qp; static struct queue qp;
void qp_mark(unsigned int type, int len) void qp_mark(unsigned int type, int len) {
{ static bool ovf = false;
static bool ovf = false; struct qpentry e = {.timestamp = mg_millis(),
struct qpentry e = { .type = ovf ? (uint16_t) QP_QUEUEOVF : (uint16_t) type,
.timestamp = mg_millis(), .len = (uint16_t) len};
.type = ovf ? (uint16_t)QP_QUEUEOVF : (uint16_t)type,
.len = (uint16_t) len
};
ovf = !q_write(&qp, &e, sizeof(e)); ovf = !q_write(&qp, &e, sizeof(e));
} }
void qp_log(void) void qp_log(void) {
{ struct qpentry e;
struct qpentry e;
for(int i=0 ; i < 10 ; i++) for (int i = 0; i < 10; i++)
if(q_read(&qp, &e)) MG_INFO(("%llu, %u, %u", e.timestamp, e.type, e.len)); if (q_read(&qp, &e))
else break; MG_INFO(("%llu, %u, %u", e.timestamp, e.type, e.len));
else
break;
} }
void qp_init(void) void qp_init(void) {
{ unsigned int qlen = 500 * (sizeof(size_t) + sizeof(struct qpentry));
unsigned int qlen = 500 * (sizeof(size_t) + sizeof(struct qpentry));
qp.buf = calloc(1, qlen); qp.buf = calloc(1, qlen);
qp.len = qlen; qp.len = qlen;
// THERE IS NO FREE // THERE IS NO FREE
} }
#endif // MIP_QPROFILE #endif // MIP_QPROFILE
#endif // MG_ENABLE_MIP #endif // MG_ENABLE_MIP

View File

@ -1107,6 +1107,11 @@ bool mg_open_listener(struct mg_connection *c, const char *url);
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg); unsigned flags, void (*fn)(void *), void *arg);
// Low-level IO primives used by TLS layer
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 };
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
long mg_io_recv(struct mg_connection *c, void *buf, size_t len);

View File

@ -151,7 +151,7 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
MG_ERROR(("Unexpected DNS response:")); MG_ERROR(("Unexpected DNS response:"));
mg_hexdump(c->recv.buf, c->recv.len); mg_hexdump(c->recv.buf, c->recv.len);
} else { } else {
MG_VERBOSE(("%s %d", dm.name, dm.resolved)); // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL; for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
d = tmp) { d = tmp) {
tmp = d->next; tmp = d->next;

View File

@ -197,7 +197,7 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
c->is_closing = 1; c->is_closing = 1;
break; break;
} else if (rc == MQTT_OK) { } else if (rc == MQTT_OK) {
MG_VERBOSE(("%p MQTT CMD %d len %d [%.*s]", c->fd, mm.cmd, MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
(int) mm.dgram.len, (int) mm.data.len, mm.data.ptr)); (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr));
switch (mm.cmd) { switch (mm.cmd) {
case MQTT_CMD_CONNACK: case MQTT_CMD_CONNACK:

View File

@ -97,3 +97,8 @@ void mg_close_conn(struct mg_connection *c);
bool mg_open_listener(struct mg_connection *c, const char *url); bool mg_open_listener(struct mg_connection *c, const char *url);
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg); unsigned flags, void (*fn)(void *), void *arg);
// Low-level IO primives used by TLS layer
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 };
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
long mg_io_recv(struct mg_connection *c, void *buf, size_t len);

View File

@ -30,7 +30,7 @@ int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
int64_t t2 = gettimestamp((uint32_t *) &buf[40]); int64_t t2 = gettimestamp((uint32_t *) &buf[40]);
int64_t t3 = (int64_t) mg_millis(); int64_t t3 = (int64_t) mg_millis();
int64_t delta = (t3 - t0) - (t2 - t1); int64_t delta = (t3 - t0) - (t2 - t1);
MG_DEBUG(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta)); MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta));
res = t2 + delta / 2; res = t2 + delta / 2;
} else { } else {
MG_ERROR(("unexpected version: %d", version)); MG_ERROR(("unexpected version: %d", version));
@ -42,6 +42,7 @@ static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
if (milliseconds > 0) { if (milliseconds > 0) {
MG_INFO(("%lu got time: %lld ms from epoch", c->id, milliseconds));
mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000), MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
(unsigned) (milliseconds % 1000))); (unsigned) (milliseconds % 1000)));

View File

@ -84,8 +84,7 @@ static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
#endif #endif
} }
bool mg_sock_would_block(void); static bool mg_sock_would_block(void) {
bool mg_sock_would_block(void) {
int err = MG_SOCK_ERRNO; int err = MG_SOCK_ERRNO;
return err == EINPROGRESS || err == EWOULDBLOCK return err == EINPROGRESS || err == EWOULDBLOCK
#ifndef WINCE #ifndef WINCE
@ -97,8 +96,7 @@ bool mg_sock_would_block(void) {
; ;
} }
bool mg_sock_conn_reset(void); static bool mg_sock_conn_reset(void) {
bool mg_sock_conn_reset(void) {
int err = MG_SOCK_ERRNO; int err = MG_SOCK_ERRNO;
#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK #if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
return err == WSAECONNRESET; return err == WSAECONNRESET;
@ -116,9 +114,9 @@ static void setlocaddr(SOCKET fd, struct mg_addr *addr) {
} }
static void iolog(struct mg_connection *c, char *buf, long n, bool r) { static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
if (n == 0) { if (n == MG_IO_WAIT) {
// Do nothing // Do nothing
} else if (n < 0) { } else if (n <= 0) {
c->is_closing = 1; // Termination. Don't call mg_error(): #1529 c->is_closing = 1; // Termination. Don't call mg_error(): #1529
} else if (n > 0) { } else if (n > 0) {
if (c->is_hexdumping) { if (c->is_hexdumping) {
@ -151,7 +149,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
} }
} }
static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) { long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
long n; long n;
if (c->is_udp) { if (c->is_udp) {
union usa usa; union usa usa;
@ -161,15 +159,18 @@ static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) {
} else { } else {
n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
#if MG_ARCH == MG_ARCH_RTX #if MG_ARCH == MG_ARCH_RTX
if (n == BSD_EWOULDBLOCK) return 0; if (n == BSD_EWOULDBLOCK) return MG_IO_WAIT;
#endif #endif
} }
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n; if (n < 0 && mg_sock_would_block()) return MG_IO_WAIT;
if (n < 0 && mg_sock_conn_reset()) return MG_IO_RESET;
if (n <= 0) return MG_IO_ERR;
return n;
} }
bool mg_send(struct mg_connection *c, const void *buf, size_t len) { bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
if (c->is_udp) { if (c->is_udp) {
long n = mg_sock_send(c, buf, len); long n = mg_io_send(c, buf, len);
MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len, MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len,
(int) c->recv.len, n, MG_SOCK_ERRNO)); (int) c->recv.len, n, MG_SOCK_ERRNO));
iolog(c, (char *) buf, n, false); iolog(c, (char *) buf, n, false);
@ -270,7 +271,7 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
return success; return success;
} }
static long mg_sock_recv(struct mg_connection *c, void *buf, size_t len) { long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
long n = 0; long n = 0;
if (c->is_udp) { if (c->is_udp) {
union usa usa; union usa usa;
@ -280,7 +281,10 @@ static long mg_sock_recv(struct mg_connection *c, void *buf, size_t len) {
} else { } else {
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n; if (n < 0 && mg_sock_would_block()) return MG_IO_WAIT;
if (n < 0 && mg_sock_conn_reset()) return MG_IO_RESET;
if (n <= 0) return MG_IO_ERR;
return n;
} }
// NOTE(lsm): do only one iteration of reads, cause some systems // NOTE(lsm): do only one iteration of reads, cause some systems
@ -295,7 +299,7 @@ static void read_conn(struct mg_connection *c) {
} else { } else {
char *buf = (char *) &c->recv.buf[c->recv.len]; char *buf = (char *) &c->recv.buf[c->recv.len];
size_t len = c->recv.size - c->recv.len; size_t len = c->recv.size - c->recv.len;
n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_sock_recv(c, buf, len); n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len);
MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, MG_DEBUG(("%lu %p 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->send.len, (long) c->send.size, (long) c->recv.len,
(long) c->recv.size, n, MG_SOCK_ERRNO)); (long) c->recv.size, n, MG_SOCK_ERRNO));
@ -306,7 +310,7 @@ static void read_conn(struct mg_connection *c) {
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
char *buf = (char *) c->send.buf; char *buf = (char *) c->send.buf;
size_t len = c->send.len; size_t len = c->send.len;
long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_sock_send(c, buf, len); long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len);
MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, MG_DEBUG(("%lu %p 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->send.len, (long) c->send.size, (long) c->recv.len,
(long) c->recv.size, n, MG_SOCK_ERRNO)); (long) c->recv.size, n, MG_SOCK_ERRNO));
@ -358,12 +362,10 @@ static void setsockopts(struct mg_connection *c) {
} }
void mg_connect_resolved(struct mg_connection *c) { void mg_connect_resolved(struct mg_connection *c) {
// char buf[40];
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP
// mg_straddr(&c->rem, buf, sizeof(buf)); c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket
c->fd = S2PTR(socket(af, type, 0)); c->is_resolving = 0; // Clear resolving flag
c->is_resolving = 0;
if (FD(c) == INVALID_SOCKET) { if (FD(c) == INVALID_SOCKET) {
mg_error(c, "socket(): %d", MG_SOCK_ERRNO); mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
} else if (c->is_udp) { } else if (c->is_udp) {

View File

@ -24,46 +24,27 @@ void mg_tls_free(struct mg_connection *c) {
} }
} }
bool mg_sock_would_block(void);
bool mg_sock_conn_reset(void);
#if MG_ENABLE_MIP
#else
static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) { static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
struct mg_connection *c = (struct mg_connection *) ctx; long n = mg_io_send((struct mg_connection *) ctx, buf, len);
int fd = (int) (size_t) c->fd; MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
int n = (int) send(fd, buf, len, 0); if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
MG_VERBOSE(("%lu n=%d, errno=%d", c->id, n, errno)); if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
if (n < 0) { if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
if (mg_sock_would_block()) return MBEDTLS_ERR_SSL_WANT_WRITE; return (int) n;
if (mg_sock_conn_reset()) return MBEDTLS_ERR_NET_CONN_RESET;
return MBEDTLS_ERR_NET_SEND_FAILED;
}
return n;
} }
static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) { static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
struct mg_connection *c = (struct mg_connection *) ctx; long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
int n, fd = (int) (size_t) c->fd; MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
n = (int) recv(fd, buf, len, 0); if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
MG_VERBOSE(("%lu n=%d, errno=%d", c->id, n, errno)); if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
if (n < 0) { if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
if (mg_sock_would_block()) return MBEDTLS_ERR_SSL_WANT_READ; return (int) n;
if (mg_sock_conn_reset()) return MBEDTLS_ERR_NET_CONN_RESET;
return MBEDTLS_ERR_NET_RECV_FAILED;
}
return n;
} }
#endif
void mg_tls_handshake(struct mg_connection *c) { void mg_tls_handshake(struct mg_connection *c) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int rc; int rc = mbedtls_ssl_handshake(&tls->ssl);
#if MG_ENABLE_MIP
#else
mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
#endif
rc = mbedtls_ssl_handshake(&tls->ssl);
if (rc == 0) { // Success if (rc == 0) { // Success
MG_DEBUG(("%lu success", c->id)); MG_DEBUG(("%lu success", c->id));
c->is_tls_hs = 0; c->is_tls_hs = 0;
@ -187,6 +168,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
c->tls = tls; c->tls = tls;
c->is_tls = 1; c->is_tls = 1;
c->is_tls_hs = 1; c->is_tls_hs = 1;
mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) { if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
mg_tls_handshake(c); mg_tls_handshake(c);
} }
@ -203,12 +185,16 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_READ ? 0 : n; if (n == MBEDTLS_ERR_SSL_WANT_READ) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len); long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_WRITE ? 0 : n; if (n == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
#endif #endif

View File

@ -139,12 +139,16 @@ size_t mg_tls_pending(struct mg_connection *c) {
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int n = SSL_read(tls->ssl, buf, (int) len); int n = SSL_read(tls->ssl, buf, (int) len);
return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int n = SSL_write(tls->ssl, buf, (int) len); int n = SSL_write(tls->ssl, buf, (int) len);
return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
if (n <= 0) return MG_IO_ERR;
return n;
} }
#endif #endif