@@ -1,5 +1,5 @@
// Copyright (c) 2004-2013 Sergey Lyubka
-// Copyright (c) 2013-2022 Cesanta Software Limited
+// Copyright (c) 2013-2024 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
@@ -15,7 +15,7 @@
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0-only or commercial
#include "mongoose.h"
@@ -24,8 +24,7 @@
#endif
-
-static int mg_b64idx(int c) {
+static int mg_base64_encode_single(int c) {
if (c < 26) {
return c + 'A';
} else if (c < 52) {
@@ -37,7 +36,7 @@ static int mg_b64idx(int c) {
}
}
-static int mg_b64rev(int c) {
+static int mg_base64_decode_single(int c) {
if (c >= 'A' && c <= 'Z') {
return c - 'A';
} else if (c >= 'a' && c <= 'z') {
@@ -55,24 +54,24 @@ static int mg_b64rev(int c) {
}
}
-int mg_base64_update(unsigned char ch, char *to, int n) {
- int rem = (n & 3) % 3;
+size_t mg_base64_update(unsigned char ch, char *to, size_t n) {
+ unsigned long rem = (n & 3) % 3;
if (rem == 0) {
- to[n] = (char) mg_b64idx(ch >> 2);
+ to[n] = (char) mg_base64_encode_single(ch >> 2);
to[++n] = (char) ((ch & 3) << 4);
} else if (rem == 1) {
- to[n] = (char) mg_b64idx(to[n] | (ch >> 4));
+ to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4));
to[++n] = (char) ((ch & 15) << 2);
} else {
- to[n] = (char) mg_b64idx(to[n] | (ch >> 6));
- to[++n] = (char) mg_b64idx(ch & 63);
+ to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6));
+ to[++n] = (char) mg_base64_encode_single(ch & 63);
n++;
}
return n;
}
-int mg_base64_final(char *to, int n) {
- int saved = n;
+size_t mg_base64_final(char *to, size_t n) {
+ size_t saved = n;
// printf("---[%.*s]\n", n, to);
if (n & 3) n = mg_base64_update(0, to, n);
if ((saved & 3) == 2) n--;
@@ -82,20 +81,27 @@ int mg_base64_final(char *to, int n) {
return n;
}
-int mg_base64_encode(const unsigned char *p, int n, char *to) {
- int i, len = 0;
+size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) {
+ size_t i, len = 0;
+ if (dl > 0) to[0] = '\0';
+ if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0;
for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len);
len = mg_base64_final(to, len);
return len;
}
-int mg_base64_decode(const char *src, int n, char *dst) {
+size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) {
const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL
- int len = 0;
+ size_t len = 0;
+ if (dl < n / 4 * 3 + 1) goto fail;
while (src != NULL && src + 3 < end) {
- int a = mg_b64rev(src[0]), b = mg_b64rev(src[1]), c = mg_b64rev(src[2]),
- d = mg_b64rev(src[3]);
- if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) return 0;
+ int a = mg_base64_decode_single(src[0]),
+ b = mg_base64_decode_single(src[1]),
+ c = mg_base64_decode_single(src[2]),
+ d = mg_base64_decode_single(src[3]);
+ if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) {
+ goto fail;
+ }
dst[len++] = (char) ((a << 2) | (b >> 4));
if (src[2] != '=') {
dst[len++] = (char) ((b << 4) | (c >> 2));
@@ -105,1942 +111,2915 @@ int mg_base64_decode(const char *src, int n, char *dst) {
}
dst[len] = '\0';
return len;
+fail:
+ if (dl > 0) dst[0] = '\0';
+ return 0;
}
#ifdef MG_ENABLE_LINES
-#line 1 "src/dns.c"
+#line 1 "src/device_ch32v307.c"
#endif
+#if MG_DEVICE == MG_DEVICE_CH32V307
+// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
+#define FLASH_BASE 0x40022000
+#define FLASH_ACTLR (FLASH_BASE + 0)
+#define FLASH_KEYR (FLASH_BASE + 4)
+#define FLASH_OBKEYR (FLASH_BASE + 8)
+#define FLASH_STATR (FLASH_BASE + 12)
+#define FLASH_CTLR (FLASH_BASE + 16)
+#define FLASH_ADDR (FLASH_BASE + 20)
+#define FLASH_OBR (FLASH_BASE + 28)
+#define FLASH_WPR (FLASH_BASE + 32)
-
-
-struct dns_data {
- struct dns_data *next;
- struct mg_connection *c;
- uint64_t expire;
- uint16_t txnid;
-};
-
-static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
- struct mg_dns *, bool);
-
-static void mg_dns_free(struct mg_connection *c, struct dns_data *d) {
- LIST_DELETE(struct dns_data,
- (struct dns_data **) &c->mgr->active_dns_requests, d);
- free(d);
+void *mg_flash_start(void) {
+ return (void *) 0x08000000;
}
-
-void mg_resolve_cancel(struct mg_connection *c) {
- struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests;
- for (; d != NULL; d = tmp) {
- tmp = d->next;
- if (d->c == c) mg_dns_free(c, d);
+size_t mg_flash_size(void) {
+ return 480 * 1024; // First 320k is 0-wait
+}
+size_t mg_flash_sector_size(void) {
+ return 4096;
+}
+size_t mg_flash_write_align(void) {
+ return 4;
+}
+int mg_flash_bank(void) {
+ return 0;
+}
+void mg_device_reset(void) {
+ *((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
+}
+static void flash_unlock(void) {
+ static bool unlocked;
+ if (unlocked == false) {
+ MG_REG(FLASH_KEYR) = 0x45670123;
+ MG_REG(FLASH_KEYR) = 0xcdef89ab;
+ unlocked = true;
}
}
+static void flash_wait(void) {
+ while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
+}
-static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
- char *to, size_t tolen, size_t j,
- int depth) {
- size_t i = 0;
- if (tolen > 0 && depth == 0) to[0] = '\0';
- if (depth > 5) return 0;
- // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1]));
- while (ofs + i + 1 < len) {
- size_t n = s[ofs + i];
- if (n == 0) {
- i++;
- break;
- }
- if (n & 0xc0) {
- size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
- // MG_INFO(("PTR %lx", (unsigned long) ptr));
- if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
- mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0)
- return 0;
- i += 2;
- break;
- }
- if (ofs + i + n + 1 >= len) return 0;
- if (j > 0) {
- if (j < tolen) to[j] = '.';
- j++;
- }
- if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
- j += n;
- i += n + 1;
- if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
- // MG_INFO(("--> [%s]", to));
- }
- if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
- return i;
+bool mg_flash_erase(void *addr) {
+ //MG_INFO(("%p", addr));
+ flash_unlock();
+ flash_wait();
+ MG_REG(FLASH_ADDR) = (uint32_t) addr;
+ MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
+ flash_wait();
+ return true;
}
-static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
- char *dst, size_t dstlen) {
- return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0);
+static bool is_page_boundary(const void *addr) {
+ uint32_t val = (uint32_t) addr;
+ return (val & (mg_flash_sector_size() - 1)) == 0;
}
-size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
- bool is_question, struct mg_dns_rr *rr) {
- const uint8_t *s = buf + ofs, *e = &buf[len];
+bool mg_flash_write(void *addr, const void *buf, size_t len) {
+ //MG_INFO(("%p %p %lu", addr, buf, len));
+ //mg_hexdump(buf, len);
+ flash_unlock();
+ const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
+ uint16_t *dst = (uint16_t *) addr;
+ MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
+ //MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
+ while (src < end) {
+ if (is_page_boundary(dst)) mg_flash_erase(dst);
+ *dst++ = *src++;
+ flash_wait();
+ }
+ MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
+ return true;
+}
+#endif
- memset(rr, 0, sizeof(*rr));
- if (len < sizeof(struct mg_dns_header)) return 0; // Too small
- if (len > 512) return 0; // Too large, we don't expect that
- if (s >= e) return 0; // Overflow
+#ifdef MG_ENABLE_LINES
+#line 1 "src/device_dummy.c"
+#endif
- if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
- return 0;
- s += rr->nlen + 4;
- if (s > e) return 0;
- rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
- rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
- if (is_question) return (size_t) (rr->nlen + 4);
- s += 6;
- if (s > e) return 0;
- rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
- if (s + rr->alen > e) return 0;
- return (size_t) (rr->nlen + rr->alen + 10);
+#if MG_DEVICE == MG_DEVICE_NONE
+void *mg_flash_start(void) {
+ return NULL;
+}
+size_t mg_flash_size(void) {
+ return 0;
+}
+size_t mg_flash_sector_size(void) {
+ return 0;
+}
+size_t mg_flash_write_align(void) {
+ return 0;
+}
+int mg_flash_bank(void) {
+ return 0;
+}
+bool mg_flash_erase(void *location) {
+ (void) location;
+ return false;
+}
+bool mg_flash_swap_bank(void) {
+ return true;
+}
+bool mg_flash_write(void *addr, const void *buf, size_t len) {
+ (void) addr, (void) buf, (void) len;
+ return false;
+}
+void mg_device_reset(void) {
}
+#endif
-bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
- const struct mg_dns_header *h = (struct mg_dns_header *) buf;
- struct mg_dns_rr rr;
- size_t i, n, ofs = sizeof(*h);
- memset(dm, 0, sizeof(*dm));
+#ifdef MG_ENABLE_LINES
+#line 1 "src/device_flash.c"
+#endif
- if (len < sizeof(*h)) return 0; // Too small, headers dont fit
- if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
- if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity
- dm->txnid = mg_ntohs(h->txnid);
- for (i = 0; i < mg_ntohs(h->num_questions); i++) {
- if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
- // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass));
- ofs += n;
+#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 || \
+ MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060
+// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
+// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
+// object, we pad it at the end for alignment.
+//
+// Objects in the flash sector are stored sequentially:
+// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
+//
+// In order to get to the next object, read its size, then align up.
+
+// Traverse the list of saved objects
+size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
+ size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
+ uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
+ if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
+ if (size) *size = (size_t) p32[0];
+ if (key) *key = p32[1];
+ aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
+ if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
}
- for (i = 0; i < mg_ntohs(h->num_answers); i++) {
- if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
- // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass,
- // dm->name));
- mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
- ofs += n;
-
- if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
- dm->addr.is_ip6 = false;
- memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
- dm->resolved = true;
- break; // Return success
- } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
- dm->addr.is_ip6 = true;
- memcpy(&dm->addr.ip6, &buf[ofs - 16], 16);
- dm->resolved = true;
- break; // Return success
+ return aligned_size;
+}
+
+// Return the last sector of Bank 2
+static char *flash_last_sector(void) {
+ size_t ss = mg_flash_sector_size(), size = mg_flash_size();
+ char *base = (char *) mg_flash_start(), *last = base + size - ss;
+ if (mg_flash_bank() == 2) last -= size / 2;
+ return last;
+}
+
+// Find a saved object with a given key
+bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
+ char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
+ size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
+ bool ok = false;
+ if (s == NULL) s = flash_last_sector();
+ if (s < base || s >= base + mg_flash_size()) {
+ MG_ERROR(("%p is outsize of flash", sector));
+ } else if (((s - base) % ss) != 0) {
+ MG_ERROR(("%p is not a sector boundary", sector));
+ } else {
+ uint32_t k, scanned = 0;
+ while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
+ // MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
+ // mg_hexdump(s + ofs, n);
+ if (k == key && sz == len) {
+ res = s + ofs + sizeof(uint32_t) * 2;
+ memcpy(buf, res, len); // Copy object
+ ok = true; // Keep scanning for the newer versions of it
+ }
+ ofs += n, scanned++;
}
+ MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
}
- return true;
+ return ok;
}
-static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- struct dns_data *d, *tmp;
- if (ev == MG_EV_POLL) {
- uint64_t now = *(uint64_t *) ev_data;
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- // MG_DEBUG ("%lu %lu dns poll", d->expire, now));
- if (now > d->expire) mg_error(d->c, "DNS timeout");
+// For all saved objects in the sector, delete old versions of objects
+static void mg_flash_sector_cleanup(char *sector) {
+ // Buffer all saved objects into an IO buffer (backed by RAM)
+ // erase sector, and re-save them.
+ struct mg_iobuf io = {0, 0, 0, 2048};
+ size_t ss = mg_flash_sector_size();
+ size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
+ uint32_t key;
+ // Traverse all objects
+ MG_DEBUG(("Cleaning up sector %p", sector));
+ while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
+ // Delete an old copy of this object in the cache
+ for (size_t o = 0; o < io.len; o += size2 + hs) {
+ uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
+ size2 = *(uint32_t *) (io.buf + o);
+ if (k == key) {
+ mg_iobuf_del(&io, o, size2 + hs);
+ break;
+ }
}
- } else if (ev == MG_EV_READ) {
- struct mg_dns_message dm;
- int resolved = 0;
- if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
- MG_ERROR(("Unexpected DNS response:"));
- mg_hexdump(c->recv.buf, c->recv.len);
- } else {
- // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
- if (dm.txnid != d->txnid) continue;
- if (d->c->is_resolving) {
- if (dm.resolved) {
- dm.addr.port = d->c->rem.port; // Save port
- d->c->rem = dm.addr; // Copy resolved address
- MG_DEBUG(
- ("%lu %s is %I", d->c->id, dm.name, d->c->rem.is_ip6 ? 16 : 4,
- d->c->rem.is_ip6 ? &d->c->rem.ip6 : (void *) &d->c->rem.ip));
- mg_connect_resolved(d->c);
-#if MG_ENABLE_IPV6
- } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' &&
- c->mgr->use_dns6 == false) {
- struct mg_str x = mg_str(dm.name);
- mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
-#endif
- } else {
- mg_error(d->c, "%s DNS lookup failed", dm.name);
+ // And add the new copy
+ mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
+ ofs += n;
+ }
+ // All objects are cached in RAM now
+ if (mg_flash_erase(sector)) { // Erase sector. If successful,
+ for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
+ size = *(uint32_t *) (io.buf + ofs);
+ key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
+ mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
+ }
+ }
+ mg_iobuf_free(&io);
+}
+
+// Save an object with a given key - append to the end of an object list
+bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
+ char *base = (char *) mg_flash_start(), *s = (char *) sector;
+ size_t ss = mg_flash_sector_size(), ofs = 0, n;
+ bool ok = false;
+ if (s == NULL) s = flash_last_sector();
+ if (s < base || s >= base + mg_flash_size()) {
+ MG_ERROR(("%p is outsize of flash", sector));
+ } else if (((s - base) % ss) != 0) {
+ MG_ERROR(("%p is not a sector boundary", sector));
+ } else {
+ char ab[mg_flash_write_align()]; // Aligned write block
+ uint32_t hdr[2] = {(uint32_t) len, key};
+ size_t needed = sizeof(hdr) + len;
+ size_t needed_aligned = MG_ROUND_UP(needed, sizeof(ab));
+ while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
+
+ // If there is not enough space left, cleanup sector and re-eval ofs
+ if (ofs + needed_aligned >= ss) {
+ mg_flash_sector_cleanup(s);
+ ofs = 0;
+ while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
+ }
+
+ if (ofs + needed_aligned <= ss) {
+ // Enough space to save this object
+ if (sizeof(ab) < sizeof(hdr)) {
+ // Flash write granularity is 32 bit or less, write with no buffering
+ ok = mg_flash_write(s + ofs, hdr, sizeof(hdr));
+ if (ok) mg_flash_write(s + ofs + sizeof(hdr), buf, len);
+ } else {
+ // Flash granularity is sizeof(hdr) or more. We need to save in
+ // 3 chunks: initial block, bulk, rest. This is because we have
+ // two memory chunks to write: hdr and buf, on aligned boundaries.
+ n = sizeof(ab) - sizeof(hdr); // Initial chunk that we write
+ if (n > len) n = len; // is
+ memset(ab, 0xff, sizeof(ab)); // initialized to all-one
+ memcpy(ab, hdr, sizeof(hdr)); // contains the header (key + size)
+ memcpy(ab + sizeof(hdr), buf, n); // and an initial part of buf
+ MG_INFO(("saving initial block of %lu", sizeof(ab)));
+ ok = mg_flash_write(s + ofs, ab, sizeof(ab));
+ if (ok && len > n) {
+ size_t n2 = MG_ROUND_DOWN(len - n, sizeof(ab));
+ if (n2 > 0) {
+ MG_INFO(("saving bulk, %lu", n2));
+ ok = mg_flash_write(s + ofs + sizeof(ab), (char *) buf + n, n2);
+ }
+ if (ok && len > n) {
+ size_t n3 = len - n - n2;
+ if (n3 > sizeof(ab)) n3 = sizeof(ab);
+ memset(ab, 0xff, sizeof(ab));
+ memcpy(ab, (char *) buf + n + n2, n3);
+ MG_INFO(("saving rest, %lu", n3));
+ ok = mg_flash_write(s + ofs + sizeof(ab) + n2, ab, sizeof(ab));
}
- } else {
- MG_ERROR(("%lu already resolved", d->c->id));
}
- mg_dns_free(c, d);
- resolved = 1;
}
- }
- if (!resolved) MG_ERROR(("stray DNS reply"));
- c->recv.len = 0;
- } else if (ev == MG_EV_CLOSE) {
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- mg_error(d->c, "DNS error");
- mg_dns_free(c, d);
+ MG_DEBUG(("Saved %lu/%lu bytes @ %p, key %x: %d", len, needed_aligned,
+ s + ofs, key, ok));
+ MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
+ } else {
+ MG_ERROR(("Sector is full"));
}
}
- (void) fn_data;
+ return ok;
+}
+#else
+bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
+ (void) sector, (void) key, (void) buf, (void) len;
+ return false;
+}
+bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
+ (void) sector, (void) key, (void) buf, (void) len;
+ return false;
}
+#endif
-static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
- uint16_t txnid, bool ipv6) {
- struct {
- struct mg_dns_header header;
- uint8_t data[256];
- } pkt;
- size_t i, n;
- memset(&pkt, 0, sizeof(pkt));
- pkt.header.txnid = mg_htons(txnid);
- pkt.header.flags = mg_htons(0x100);
- pkt.header.num_questions = mg_htons(1);
- for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
- if (name->ptr[i] == '.' || i >= name->len) {
- pkt.data[n] = (uint8_t) (i - n);
- memcpy(&pkt.data[n + 1], name->ptr + n, i - n);
- n = i + 1;
- }
- if (i >= name->len) break;
+#ifdef MG_ENABLE_LINES
+#line 1 "src/device_imxrt.c"
+#endif
+
+
+
+#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060
+
+struct mg_flexspi_lut_seq {
+ uint8_t seqNum;
+ uint8_t seqId;
+ uint16_t reserved;
+};
+
+struct mg_flexspi_mem_config {
+ uint32_t tag;
+ uint32_t version;
+ uint32_t reserved0;
+ uint8_t readSampleClkSrc;
+ uint8_t csHoldTime;
+ uint8_t csSetupTime;
+ uint8_t columnAddressWidth;
+ uint8_t deviceModeCfgEnable;
+ uint8_t deviceModeType;
+ uint16_t waitTimeCfgCommands;
+ struct mg_flexspi_lut_seq deviceModeSeq;
+ uint32_t deviceModeArg;
+ uint8_t configCmdEnable;
+ uint8_t configModeType[3];
+ struct mg_flexspi_lut_seq configCmdSeqs[3];
+ uint32_t reserved1;
+ uint32_t configCmdArgs[3];
+ uint32_t reserved2;
+ uint32_t controllerMiscOption;
+ uint8_t deviceType;
+ uint8_t sflashPadType;
+ uint8_t serialClkFreq;
+ uint8_t lutCustomSeqEnable;
+ uint32_t reserved3[2];
+ uint32_t sflashA1Size;
+ uint32_t sflashA2Size;
+ uint32_t sflashB1Size;
+ uint32_t sflashB2Size;
+ uint32_t csPadSettingOverride;
+ uint32_t sclkPadSettingOverride;
+ uint32_t dataPadSettingOverride;
+ uint32_t dqsPadSettingOverride;
+ uint32_t timeoutInMs;
+ uint32_t commandInterval;
+ uint16_t dataValidTime[2];
+ uint16_t busyOffset;
+ uint16_t busyBitPolarity;
+ uint32_t lookupTable[64];
+ struct mg_flexspi_lut_seq lutCustomSeq[12];
+ uint32_t reserved4[4];
+};
+
+struct mg_flexspi_nor_config {
+ struct mg_flexspi_mem_config memConfig;
+ uint32_t pageSize;
+ uint32_t sectorSize;
+ uint8_t ipcmdSerialClkFreq;
+ uint8_t isUniformBlockSize;
+ uint8_t reserved0[2];
+ uint8_t serialNorType;
+ uint8_t needExitNoCmdMode;
+ uint8_t halfClkForNonReadCmd;
+ uint8_t needRestoreNoCmdMode;
+ uint32_t blockSize;
+ uint32_t reserve2[11];
+};
+
+/* FLEXSPI memory config block related defintions */
+#define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian
+#define MG_FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0
+
+#define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
+ (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | MG_FLEXSPI_LUT_OPCODE0(cmd0) | \
+ MG_FLEXSPI_LUT_OPERAND1(op1) | MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1))
+
+#define MG_CMD_SDR 0x01
+#define MG_CMD_DDR 0x21
+#define MG_DUMMY_SDR 0x0C
+#define MG_DUMMY_DDR 0x2C
+#define MG_RADDR_SDR 0x02
+#define MG_RADDR_DDR 0x22
+#define MG_READ_SDR 0x09
+#define MG_READ_DDR 0x29
+#define MG_WRITE_SDR 0x08
+#define MG_WRITE_DDR 0x28
+#define MG_STOP 0
+
+#define MG_FLEXSPI_1PAD 0
+#define MG_FLEXSPI_2PAD 1
+#define MG_FLEXSPI_4PAD 2
+#define MG_FLEXSPI_8PAD 3
+
+#define MG_FLEXSPI_QSPI_LUT \
+ { \
+ [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, MG_RADDR_SDR, MG_FLEXSPI_4PAD, \
+ 0x18), \
+ [1] = MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, MG_READ_SDR, MG_FLEXSPI_4PAD, \
+ 0x04), \
+ [4 * 1 + 0] = \
+ MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), \
+ [4 * 3 + 0] = \
+ MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
+ [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, MG_RADDR_SDR, \
+ MG_FLEXSPI_1PAD, 0x18), \
+ [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xD8, MG_RADDR_SDR, \
+ MG_FLEXSPI_1PAD, 0x18), \
+ [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, MG_RADDR_SDR, \
+ MG_FLEXSPI_1PAD, 0x18), \
+ [4 * 9 + 1] = \
+ MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x04, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
+ [4 * 11 + 0] = \
+ MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
}
- memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
- n += 5;
- if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
- // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
- // n += 6;
- return mg_send(c, &pkt, sizeof(pkt.header) + n);
+
+#define MG_FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU)
+#define MG_FLEXSPI_LUT_NUM_PADS0(x) (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U)
+#define MG_FLEXSPI_LUT_OPCODE0(x) (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U)
+#define MG_FLEXSPI_LUT_OPERAND1(x) (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U)
+#define MG_FLEXSPI_LUT_NUM_PADS1(x) (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U)
+#define MG_FLEXSPI_LUT_OPCODE1(x) (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U)
+
+#define FLEXSPI_NOR_INSTANCE 0
+
+#if MG_DEVICE == MG_DEVICE_RT1020
+struct mg_flexspi_nor_driver_interface {
+ uint32_t version;
+ int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
+ int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t dst_addr,
+ const uint32_t *src);
+ uint32_t reserved;
+ int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t start,
+ uint32_t lengthInBytes);
+ uint32_t reserved2;
+ int (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase,
+ uint32_t seqNumber);
+ int (*xfer)(uint32_t instance, char *xfer);
+ void (*clear_cache)(uint32_t instance);
+};
+#elif MG_DEVICE == MG_DEVICE_RT1060
+struct mg_flexspi_nor_driver_interface {
+ uint32_t version;
+ int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
+ int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t dst_addr,
+ const uint32_t *src);
+ int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config);
+ int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t start,
+ uint32_t lengthInBytes);
+ int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t *dst, uint32_t addr,
+ uint32_t lengthInBytes);
+ void (*clear_cache)(uint32_t instance);
+ int (*xfer)(uint32_t instance, char *xfer);
+ int (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase,
+ uint32_t seqNumber);
+ int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t *option);
+};
+#endif
+
+#define flexspi_nor (*((struct mg_flexspi_nor_driver_interface**) \
+ (*(uint32_t*)0x0020001c + 16)))
+
+static bool s_flash_irq_disabled;
+
+MG_IRAM void *mg_flash_start(void) {
+ return (void *) 0x60000000;
+}
+MG_IRAM size_t mg_flash_size(void) {
+ return 8 * 1024 * 1024;
+}
+MG_IRAM size_t mg_flash_sector_size(void) {
+ return 4 * 1024; // 4k
+}
+MG_IRAM size_t mg_flash_write_align(void) {
+ return 256;
+}
+MG_IRAM int mg_flash_bank(void) {
+ return 0;
}
-static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
- struct mg_dns *dnsc, bool ipv6) {
- struct dns_data *d = NULL;
- if (dnsc->url == NULL) {
- mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
- } else if (dnsc->c == NULL) {
- dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
- if (dnsc->c != NULL) {
- dnsc->c->pfn = dns_cb;
- // dnsc->c->is_hexdumping = 1;
- }
+MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
+ char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
+ volatile char *p = (char *) dst;
+ return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
+}
+
+// Note: the get_config function below works both for RT1020 and 1060
+#if MG_DEVICE == MG_DEVICE_RT1020
+MG_IRAM static int flexspi_nor_get_config(struct mg_flexspi_nor_config *config) {
+ struct mg_flexspi_nor_config default_config = {
+ .memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG,
+ .version = MG_FLEXSPI_CFG_BLK_VERSION,
+ .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad
+ .csHoldTime = 3,
+ .csSetupTime = 3,
+ .controllerMiscOption = MG_BIT(4),
+ .deviceType = 1, // serial NOR
+ .sflashPadType = 4,
+ .serialClkFreq = 7, // 133MHz
+ .sflashA1Size = 8 * 1024 * 1024,
+ .lookupTable = MG_FLEXSPI_QSPI_LUT},
+ .pageSize = 256,
+ .sectorSize = 4 * 1024,
+ .ipcmdSerialClkFreq = 1,
+ .blockSize = 64 * 1024,
+ .isUniformBlockSize = false};
+
+ *config = default_config;
+ return 0;
+}
+#else
+MG_IRAM static int flexspi_nor_get_config(struct mg_flexspi_nor_config *config) {
+ uint32_t options[] = {0xc0000000, 0x00};
+
+ MG_ARM_DISABLE_IRQ();
+ uint32_t status =
+ flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options);
+ if (!s_flash_irq_disabled) {
+ MG_ARM_ENABLE_IRQ();
}
- if (dnsc->c == NULL) {
- mg_error(c, "resolver");
- } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
- mg_error(c, "resolve OOM");
- } else {
- struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
- d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
- d->next = (struct dns_data *) c->mgr->active_dns_requests;
- c->mgr->active_dns_requests = d;
- d->expire = mg_millis() + (uint64_t) ms;
- d->c = c;
- c->is_resolving = 1;
- MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len,
- name->ptr, &dnsc->url, d->txnid));
- if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) {
- mg_error(dnsc->c, "DNS send");
- }
+ if (status) {
+ MG_ERROR(("Failed to extract flash configuration: status %u", status));
}
+ return status;
}
+#endif
-void mg_resolve(struct mg_connection *c, const char *url) {
- struct mg_str host = mg_url_host(url);
- c->rem.port = mg_htons(mg_url_port(url));
- if (mg_aton(host, &c->rem)) {
- // host is an IP address, do not fire name resolution
- mg_connect_resolved(c);
- } else {
- // host is not an IP, send DNS resolution request
- struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
- mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
+MG_IRAM bool mg_flash_erase(void *addr) {
+ struct mg_flexspi_nor_config config;
+ if (flexspi_nor_get_config(&config) != 0) {
+ return false;
+ }
+ if (flash_page_start(addr) == false) {
+ MG_ERROR(("%p is not on a sector boundary", addr));
+ return false;
+ }
+
+ void *dst = (void *)((char *) addr - (char *) mg_flash_start());
+
+ // Note: Interrupts must be disabled before any call to the ROM API on RT1020
+ // and 1060
+ MG_ARM_DISABLE_IRQ();
+ bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) dst,
+ mg_flash_sector_size()) == 0);
+ if (!s_flash_irq_disabled) {
+ MG_ARM_ENABLE_IRQ(); // Reenable them after the call
}
+ MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail"));
+ return ok;
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/event.c"
-#endif
+MG_IRAM bool mg_flash_swap_bank(void) {
+ return true;
+}
+
+static inline void spin(volatile uint32_t count) {
+ while (count--) (void) 0;
+}
+
+static inline void flash_wait(void) {
+ while ((*((volatile uint32_t *)(0x402A8000 + 0xE0)) & MG_BIT(1)) == 0)
+ spin(1);
+}
+MG_IRAM static void *flash_code_location(void) {
+ return (void *) ((char *) mg_flash_start() + 0x2000);
+}
+MG_IRAM bool mg_flash_write(void *addr, const void *buf, size_t len) {
+ struct mg_flexspi_nor_config config;
+ if (flexspi_nor_get_config(&config) != 0) {
+ return false;
+ }
+ if ((len % mg_flash_write_align()) != 0) {
+ MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align()));
+ return false;
+ }
+ if ((char *) addr < (char *) mg_flash_start()) {
+ MG_ERROR(("Invalid flash write address: %p", addr));
+ return false;
+ }
+ uint32_t *dst = (uint32_t *) addr;
+ uint32_t *src = (uint32_t *) buf;
+ uint32_t *end = (uint32_t *) ((char *) buf + len);
+ bool ok = true;
+
+ // Note: If we overwrite the flash irq section of the image, we must also
+ // make sure interrupts are disabled and are not reenabled until we write
+ // this sector with another irq table.
+ if ((char *) addr == (char *) flash_code_location()) {
+ s_flash_irq_disabled = true;
+ MG_ARM_DISABLE_IRQ();
+ }
-void mg_call(struct mg_connection *c, int ev, void *ev_data) {
- // Run user-defined handler first, in order to give it an ability
- // to intercept processing (e.g. clean input buffer) before the
- // protocol handler kicks in
- if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data);
- if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data);
+ while (ok && src < end) {
+ if (flash_page_start(dst) && mg_flash_erase(dst) == false) {
+ break;
+ }
+ uint32_t status;
+ uint32_t dst_ofs = (uint32_t) dst - (uint32_t) mg_flash_start();
+ if ((char *) buf >= (char *) mg_flash_start()) {
+ // If we copy from FLASH to FLASH, then we first need to copy the source
+ // to RAM
+ size_t tmp_buf_size = mg_flash_write_align() / sizeof(uint32_t);
+ uint32_t tmp[tmp_buf_size];
+
+ for (size_t i = 0; i < tmp_buf_size; i++) {
+ flash_wait();
+ tmp[i] = src[i];
+ }
+ MG_ARM_DISABLE_IRQ();
+ status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config,
+ (uint32_t) dst_ofs, tmp);
+ } else {
+ MG_ARM_DISABLE_IRQ();
+ status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config,
+ (uint32_t) dst_ofs, src);
+ }
+ if (!s_flash_irq_disabled) {
+ MG_ARM_ENABLE_IRQ();
+ }
+ src = (uint32_t *) ((char *) src + mg_flash_write_align());
+ dst = (uint32_t *) ((char *) dst + mg_flash_write_align());
+ if (status != 0) {
+ ok = false;
+ }
+ }
+ MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail"));
+ return ok;
}
-void mg_error(struct mg_connection *c, const char *fmt, ...) {
- char buf[64];
- va_list ap;
- va_start(ap, fmt);
- mg_vsnprintf(buf, sizeof(buf), fmt, &ap);
- va_end(ap);
- MG_ERROR(("%lu %p %s", c->id, c->fd, buf));
- c->is_closing = 1; // Set is_closing before sending MG_EV_CALL
- mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it
+MG_IRAM void mg_device_reset(void) {
+ MG_DEBUG(("Resetting device..."));
+ *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
+#endif
+
#ifdef MG_ENABLE_LINES
-#line 1 "src/fmt.c"
+#line 1 "src/device_stm32h5.c"
#endif
+#if MG_DEVICE == MG_DEVICE_STM32H5
-static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
- struct mg_iobuf *io = (struct mg_iobuf *) param;
- if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2);
- if (io->len + 2 <= io->size) {
- io->buf[io->len++] = (uint8_t) ch;
- io->buf[io->len] = 0;
- } else if (io->len < io->size) {
- io->buf[io->len++] = 0; // Guarantee to 0-terminate
- }
-}
+#define FLASH_BASE 0x40022000 // Base address of the flash controller
+#define FLASH_KEYR (FLASH_BASE + 0x4) // See RM0481 7.11
+#define FLASH_OPTKEYR (FLASH_BASE + 0xc)
+#define FLASH_OPTCR (FLASH_BASE + 0x1c)
+#define FLASH_NSSR (FLASH_BASE + 0x20)
+#define FLASH_NSCR (FLASH_BASE + 0x28)
+#define FLASH_NSCCR (FLASH_BASE + 0x30)
+#define FLASH_OPTSR_CUR (FLASH_BASE + 0x50)
+#define FLASH_OPTSR_PRG (FLASH_BASE + 0x54)
-static void mg_putchar_iobuf_static(char ch, void *param) {
- mg_pfn_iobuf_private(ch, param, false);
+void *mg_flash_start(void) {
+ return (void *) 0x08000000;
}
-
-void mg_pfn_iobuf(char ch, void *param) {
- mg_pfn_iobuf_private(ch, param, true);
+size_t mg_flash_size(void) {
+ return 2 * 1024 * 1024; // 2Mb
}
-
-size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) {
- struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0};
- size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap);
- if (n < len) buf[n] = '\0';
- return n;
+size_t mg_flash_sector_size(void) {
+ return 8 * 1024; // 8k
}
-
-size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) {
- va_list ap;
- size_t n;
- va_start(ap, fmt);
- n = mg_vsnprintf(buf, len, fmt, &ap);
- va_end(ap);
- return n;
+size_t mg_flash_write_align(void) {
+ return 16; // 128 bit
}
-
-char *mg_vmprintf(const char *fmt, va_list *ap) {
- struct mg_iobuf io = {0, 0, 0, 256};
- mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap);
- return (char *) io.buf;
+int mg_flash_bank(void) {
+ return MG_REG(FLASH_OPTCR) & MG_BIT(31) ? 2 : 1;
}
-char *mg_mprintf(const char *fmt, ...) {
- char *s;
- va_list ap;
- va_start(ap, fmt);
- s = mg_vmprintf(fmt, &ap);
- va_end(ap);
- return s;
+static void flash_unlock(void) {
+ static bool unlocked = false;
+ if (unlocked == false) {
+ MG_REG(FLASH_KEYR) = 0x45670123;
+ MG_REG(FLASH_KEYR) = 0Xcdef89ab;
+ MG_REG(FLASH_OPTKEYR) = 0x08192a3b;
+ MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
+ unlocked = true;
+ }
}
-size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) {
- size_t len = 0;
- va_list ap;
- va_start(ap, fmt);
- len = mg_vxprintf(out, ptr, fmt, &ap);
- va_end(ap);
- return len;
+static int flash_page_start(volatile uint32_t *dst) {
+ char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
+ volatile char *p = (char *) dst;
+ return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
}
-static bool is_digit(int c) {
- return c >= '0' && c <= '9';
+static bool flash_is_err(void) {
+ return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9
}
-static int addexp(char *buf, int e, int sign) {
- int n = 0;
- buf[n++] = 'e';
- buf[n++] = (char) sign;
- if (e > 400) return 0;
- if (e < 10) buf[n++] = '0';
- if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100);
- if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10);
- buf[n++] = (char) (e + '0');
- return n;
+static void flash_wait(void) {
+ while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) &&
+ (MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) {
+ (void) 0;
+ }
}
-static int xisinf(double x) {
- union {
- double f;
- uint64_t u;
- } ieee754 = {x};
- return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
- ((unsigned) ieee754.u == 0);
+static void flash_clear_err(void) {
+ flash_wait(); // Wait until ready
+ MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors
}
-static int xisnan(double x) {
- union {
- double f;
- uint64_t u;
- } ieee754 = {x};
- return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) +
- ((unsigned) ieee754.u != 0) >
- 0x7ff00000;
+static bool flash_bank_is_swapped(void) {
+ return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8
}
-static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width) {
- char buf[40];
- int i, s = 0, n = 0, e = 0;
- double t, mul, saved;
- if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0");
- if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf");
- if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan");
- if (d < 0.0) d = -d, buf[s++] = '-';
-
- // Round
- saved = d;
- mul = 1.0;
- while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
- while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
- for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
- d += t;
- // Calculate exponent, and 'mul' for scientific representation
- mul = 1.0;
- while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
- while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
- // printf(" --> %g %d %g %g\n", saved, e, t, mul);
-
- if (e >= width) {
- n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width);
- // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
- n += addexp(buf + s + n, e, '+');
- return mg_snprintf(dst, dstlen, "%.*s", n, buf);
- } else if (e <= -width) {
- n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width);
- // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf);
- n += addexp(buf + s + n, -e, '-');
- return mg_snprintf(dst, dstlen, "%.*s", n, buf);
+bool mg_flash_erase(void *location) {
+ bool ok = false;
+ if (flash_page_start(location) == false) {
+ MG_ERROR(("%p is not on a sector boundary"));
} else {
- for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {
- int ch = (int) (d / t);
- if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0');
- d -= ch * t;
- t /= 10.0;
- }
- // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
- if (n == 0) buf[s++] = '0';
- while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
- if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
- // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
- for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
- int ch = (int) (d / t);
- buf[s + n++] = (char) (ch + '0');
- d -= ch * t;
- t /= 10.0;
+ uintptr_t diff = (char *) location - (char *) mg_flash_start();
+ uint32_t sector = diff / mg_flash_sector_size();
+ uint32_t saved_cr = MG_REG(FLASH_NSCR); // Save CR value
+ flash_unlock();
+ flash_clear_err();
+ MG_REG(FLASH_NSCR) = 0;
+ if ((sector < 128 && flash_bank_is_swapped()) ||
+ (sector > 127 && !flash_bank_is_swapped())) {
+ MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL
}
+ if (sector > 127) sector -= 128;
+ MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num
+ MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing
+ flash_wait();
+ ok = !flash_is_err();
+ MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location,
+ ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR)));
+ // mg_hexdump(location, 32);
+ MG_REG(FLASH_NSCR) = saved_cr; // Restore saved CR
}
- while (n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeros
- if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
- n += s;
- if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1;
- buf[n] = '\0';
- return mg_snprintf(dst, dstlen, "%s", buf);
+ return ok;
}
-static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) {
- const char *letters = "0123456789abcdef";
- uint64_t v = (uint64_t) val;
- size_t s = 0, n, i;
- if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val);
- // This loop prints a number in reverse order. I guess this is because we
- // write numbers from right to left: least significant digit comes last.
- // Maybe because we use Arabic numbers, and Arabs write RTL?
- if (is_hex) {
- for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15];
- } else {
- for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10];
+bool mg_flash_swap_bank(void) {
+ uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31);
+ flash_unlock();
+ flash_clear_err();
+ // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG);
+ MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired);
+ // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG);
+ MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART
+ while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0;
+ return true;
+}
+
+bool mg_flash_write(void *addr, const void *buf, size_t len) {
+ if ((len % mg_flash_write_align()) != 0) {
+ MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align()));
+ return false;
}
- // Reverse a string
- for (i = 0; i < n / 2; i++) {
- char t = buf[s + i];
- buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
+ uint32_t *dst = (uint32_t *) addr;
+ uint32_t *src = (uint32_t *) buf;
+ uint32_t *end = (uint32_t *) ((char *) buf + len);
+ bool ok = true;
+ flash_unlock();
+ flash_clear_err();
+ MG_ARM_DISABLE_IRQ();
+ // MG_DEBUG(("Starting flash write %lu bytes @ %p", len, addr));
+ MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag
+ while (ok && src < end) {
+ if (flash_page_start(dst) && mg_flash_erase(dst) == false) break;
+ *(volatile uint32_t *) dst++ = *src++;
+ flash_wait();
+ if (flash_is_err()) ok = false;
}
- if (val == 0) buf[n++] = '0'; // Handle special case
- return n + s;
+ MG_ARM_ENABLE_IRQ();
+ MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
+ flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR),
+ MG_REG(FLASH_NSSR)));
+ MG_REG(FLASH_NSCR) = 0; // Clear flags
+ return ok;
}
-static size_t scpy(void (*out)(char, void *), void *ptr, char *buf,
- size_t len) {
- size_t i = 0;
- while (i < len && buf[i] != '\0') out(buf[i++], ptr);
- return i;
+void mg_device_reset(void) {
+ // SCB->AIRCR = ((0x5fa << SCB_AIRCR_VECTKEY_Pos)|SCB_AIRCR_SYSRESETREQ_Msk);
+ *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
+#endif
-static char mg_esc(int c, bool esc) {
- const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
- for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
- if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
+#ifdef MG_ENABLE_LINES
+#line 1 "src/device_stm32h7.c"
+#endif
+
+
+
+#if MG_DEVICE == MG_DEVICE_STM32H7
+
+#define FLASH_BASE1 0x52002000 // Base address for bank1
+#define FLASH_BASE2 0x52002100 // Base address for bank2
+#define FLASH_KEYR 0x04 // See RM0433 4.9.2
+#define FLASH_OPTKEYR 0x08
+#define FLASH_OPTCR 0x18
+#define FLASH_SR 0x10
+#define FLASH_CR 0x0c
+#define FLASH_CCR 0x14
+#define FLASH_OPTSR_CUR 0x1c
+#define FLASH_OPTSR_PRG 0x20
+#define FLASH_SIZE_REG 0x1ff1e880
+
+MG_IRAM void *mg_flash_start(void) {
+ return (void *) 0x08000000;
+}
+MG_IRAM size_t mg_flash_size(void) {
+ return MG_REG(FLASH_SIZE_REG) * 1024;
+}
+MG_IRAM size_t mg_flash_sector_size(void) {
+ return 128 * 1024; // 128k
+}
+MG_IRAM size_t mg_flash_write_align(void) {
+ return 32; // 256 bit
+}
+MG_IRAM int mg_flash_bank(void) {
+ if (mg_flash_size() < 2 * 1024 * 1024) return 0; // No dual bank support
+ return MG_REG(FLASH_BASE1 + FLASH_OPTCR) & MG_BIT(31) ? 2 : 1;
+}
+
+MG_IRAM static void flash_unlock(void) {
+ static bool unlocked = false;
+ if (unlocked == false) {
+ MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123;
+ MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab;
+ if (mg_flash_bank() > 0) {
+ MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123;
+ MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab;
+ }
+ MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared"
+ MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once
+ unlocked = true;
}
- return 0;
}
-static char mg_escape(int c) {
- return mg_esc(c, true);
+MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
+ char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
+ volatile char *p = (char *) dst;
+ return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
}
-static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf,
- size_t len) {
- size_t i = 0, extra = 0;
- for (i = 0; i < len && buf[i] != '\0'; i++) {
- char c = mg_escape(buf[i]);
- if (c) {
- out('\\', ptr), out(c, ptr), extra++;
- } else {
- out(buf[i], ptr);
- }
- }
- return i + extra;
+MG_IRAM static bool flash_is_err(uint32_t bank) {
+ return MG_REG(bank + FLASH_SR) & ((MG_BIT(11) - 1) << 17); // RM0433 4.9.5
}
-static size_t Qcpy(void (*out)(char, void *), void *ptr, char *buf,
- size_t len) {
- size_t n = 2;
- out('"', ptr);
- n += qcpy(out, ptr, buf, len);
- out('"', ptr);
- return n;
+MG_IRAM static void flash_wait(uint32_t bank) {
+ while (MG_REG(bank + FLASH_SR) & (MG_BIT(0) | MG_BIT(2))) (void) 0;
}
-static size_t bcpy(void (*out)(char, void *), void *ptr, uint8_t *buf,
- size_t len) {
- size_t i, n = 0;
- const char *t =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- out('"', ptr), n++;
- for (i = 0; i < len; i += 3) {
- uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0,
- c3 = i + 2 < len ? buf[i + 2] : 0;
- char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='};
- if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
- if (i + 2 < len) tmp[3] = t[c3 & 63];
- n += scpy(out, ptr, tmp, sizeof(tmp));
- }
- out('"', ptr), n++;
- return n;
+MG_IRAM static void flash_clear_err(uint32_t bank) {
+ flash_wait(bank); // Wait until ready
+ MG_REG(bank + FLASH_CCR) = ((MG_BIT(11) - 1) << 16U); // Clear all errors
}
-size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
- va_list *ap) {
- size_t i = 0, n = 0;
- while (fmt[i] != '\0') {
- if (fmt[i] == '%') {
- size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */;
- char pad = ' ', minus = 0, c = fmt[++i];
- if (c == '#') x++, c = fmt[++i];
- if (c == '-') minus++, c = fmt[++i];
- if (c == '0') pad = '0', c = fmt[++i];
- while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i];
- if (c == '.') {
- c = fmt[++i];
- if (c == '*') {
- pr = (size_t) va_arg(*ap, int);
- c = fmt[++i];
- } else {
- pr = 0;
- while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i];
- }
- }
- while (c == 'h') c = fmt[++i]; // Treat h and hh as int
- if (c == 'l') {
- is_long++, c = fmt[++i];
- if (c == 'l') is_long++, c = fmt[++i];
- }
- if (c == 'p') x = 1, is_long = 1;
- if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
- c == 'g' || c == 'f') {
- bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p');
- char tmp[40];
- size_t xl = x ? 2 : 0;
- if (c == 'g' || c == 'f') {
- double v = va_arg(*ap, double);
- if (pr == ~0U) pr = 6;
- k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr);
- } else if (is_long == 2) {
- int64_t v = va_arg(*ap, int64_t);
- k = mg_lld(tmp, v, s, h);
- } else if (is_long == 1) {
- long v = va_arg(*ap, long);
- k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h);
- } else {
- int v = va_arg(*ap, int);
- k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h);
- }
- for (j = 0; j < xl && w > 0; j++) w--;
- for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- n += scpy(out, param, (char *) "0x", xl);
- for (j = 0; pad == '0' && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- n += scpy(out, param, tmp, k);
- for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- } else if (c == 'M') {
- mg_pm_t f = va_arg(*ap, mg_pm_t);
- n += f(out, param, ap);
- } else if (c == 'c') {
- int ch = va_arg(*ap, int);
- out((char) ch, param);
- n++;
- } else if (c == 'H') {
- // Print hex-encoded double-quoted string
- size_t bl = (size_t) va_arg(*ap, int);
- uint8_t *p = va_arg(*ap, uint8_t *), dquote = '"';
- const char *hex = "0123456789abcdef";
- n += scpy(out, param, (char *) &dquote, 1);
- for (j = 0; j < bl; j++) {
- n += scpy(out, param, (char *) &hex[(p[j] >> 4) & 15], 1);
- n += scpy(out, param, (char *) &hex[p[j] & 15], 1);
- }
- n += scpy(out, param, (char *) &dquote, 1);
- } else if (c == 'I') {
- // Print IPv4 or IPv6 address
- size_t len = (size_t) va_arg(*ap, int); // Length 16 means IPv6 address
- uint8_t *buf = va_arg(*ap, uint8_t *); // Pointer to the IP address
- if (len == 6) {
- uint16_t *p = (uint16_t *) buf;
- n += mg_xprintf(out, param, "%x:%x:%x:%x:%x:%x:%x:%x", mg_htons(p[0]),
- mg_htons(p[1]), mg_htons(p[2]), mg_htons(p[3]),
- mg_htons(p[4]), mg_htons(p[5]), mg_htons(p[6]),
- mg_htons(p[7]));
- } else {
- n += mg_xprintf(out, param, "%d.%d.%d.%d", (int) buf[0], (int) buf[1],
- (int) buf[2], (int) buf[3]);
- }
- } else if (c == 'A') {
- // Print hardware addresses (currently Ethernet MAC)
- uint8_t *buf = va_arg(*ap, uint8_t *); // Pointer to the hw address
- n += mg_xprintf(out, param, "%02x:%02x:%02x:%02x:%02x:%02x",
- (int) buf[0], (int) buf[1], (int) buf[2], (int) buf[3],
- (int) buf[4], (int) buf[5]);
- } else if (c == 'V') {
- // Print base64-encoded double-quoted string
- size_t len = (size_t) va_arg(*ap, int);
- uint8_t *buf = va_arg(*ap, uint8_t *);
- n += bcpy(out, param, buf, len);
- } else if (c == 's' || c == 'Q' || c == 'q') {
- char *p = va_arg(*ap, char *);
- size_t (*f)(void (*)(char, void *), void *, char *, size_t) = scpy;
- if (c == 'Q') f = Qcpy;
- if (c == 'q') f = qcpy;
- if (pr == ~0U) pr = p == NULL ? 0 : strlen(p);
- for (j = 0; !minus && pr < w && j + pr < w; j++)
- n += f(out, param, &pad, 1);
- n += f(out, param, p, pr);
- for (j = 0; minus && pr < w && j + pr < w; j++)
- n += f(out, param, &pad, 1);
- } else if (c == '%') {
- out('%', param);
- n++;
- } else {
- out('%', param);
- out(c, param);
- n += 2;
- }
- i++;
- } else {
- out(fmt[i], param), n++, i++;
- }
- }
- return n;
+MG_IRAM static bool flash_bank_is_swapped(uint32_t bank) {
+ return MG_REG(bank + FLASH_OPTCR) & MG_BIT(31); // RM0433 4.9.7
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/fs.c"
-#endif
-
-
-
-struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) {
- struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd));
- if (fd != NULL) {
- fd->fd = fs->op(path, flags);
- fd->fs = fs;
- if (fd->fd == NULL) {
- free(fd);
- fd = NULL;
- }
- }
- return fd;
+// Figure out flash bank based on the address
+MG_IRAM static uint32_t flash_bank(void *addr) {
+ size_t ofs = (char *) addr - (char *) mg_flash_start();
+ if (mg_flash_bank() == 0) return FLASH_BASE1;
+ return ofs < mg_flash_size() / 2 ? FLASH_BASE1 : FLASH_BASE2;
}
-void mg_fs_close(struct mg_fd *fd) {
- if (fd != NULL) {
- fd->fs->cl(fd->fd);
- free(fd);
+MG_IRAM bool mg_flash_erase(void *addr) {
+ bool ok = false;
+ if (flash_page_start(addr) == false) {
+ MG_ERROR(("%p is not on a sector boundary", addr));
+ } else {
+ uintptr_t diff = (char *) addr - (char *) mg_flash_start();
+ uint32_t sector = diff / mg_flash_sector_size();
+ uint32_t bank = flash_bank(addr);
+ uint32_t saved_cr = MG_REG(bank + FLASH_CR); // Save CR value
+
+ flash_unlock();
+ if (sector > 7) sector -= 8;
+
+ flash_clear_err(bank);
+ MG_REG(bank + FLASH_CR) = MG_BIT(5); // 32-bit write parallelism
+ MG_REG(bank + FLASH_CR) |= (sector & 7U) << 8U; // Sector to erase
+ MG_REG(bank + FLASH_CR) |= MG_BIT(2); // Sector erase bit
+ MG_REG(bank + FLASH_CR) |= MG_BIT(7); // Start erasing
+ ok = !flash_is_err(bank);
+ MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr,
+ ok ? "ok" : "fail", MG_REG(bank + FLASH_CR),
+ MG_REG(bank + FLASH_SR)));
+ MG_REG(bank + FLASH_CR) = saved_cr; // Restore CR
}
+ return ok;
}
-char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep) {
- struct mg_fd *fd;
- char *data = NULL;
- size_t size = 0;
- fs->st(path, &size, NULL);
- if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) {
- data = (char *) calloc(1, size + 1);
- if (data != NULL) {
- if (fs->rd(fd->fd, data, size) != size) {
- free(data);
- data = NULL;
- } else {
- data[size] = '\0';
- if (sizep != NULL) *sizep = size;
- }
- }
- mg_fs_close(fd);
- }
- return data;
+MG_IRAM bool mg_flash_swap_bank(void) {
+ if (mg_flash_bank() == 0) return true;
+ uint32_t bank = FLASH_BASE1;
+ uint32_t desired = flash_bank_is_swapped(bank) ? 0 : MG_BIT(31);
+ flash_unlock();
+ flash_clear_err(bank);
+ // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG);
+ MG_SET_BITS(MG_REG(bank + FLASH_OPTSR_PRG), MG_BIT(31), desired);
+ // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG);
+ MG_REG(bank + FLASH_OPTCR) |= MG_BIT(1); // OPTSTART
+ while ((MG_REG(bank + FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0;
+ return true;
}
-bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
- size_t len) {
- bool result = false;
- struct mg_fd *fd;
- char tmp[MG_PATH_MAX];
- mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand());
- if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) {
- result = fs->wr(fd->fd, buf, len) == len;
- mg_fs_close(fd);
- if (result) {
- fs->rm(path);
- fs->mv(tmp, path);
- } else {
- fs->rm(tmp);
- }
+MG_IRAM bool mg_flash_write(void *addr, const void *buf, size_t len) {
+ if ((len % mg_flash_write_align()) != 0) {
+ MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align()));
+ return false;
}
- return result;
+ uint32_t bank = flash_bank(addr);
+ uint32_t *dst = (uint32_t *) addr;
+ uint32_t *src = (uint32_t *) buf;
+ uint32_t *end = (uint32_t *) ((char *) buf + len);
+ bool ok = true;
+ flash_unlock();
+ flash_clear_err(bank);
+ MG_REG(bank + FLASH_CR) = MG_BIT(1); // Set programming flag
+ MG_REG(bank + FLASH_CR) |= MG_BIT(5); // 32-bit write parallelism
+ MG_DEBUG(("Writing flash @ %p, %lu bytes", addr, len));
+ MG_ARM_DISABLE_IRQ();
+ while (ok && src < end) {
+ if (flash_page_start(dst) && mg_flash_erase(dst) == false) break;
+ *(volatile uint32_t *) dst++ = *src++;
+ flash_wait(bank);
+ if (flash_is_err(bank)) ok = false;
+ }
+ MG_ARM_ENABLE_IRQ();
+ MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
+ ok ? "ok" : "fail", MG_REG(bank + FLASH_CR),
+ MG_REG(bank + FLASH_SR)));
+ MG_REG(bank + FLASH_CR) &= ~MG_BIT(1); // Clear programming flag
+ return ok;
}
-bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
- va_list ap;
- char *data;
- bool result = false;
- va_start(ap, fmt);
- data = mg_vmprintf(fmt, &ap);
- va_end(ap);
- result = mg_file_write(fs, path, data, strlen(data));
- free(data);
- return result;
+MG_IRAM void mg_device_reset(void) {
+ // SCB->AIRCR = ((0x5fa << SCB_AIRCR_VECTKEY_Pos)|SCB_AIRCR_SYSRESETREQ_Msk);
+ *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
+#endif
#ifdef MG_ENABLE_LINES
-#line 1 "src/fs_fat.c"
+#line 1 "src/dns.c"
#endif
-#if MG_ENABLE_FATFS
-#include <ff.h>
-static int mg_days_from_epoch(int y, int m, int d) {
- y -= m <= 2;
- int era = y / 400;
- int yoe = y - era * 400;
- int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
- int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
- return era * 146097 + doe - 719468;
-}
-static time_t mg_timegm(const struct tm *t) {
- int year = t->tm_year + 1900;
- int month = t->tm_mon; // 0-11
- if (month > 11) {
- year += month / 12;
- month %= 12;
- } else if (month < 0) {
- int years_diff = (11 - month) / 12;
- year -= years_diff;
- month += 12 * years_diff;
- }
- int x = mg_days_from_epoch(year, month + 1, t->tm_mday);
- return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec;
-}
-static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- tm.tm_sec = (ftime << 1) & 0x3e;
- tm.tm_min = ((ftime >> 5) & 0x3f);
- tm.tm_hour = ((ftime >> 11) & 0x1f);
- tm.tm_mday = (fdate & 0x1f);
- tm.tm_mon = ((fdate >> 5) & 0x0f) - 1;
- tm.tm_year = ((fdate >> 9) & 0x7f) + 80;
- return mg_timegm(&tm);
-}
-static int ff_stat(const char *path, size_t *size, time_t *mtime) {
- FILINFO fi;
- if (path[0] == '\0') {
- if (size) *size = 0;
- if (mtime) *mtime = 0;
- return MG_FS_DIR;
- } else if (f_stat(path, &fi) == 0) {
- if (size) *size = (size_t) fi.fsize;
- if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime);
- return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
- } else {
- return 0;
- }
-}
-static void ff_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
- DIR d;
- FILINFO fi;
- if (f_opendir(&d, dir) == FR_OK) {
- while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
- if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
- fn(fi.fname, userdata);
- }
- f_closedir(&d);
- }
-}
+struct dns_data {
+ struct dns_data *next;
+ struct mg_connection *c;
+ uint64_t expire;
+ uint16_t txnid;
+};
-static void *ff_open(const char *path, int flags) {
- FIL f;
- unsigned char mode = FA_READ;
- if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
- if (f_open(&f, path, mode) == 0) {
- FIL *fp = calloc(1, sizeof(*fp));
- memcpy(fp, &f, sizeof(*fp));
- return fp;
- } else {
- return NULL;
- }
+static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
+ struct mg_dns *, bool);
+
+static void mg_dns_free(struct dns_data **head, struct dns_data *d) {
+ LIST_DELETE(struct dns_data, head, d);
+ free(d);
}
-static void ff_close(void *fp) {
- if (fp != NULL) {
- f_close((FIL *) fp);
- free(fp);
+void mg_resolve_cancel(struct mg_connection *c) {
+ struct dns_data *tmp, *d;
+ struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
+ for (d = *head; d != NULL; d = tmp) {
+ tmp = d->next;
+ if (d->c == c) mg_dns_free(head, d);
}
}
-static size_t ff_read(void *fp, void *buf, size_t len) {
- UINT n = 0, misalign = ((size_t) buf) & 3;
- if (misalign) {
- char aligned[4];
- f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n);
- memcpy(buf, aligned, n);
- } else {
- f_read((FIL *) fp, buf, len, &n);
+static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
+ char *to, size_t tolen, size_t j,
+ int depth) {
+ size_t i = 0;
+ if (tolen > 0 && depth == 0) to[0] = '\0';
+ if (depth > 5) return 0;
+ // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1]));
+ while (ofs + i + 1 < len) {
+ size_t n = s[ofs + i];
+ if (n == 0) {
+ i++;
+ break;
+ }
+ if (n & 0xc0) {
+ size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
+ // MG_INFO(("PTR %lx", (unsigned long) ptr));
+ if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
+ mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0)
+ return 0;
+ i += 2;
+ break;
+ }
+ if (ofs + i + n + 1 >= len) return 0;
+ if (j > 0) {
+ if (j < tolen) to[j] = '.';
+ j++;
+ }
+ if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
+ j += n;
+ i += n + 1;
+ if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
+ // MG_INFO(("--> [%s]", to));
}
- return n;
+ if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
+ return i;
}
-static size_t ff_write(void *fp, const void *buf, size_t len) {
- UINT n = 0;
- return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0;
+static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
+ char *dst, size_t dstlen) {
+ return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0);
}
-static size_t ff_seek(void *fp, size_t offset) {
- f_lseek((FIL *) fp, offset);
- return offset;
+size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
+ bool is_question, struct mg_dns_rr *rr) {
+ const uint8_t *s = buf + ofs, *e = &buf[len];
+
+ memset(rr, 0, sizeof(*rr));
+ if (len < sizeof(struct mg_dns_header)) return 0; // Too small
+ if (len > 512) return 0; // Too large, we don't expect that
+ if (s >= e) return 0; // Overflow
+
+ if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
+ return 0;
+ s += rr->nlen + 4;
+ if (s > e) return 0;
+ rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
+ rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
+ if (is_question) return (size_t) (rr->nlen + 4);
+
+ s += 6;
+ if (s > e) return 0;
+ rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
+ if (s + rr->alen > e) return 0;
+ return (size_t) (rr->nlen + rr->alen + 10);
}
-static bool ff_rename(const char *from, const char *to) {
- return f_rename(from, to) == FR_OK;
+bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
+ const struct mg_dns_header *h = (struct mg_dns_header *) buf;
+ struct mg_dns_rr rr;
+ size_t i, n, num_answers, ofs = sizeof(*h);
+ memset(dm, 0, sizeof(*dm));
+
+ if (len < sizeof(*h)) return 0; // Too small, headers dont fit
+ if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
+ num_answers = mg_ntohs(h->num_answers);
+ if (num_answers > 10) {
+ MG_DEBUG(("Got %u answers, ignoring beyond 10th one", num_answers));
+ num_answers = 10; // Sanity cap
+ }
+ dm->txnid = mg_ntohs(h->txnid);
+
+ for (i = 0; i < mg_ntohs(h->num_questions); i++) {
+ if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
+ // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass));
+ ofs += n;
+ }
+ for (i = 0; i < num_answers; i++) {
+ if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
+ // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass,
+ // dm->name));
+ mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
+ ofs += n;
+
+ if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
+ dm->addr.is_ip6 = false;
+ memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
+ dm->resolved = true;
+ break; // Return success
+ } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
+ dm->addr.is_ip6 = true;
+ memcpy(&dm->addr.ip, &buf[ofs - 16], 16);
+ dm->resolved = true;
+ break; // Return success
+ }
+ }
+ return true;
}
-static bool ff_remove(const char *path) {
- return f_unlink(path) == FR_OK;
+static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
+ struct dns_data *d, *tmp;
+ struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
+ if (ev == MG_EV_POLL) {
+ uint64_t now = *(uint64_t *) ev_data;
+ for (d = *head; d != NULL; d = tmp) {
+ tmp = d->next;
+ // MG_DEBUG ("%lu %lu dns poll", d->expire, now));
+ if (now > d->expire) mg_error(d->c, "DNS timeout");
+ }
+ } else if (ev == MG_EV_READ) {
+ struct mg_dns_message dm;
+ int resolved = 0;
+ if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
+ MG_ERROR(("Unexpected DNS response:"));
+ mg_hexdump(c->recv.buf, c->recv.len);
+ } else {
+ // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
+ for (d = *head; d != NULL; d = tmp) {
+ tmp = d->next;
+ // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
+ if (dm.txnid != d->txnid) continue;
+ if (d->c->is_resolving) {
+ if (dm.resolved) {
+ dm.addr.port = d->c->rem.port; // Save port
+ d->c->rem = dm.addr; // Copy resolved address
+ MG_DEBUG(
+ ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem));
+ mg_connect_resolved(d->c);
+#if MG_ENABLE_IPV6
+ } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' &&
+ c->mgr->use_dns6 == false) {
+ struct mg_str x = mg_str(dm.name);
+ mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
+#endif
+ } else {
+ mg_error(d->c, "%s DNS lookup failed", dm.name);
+ }
+ } else {
+ MG_ERROR(("%lu already resolved", d->c->id));
+ }
+ mg_dns_free(head, d);
+ resolved = 1;
+ }
+ }
+ if (!resolved) MG_ERROR(("stray DNS reply"));
+ c->recv.len = 0;
+ } else if (ev == MG_EV_CLOSE) {
+ for (d = *head; d != NULL; d = tmp) {
+ tmp = d->next;
+ mg_error(d->c, "DNS error");
+ mg_dns_free(head, d);
+ }
+ }
}
-static bool ff_mkdir(const char *path) {
- return f_mkdir(path) == FR_OK;
+static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
+ uint16_t txnid, bool ipv6) {
+ struct {
+ struct mg_dns_header header;
+ uint8_t data[256];
+ } pkt;
+ size_t i, n;
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.header.txnid = mg_htons(txnid);
+ pkt.header.flags = mg_htons(0x100);
+ pkt.header.num_questions = mg_htons(1);
+ for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
+ if (name->buf[i] == '.' || i >= name->len) {
+ pkt.data[n] = (uint8_t) (i - n);
+ memcpy(&pkt.data[n + 1], name->buf + n, i - n);
+ n = i + 1;
+ }
+ if (i >= name->len) break;
+ }
+ memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
+ n += 5;
+ if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
+ // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
+ // n += 6;
+ return mg_send(c, &pkt, sizeof(pkt.header) + n);
}
-struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read,
- ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir};
-#endif
+static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
+ struct mg_dns *dnsc, bool ipv6) {
+ struct dns_data *d = NULL;
+ if (dnsc->url == NULL) {
+ mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
+ } else if (dnsc->c == NULL) {
+ dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
+ if (dnsc->c != NULL) {
+ dnsc->c->pfn = dns_cb;
+ // dnsc->c->is_hexdumping = 1;
+ }
+ }
+ if (dnsc->c == NULL) {
+ mg_error(c, "resolver");
+ } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
+ mg_error(c, "resolve OOM");
+ } else {
+ struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
+ d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
+ d->next = (struct dns_data *) c->mgr->active_dns_requests;
+ c->mgr->active_dns_requests = d;
+ d->expire = mg_millis() + (uint64_t) ms;
+ d->c = c;
+ c->is_resolving = 1;
+ MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len,
+ name->buf, dnsc->url, d->txnid));
+ if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) {
+ mg_error(dnsc->c, "DNS send");
+ }
+ }
+}
+
+void mg_resolve(struct mg_connection *c, const char *url) {
+ struct mg_str host = mg_url_host(url);
+ c->rem.port = mg_htons(mg_url_port(url));
+ if (mg_aton(host, &c->rem)) {
+ // host is an IP address, do not fire name resolution
+ mg_connect_resolved(c);
+ } else {
+ // host is not an IP, send DNS resolution request
+ struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
+ mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
+ }
+}
#ifdef MG_ENABLE_LINES
-#line 1 "src/fs_packed.c"
+#line 1 "src/event.c"
#endif
-struct packed_file {
- const char *data;
- size_t size;
- size_t pos;
-};
-const char *mg_unpack(const char *path, size_t *size, time_t *mtime);
-const char *mg_unlist(size_t no);
-#if MG_ENABLE_PACKED_FS
-#else
-const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {
- (void) path, (void) size, (void) mtime;
- return NULL;
-}
-const char *mg_unlist(size_t no) {
- (void) no;
- return NULL;
-}
+void mg_call(struct mg_connection *c, int ev, void *ev_data) {
+#if MG_ENABLE_PROFILE
+ const char *names[] = {
+ "EV_ERROR", "EV_OPEN", "EV_POLL", "EV_RESOLVE",
+ "EV_CONNECT", "EV_ACCEPT", "EV_TLS_HS", "EV_READ",
+ "EV_WRITE", "EV_CLOSE", "EV_HTTP_MSG", "EV_HTTP_CHUNK",
+ "EV_WS_OPEN", "EV_WS_MSG", "EV_WS_CTL", "EV_MQTT_CMD",
+ "EV_MQTT_MSG", "EV_MQTT_OPEN", "EV_SNTP_TIME", "EV_USER"};
+ if (ev != MG_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) {
+ MG_PROF_ADD(c, names[ev]);
+ }
#endif
-
-static int is_dir_prefix(const char *prefix, size_t n, const char *path) {
- // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n]));
- return n < strlen(path) && strncmp(prefix, path, n) == 0 &&
- (n == 0 || path[n] == '/' || path[n - 1] == '/');
+ // Fire protocol handler first, user handler second. See #2559
+ if (c->pfn != NULL) c->pfn(c, ev, ev_data);
+ if (c->fn != NULL) c->fn(c, ev, ev_data);
}
-static int packed_stat(const char *path, size_t *size, time_t *mtime) {
- const char *p;
- size_t i, n = strlen(path);
- if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file
- // Scan all files. If `path` is a dir prefix for any of them, it's a dir
- for (i = 0; (p = mg_unlist(i)) != NULL; i++) {
- if (is_dir_prefix(path, n, p)) return MG_FS_DIR;
- }
- return 0;
-}
-
-static void packed_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
- char buf[MG_PATH_MAX], tmp[sizeof(buf)];
- const char *path, *begin, *end;
- size_t i, n = strlen(dir);
- tmp[0] = '\0'; // Previously listed entry
- for (i = 0; (path = mg_unlist(i)) != NULL; i++) {
- if (!is_dir_prefix(dir, n, path)) continue;
- begin = &path[n + 1];
- end = strchr(begin, '/');
- if (end == NULL) end = begin + strlen(begin);
- mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin);
- buf[sizeof(buf) - 1] = '\0';
- // If this entry has been already listed, skip
- // NOTE: we're assuming that file list is sorted alphabetically
- if (strcmp(buf, tmp) == 0) continue;
- fn(buf, userdata); // Not yet listed, call user function
- strcpy(tmp, buf); // And save this entry as listed
- }
-}
-
-static void *packed_open(const char *path, int flags) {
- size_t size = 0;
- const char *data = mg_unpack(path, &size, NULL);
- struct packed_file *fp = NULL;
- if (data == NULL) return NULL;
- if (flags & MG_FS_WRITE) return NULL;
- fp = (struct packed_file *) calloc(1, sizeof(*fp));
- fp->size = size;
- fp->data = data;
- return (void *) fp;
+void mg_error(struct mg_connection *c, const char *fmt, ...) {
+ char buf[64];
+ va_list ap;
+ va_start(ap, fmt);
+ mg_vsnprintf(buf, sizeof(buf), fmt, &ap);
+ va_end(ap);
+ MG_ERROR(("%lu %ld %s", c->id, c->fd, buf));
+ c->is_closing = 1; // Set is_closing before sending MG_EV_CALL
+ mg_call(c, MG_EV_ERROR, buf); // Let user handler override it
}
-static void packed_close(void *fp) {
- if (fp != NULL) free(fp);
-}
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fmt.c"
+#endif
-static size_t packed_read(void *fd, void *buf, size_t len) {
- struct packed_file *fp = (struct packed_file *) fd;
- if (fp->pos + len > fp->size) len = fp->size - fp->pos;
- memcpy(buf, &fp->data[fp->pos], len);
- fp->pos += len;
- return len;
-}
-static size_t packed_write(void *fd, const void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
-}
-static size_t packed_seek(void *fd, size_t offset) {
- struct packed_file *fp = (struct packed_file *) fd;
- fp->pos = offset;
- if (fp->pos > fp->size) fp->pos = fp->size;
- return fp->pos;
-}
-static bool packed_rename(const char *from, const char *to) {
- (void) from, (void) to;
- return false;
+static bool is_digit(int c) {
+ return c >= '0' && c <= '9';
}
-static bool packed_remove(const char *path) {
- (void) path;
- return false;
+static int addexp(char *buf, int e, int sign) {
+ int n = 0;
+ buf[n++] = 'e';
+ buf[n++] = (char) sign;
+ if (e > 400) return 0;
+ if (e < 10) buf[n++] = '0';
+ if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100);
+ if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10);
+ buf[n++] = (char) (e + '0');
+ return n;
}
-static bool packed_mkdir(const char *path) {
- (void) path;
- return false;
+static int xisinf(double x) {
+ union {
+ double f;
+ uint64_t u;
+ } ieee754 = {x};
+ return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
+ ((unsigned) ieee754.u == 0);
}
-struct mg_fs mg_fs_packed = {
- packed_stat, packed_list, packed_open, packed_close, packed_read,
- packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir};
-
-#ifdef MG_ENABLE_LINES
-#line 1 "src/fs_posix.c"
-#endif
-
-
-#if MG_ENABLE_FILE
-
-#ifndef MG_STAT_STRUCT
-#define MG_STAT_STRUCT stat
-#endif
-
-#ifndef MG_STAT_FUNC
-#define MG_STAT_FUNC stat
-#endif
-
-static int p_stat(const char *path, size_t *size, time_t *mtime) {
-#if !defined(S_ISDIR)
- MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
- return 0;
-#else
-#if MG_ARCH == MG_ARCH_WIN32
- struct _stati64 st;
- wchar_t tmp[MG_PATH_MAX];
- MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
- if (_wstati64(tmp, &st) != 0) return 0;
-#else
- struct MG_STAT_STRUCT st;
- if (MG_STAT_FUNC(path, &st) != 0) return 0;
-#endif
- if (size) *size = (size_t) st.st_size;
- if (mtime) *mtime = st.st_mtime;
- return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0);
-#endif
+static int xisnan(double x) {
+ union {
+ double f;
+ uint64_t u;
+ } ieee754 = {x};
+ return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) +
+ ((unsigned) ieee754.u != 0) >
+ 0x7ff00000;
}
-#if MG_ARCH == MG_ARCH_WIN32
-struct dirent {
- char d_name[MAX_PATH];
-};
-
-typedef struct win32_dir {
- HANDLE handle;
- WIN32_FIND_DATAW info;
- struct dirent result;
-} DIR;
+static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) {
+ char buf[40];
+ int i, s = 0, n = 0, e = 0;
+ double t, mul, saved;
+ if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0");
+ if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf");
+ if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan");
+ if (d < 0.0) d = -d, buf[s++] = '-';
-int gettimeofday(struct timeval *tv, void *tz) {
- FILETIME ft;
- unsigned __int64 tmpres = 0;
+ // Round
+ saved = d;
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
+ while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
+ for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
+ d += t;
+ // Calculate exponent, and 'mul' for scientific representation
+ mul = 1.0;
+ while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
+ while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
+ // printf(" --> %g %d %g %g\n", saved, e, t, mul);
- if (tv != NULL) {
- GetSystemTimeAsFileTime(&ft);
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
- tmpres /= 10; // convert into microseconds
- tmpres -= (int64_t) 11644473600000000;
- tv->tv_sec = (long) (tmpres / 1000000UL);
- tv->tv_usec = (long) (tmpres % 1000000UL);
+ if (e >= width && width > 1) {
+ n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
+ n += addexp(buf + s + n, e, '+');
+ return mg_snprintf(dst, dstlen, "%.*s", n, buf);
+ } else if (e <= -width && width > 1) {
+ n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
+ // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf);
+ n += addexp(buf + s + n, -e, '-');
+ return mg_snprintf(dst, dstlen, "%.*s", n, buf);
+ } else {
+ for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {
+ int ch = (int) (d / t);
+ if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
+ // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
+ if (n == 0) buf[s++] = '0';
+ while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
+ if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
+ // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
+ for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
+ int ch = (int) (d / t);
+ buf[s + n++] = (char) (ch + '0');
+ d -= ch * t;
+ t /= 10.0;
+ }
}
- (void) tz;
- return 0;
+ while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes
+ if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
+ n += s;
+ if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1;
+ buf[n] = '\0';
+ return mg_snprintf(dst, dstlen, "%s", buf);
}
-static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
- int ret;
- char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;
- strncpy(buf, path, sizeof(buf));
- buf[sizeof(buf) - 1] = '\0';
- // Trim trailing slashes. Leave backslash for paths like "X:\"
- p = buf + strlen(buf) - 1;
- while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
- memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
- ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
- // Convert back to Unicode. If doubly-converted string does not match the
- // original, something is fishy, reject.
- WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
- NULL, NULL);
- if (strcmp(buf, buf2) != 0) {
- wbuf[0] = L'\0';
- ret = 0;
+static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) {
+ const char *letters = "0123456789abcdef";
+ uint64_t v = (uint64_t) val;
+ size_t s = 0, n, i;
+ if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val);
+ // This loop prints a number in reverse order. I guess this is because we
+ // write numbers from right to left: least significant digit comes last.
+ // Maybe because we use Arabic numbers, and Arabs write RTL?
+ if (is_hex) {
+ for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15];
+ } else {
+ for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10];
}
- return ret;
+ // Reverse a string
+ for (i = 0; i < n / 2; i++) {
+ char t = buf[s + i];
+ buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
+ }
+ if (val == 0) buf[n++] = '0'; // Handle special case
+ return n + s;
}
-DIR *opendir(const char *name) {
- DIR *d = NULL;
- wchar_t wpath[MAX_PATH];
- DWORD attrs;
+static size_t scpy(void (*out)(char, void *), void *ptr, char *buf,
+ size_t len) {
+ size_t i = 0;
+ while (i < len && buf[i] != '\0') out(buf[i++], ptr);
+ return i;
+}
- if (name == NULL) {
- SetLastError(ERROR_BAD_ARGUMENTS);
- } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- } else {
- to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
- attrs = GetFileAttributesW(wpath);
- if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
- (void) wcscat(wpath, L"\\*");
- d->handle = FindFirstFileW(wpath, &d->info);
- d->result.d_name[0] = '\0';
+size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) {
+ size_t len = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ len = mg_vxprintf(out, ptr, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
+ va_list *ap) {
+ size_t i = 0, n = 0;
+ while (fmt[i] != '\0') {
+ if (fmt[i] == '%') {
+ size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */;
+ char pad = ' ', minus = 0, c = fmt[++i];
+ if (c == '#') x++, c = fmt[++i];
+ if (c == '-') minus++, c = fmt[++i];
+ if (c == '0') pad = '0', c = fmt[++i];
+ while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i];
+ if (c == '.') {
+ c = fmt[++i];
+ if (c == '*') {
+ pr = (size_t) va_arg(*ap, int);
+ c = fmt[++i];
+ } else {
+ pr = 0;
+ while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i];
+ }
+ }
+ while (c == 'h') c = fmt[++i]; // Treat h and hh as int
+ if (c == 'l') {
+ is_long++, c = fmt[++i];
+ if (c == 'l') is_long++, c = fmt[++i];
+ }
+ if (c == 'p') x = 1, is_long = 1;
+ if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
+ c == 'g' || c == 'f') {
+ bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p');
+ char tmp[40];
+ size_t xl = x ? 2 : 0;
+ if (c == 'g' || c == 'f') {
+ double v = va_arg(*ap, double);
+ if (pr == ~0U) pr = 6;
+ k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g');
+ } else if (is_long == 2) {
+ int64_t v = va_arg(*ap, int64_t);
+ k = mg_lld(tmp, v, s, h);
+ } else if (is_long == 1) {
+ long v = va_arg(*ap, long);
+ k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h);
+ } else {
+ int v = va_arg(*ap, int);
+ k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h);
+ }
+ for (j = 0; j < xl && w > 0; j++) w--;
+ for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, (char *) "0x", xl);
+ for (j = 0; pad == '0' && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, tmp, k);
+ for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++)
+ n += scpy(out, param, &pad, 1);
+ } else if (c == 'm' || c == 'M') {
+ mg_pm_t f = va_arg(*ap, mg_pm_t);
+ if (c == 'm') out('"', param);
+ n += f(out, param, ap);
+ if (c == 'm') n += 2, out('"', param);
+ } else if (c == 'c') {
+ int ch = va_arg(*ap, int);
+ out((char) ch, param);
+ n++;
+ } else if (c == 's') {
+ char *p = va_arg(*ap, char *);
+ if (pr == ~0U) pr = p == NULL ? 0 : strlen(p);
+ for (j = 0; !minus && pr < w && j + pr < w; j++)
+ n += scpy(out, param, &pad, 1);
+ n += scpy(out, param, p, pr);
+ for (j = 0; minus && pr < w && j + pr < w; j++)
+ n += scpy(out, param, &pad, 1);
+ } else if (c == '%') {
+ out('%', param);
+ n++;
+ } else {
+ out('%', param);
+ out(c, param);
+ n += 2;
+ }
+ i++;
} else {
- free(d);
- d = NULL;
+ out(fmt[i], param), n++, i++;
}
}
- return d;
+ return n;
}
-int closedir(DIR *d) {
- int result = 0;
- if (d != NULL) {
- if (d->handle != INVALID_HANDLE_VALUE)
- result = FindClose(d->handle) ? 0 : -1;
- free(d);
- } else {
- result = -1;
- SetLastError(ERROR_BAD_ARGUMENTS);
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs.c"
+#endif
+
+
+
+
+struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) {
+ struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd));
+ if (fd != NULL) {
+ fd->fd = fs->op(path, flags);
+ fd->fs = fs;
+ if (fd->fd == NULL) {
+ free(fd);
+ fd = NULL;
+ }
}
- return result;
+ return fd;
}
-struct dirent *readdir(DIR *d) {
- struct dirent *result = NULL;
- if (d != NULL) {
- memset(&d->result, 0, sizeof(d->result));
- if (d->handle != INVALID_HANDLE_VALUE) {
- result = &d->result;
- WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name,
- sizeof(result->d_name), NULL, NULL);
- if (!FindNextFileW(d->handle, &d->info)) {
- FindClose(d->handle);
- d->handle = INVALID_HANDLE_VALUE;
- }
- } else {
- SetLastError(ERROR_FILE_NOT_FOUND);
- }
- } else {
- SetLastError(ERROR_BAD_ARGUMENTS);
+void mg_fs_close(struct mg_fd *fd) {
+ if (fd != NULL) {
+ fd->fs->cl(fd->fd);
+ free(fd);
}
- return result;
}
-#endif
-static void p_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
-#if MG_ENABLE_DIRLIST
- struct dirent *dp;
- DIR *dirp;
- if ((dirp = (opendir(dir))) == NULL) return;
- while ((dp = readdir(dirp)) != NULL) {
- if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
- fn(dp->d_name, userdata);
+struct mg_str mg_file_read(struct mg_fs *fs, const char *path) {
+ struct mg_str result = {NULL, 0};
+ void *fp;
+ fs->st(path, &result.len, NULL);
+ if ((fp = fs->op(path, MG_FS_READ)) != NULL) {
+ result.buf = (char *) calloc(1, result.len + 1);
+ if (result.buf != NULL &&
+ fs->rd(fp, (void *) result.buf, result.len) != result.len) {
+ free((void *) result.buf);
+ result.buf = NULL;
+ }
+ fs->cl(fp);
}
- closedir(dirp);
-#else
- (void) dir, (void) fn, (void) userdata;
-#endif
+ if (result.buf == NULL) result.len = 0;
+ return result;
}
-static void *p_open(const char *path, int flags) {
- const char *mode = flags == MG_FS_READ ? "rb" : "a+b";
-#if MG_ARCH == MG_ARCH_WIN32
- wchar_t b1[MG_PATH_MAX], b2[10];
- MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
- MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
- return (void *) _wfopen(b1, b2);
-#else
- return (void *) fopen(path, mode);
-#endif
+bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
+ size_t len) {
+ bool result = false;
+ struct mg_fd *fd;
+ char tmp[MG_PATH_MAX];
+ mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand());
+ if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) {
+ result = fs->wr(fd->fd, buf, len) == len;
+ mg_fs_close(fd);
+ if (result) {
+ fs->rm(path);
+ fs->mv(tmp, path);
+ } else {
+ fs->rm(tmp);
+ }
+ }
+ return result;
}
-static void p_close(void *fp) {
- fclose((FILE *) fp);
+bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
+ va_list ap;
+ char *data;
+ bool result = false;
+ va_start(ap, fmt);
+ data = mg_vmprintf(fmt, &ap);
+ va_end(ap);
+ result = mg_file_write(fs, path, data, strlen(data));
+ free(data);
+ return result;
}
-static size_t p_read(void *fp, void *buf, size_t len) {
- return fread(buf, 1, len, (FILE *) fp);
+// This helper function allows to scan a filesystem in a sequential way,
+// without using callback function:
+// char buf[100] = "";
+// while (mg_fs_ls(&mg_fs_posix, "./", buf, sizeof(buf))) {
+// ...
+static void mg_fs_ls_fn(const char *filename, void *param) {
+ struct mg_str *s = (struct mg_str *) param;
+ if (s->buf[0] == '\0') {
+ mg_snprintf((char *) s->buf, s->len, "%s", filename);
+ } else if (strcmp(s->buf, filename) == 0) {
+ ((char *) s->buf)[0] = '\0'; // Fetch next file
+ }
}
-static size_t p_write(void *fp, const void *buf, size_t len) {
- return fwrite(buf, 1, len, (FILE *) fp);
+bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) {
+ struct mg_str s = {buf, len};
+ fs->ls(path, mg_fs_ls_fn, &s);
+ return buf[0] != '\0';
}
-static size_t p_seek(void *fp, size_t offset) {
-#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
- (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
- (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
- if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0;
-#else
- if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0;
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_fat.c"
#endif
- return (size_t) ftell((FILE *) fp);
-}
-static bool p_rename(const char *from, const char *to) {
- return rename(from, to) == 0;
-}
-static bool p_remove(const char *path) {
- return remove(path) == 0;
-}
-static bool p_mkdir(const char *path) {
- return mkdir(path, 0775) == 0;
-}
+#if MG_ENABLE_FATFS
+#include <ff.h>
-#else
+static int mg_days_from_epoch(int y, int m, int d) {
+ y -= m <= 2;
+ int era = y / 400;
+ int yoe = y - era * 400;
+ int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
+ int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
+ return era * 146097 + doe - 719468;
+}
-static int p_stat(const char *path, size_t *size, time_t *mtime) {
- (void) path, (void) size, (void) mtime;
- return 0;
+static time_t mg_timegm(const struct tm *t) {
+ int year = t->tm_year + 1900;
+ int month = t->tm_mon; // 0-11
+ if (month > 11) {
+ year += month / 12;
+ month %= 12;
+ } else if (month < 0) {
+ int years_diff = (11 - month) / 12;
+ year -= years_diff;
+ month += 12 * years_diff;
+ }
+ int x = mg_days_from_epoch(year, month + 1, t->tm_mday);
+ return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec;
}
-static void p_list(const char *path, void (*fn)(const char *, void *),
- void *userdata) {
- (void) path, (void) fn, (void) userdata;
-}
-static void *p_open(const char *path, int flags) {
- (void) path, (void) flags;
- return NULL;
-}
-static void p_close(void *fp) {
- (void) fp;
-}
-static size_t p_read(void *fd, void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
-}
-static size_t p_write(void *fd, const void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
-}
-static size_t p_seek(void *fd, size_t offset) {
- (void) fd, (void) offset;
- return (size_t) ~0;
-}
-static bool p_rename(const char *from, const char *to) {
- (void) from, (void) to;
- return false;
-}
-static bool p_remove(const char *path) {
- (void) path;
- return false;
-}
-static bool p_mkdir(const char *path) {
- (void) path;
- return false;
-}
-#endif
-
-struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
- p_write, p_seek, p_rename, p_remove, p_mkdir};
-
-#ifdef MG_ENABLE_LINES
-#line 1 "src/http.c"
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
-// Chunk deletion marker is the MSB in the "processed" counter
-#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
-
-// Multipart POST example:
-// --xyz
-// Content-Disposition: form-data; name="val"
-//
-// abcdef
-// --xyz
-// Content-Disposition: form-data; name="foo"; filename="a.txt"
-// Content-Type: text/plain
-//
-// hello world
-//
-// --xyz--
-size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
- struct mg_http_part *part) {
- struct mg_str cd = mg_str_n("Content-Disposition", 19);
- const char *s = body.ptr;
- size_t b = ofs, h1, h2, b1, b2, max = body.len;
-
- // Init part params
- if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
-
- // Skip boundary
- while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
- if (b <= ofs || b + 2 >= max) return 0;
- // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
-
- // Skip headers
- h1 = h2 = b + 2;
- for (;;) {
- while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
- if (h2 == h1) break;
- if (h2 + 2 >= max) return 0;
- // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
- if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
- mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) {
- struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
- part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
- part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
- }
- h1 = h2 = h2 + 2;
- }
- b1 = b2 = h2 + 2;
- while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
- memcmp(&s[b2 + 2], s, b - ofs) == 0))
- b2++;
- if (b2 + 2 >= max) return 0;
- if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
- // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
- return b2 + 2;
+static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_sec = (ftime << 1) & 0x3e;
+ tm.tm_min = ((ftime >> 5) & 0x3f);
+ tm.tm_hour = ((ftime >> 11) & 0x1f);
+ tm.tm_mday = (fdate & 0x1f);
+ tm.tm_mon = ((fdate >> 5) & 0x0f) - 1;
+ tm.tm_year = ((fdate >> 9) & 0x7f) + 80;
+ return mg_timegm(&tm);
}
-void mg_http_bauth(struct mg_connection *c, const char *user,
- const char *pass) {
- struct mg_str u = mg_str(user), p = mg_str(pass);
- size_t need = c->send.len + 36 + (u.len + p.len) * 2;
- if (c->send.size < need) mg_iobuf_resize(&c->send, need);
- if (c->send.size >= need) {
- int i, n = 0;
- char *buf = (char *) &c->send.buf[c->send.len];
- memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send!
- for (i = 0; i < (int) u.len; i++) {
- n = mg_base64_update(((unsigned char *) u.ptr)[i], buf + 21, n);
- }
- if (p.len > 0) {
- n = mg_base64_update(':', buf + 21, n);
- for (i = 0; i < (int) p.len; i++) {
- n = mg_base64_update(((unsigned char *) p.ptr)[i], buf + 21, n);
- }
- }
- n = mg_base64_final(buf + 21, n);
- c->send.len += 21 + (size_t) n + 2;
- memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2);
+static int ff_stat(const char *path, size_t *size, time_t *mtime) {
+ FILINFO fi;
+ if (path[0] == '\0') {
+ if (size) *size = 0;
+ if (mtime) *mtime = 0;
+ return MG_FS_DIR;
+ } else if (f_stat(path, &fi) == 0) {
+ if (size) *size = (size_t) fi.fsize;
+ if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime);
+ return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
} else {
- MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need));
+ return 0;
}
}
-struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) {
- struct mg_str k, v, result = mg_str_n(NULL, 0);
- while (mg_split(&buf, &k, &v, '&')) {
- if (name.len == k.len && mg_ncasecmp(name.ptr, k.ptr, k.len) == 0) {
- result = v;
- break;
+static void ff_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ DIR d;
+ FILINFO fi;
+ if (f_opendir(&d, dir) == FR_OK) {
+ while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
+ if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
+ fn(fi.fname, userdata);
}
+ f_closedir(&d);
}
- return result;
}
-int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
- size_t dst_len) {
- int len;
- if (dst == NULL || dst_len == 0) {
- len = -2; // Bad destination
- } else if (buf->ptr == NULL || name == NULL || buf->len == 0) {
- len = -1; // Bad source
- dst[0] = '\0';
- } else {
- struct mg_str v = mg_http_var(*buf, mg_str(name));
- if (v.ptr == NULL) {
- len = -4; // Name does not exist
- } else {
- len = mg_url_decode(v.ptr, v.len, dst, dst_len, 1);
- if (len < 0) len = -3; // Failed to decode
+static void *ff_open(const char *path, int flags) {
+ FIL f;
+ unsigned char mode = FA_READ;
+ if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
+ if (f_open(&f, path, mode) == 0) {
+ FIL *fp;
+ if ((fp = calloc(1, sizeof(*fp))) != NULL) {
+ memcpy(fp, &f, sizeof(*fp));
+ return fp;
}
}
- return len;
+ return NULL;
}
-static bool isx(int c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F');
+static void ff_close(void *fp) {
+ if (fp != NULL) {
+ f_close((FIL *) fp);
+ free(fp);
+ }
}
-int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
- int is_form_url_encoded) {
- size_t i, j;
- for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) {
- if (src[i] == '%') {
- // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len
- if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) {
- mg_unhex(src + i + 1, 2, (uint8_t *) &dst[j]);
- i += 2;
- } else {
- return -1;
- }
- } else if (is_form_url_encoded && src[i] == '+') {
- dst[j] = ' ';
- } else {
- dst[j] = src[i];
- }
+static size_t ff_read(void *fp, void *buf, size_t len) {
+ UINT n = 0, misalign = ((size_t) buf) & 3;
+ if (misalign) {
+ char aligned[4];
+ f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n);
+ memcpy(buf, aligned, n);
+ } else {
+ f_read((FIL *) fp, buf, len, &n);
}
- if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
- return i >= src_len && j < dst_len ? (int) j : -1;
+ return n;
}
-static bool isok(uint8_t c) { return c == '\n' || c == '\r' || c >= ' '; }
-
-int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
- size_t i;
- for (i = 0; i < buf_len; i++) {
- if (!isok(buf[i])) return -1;
- if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') ||
- (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n'))
- return (int) i + 1;
- }
- return 0;
+static size_t ff_write(void *fp, const void *buf, size_t len) {
+ UINT n = 0;
+ return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0;
}
-static const char *skip(const char *s, const char *e, const char *d,
- struct mg_str *v) {
- v->ptr = s;
- while (s < e && *s != '\n' && strchr(d, *s) == NULL) s++;
- v->len = (size_t) (s - v->ptr);
- while (s < e && strchr(d, *s) != NULL) s++;
- return s;
+static size_t ff_seek(void *fp, size_t offset) {
+ f_lseek((FIL *) fp, offset);
+ return offset;
}
-struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) {
- size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]);
- for (i = 0; i < max && h->headers[i].name.len > 0; i++) {
- struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value;
- if (n == k->len && mg_ncasecmp(k->ptr, name, n) == 0) return v;
- }
- return NULL;
+static bool ff_rename(const char *from, const char *to) {
+ return f_rename(from, to) == FR_OK;
}
-static void mg_http_parse_headers(const char *s, const char *end,
- struct mg_http_header *h, int max_headers) {
- int i;
- for (i = 0; i < max_headers; i++) {
- struct mg_str k, v, tmp;
- const char *he = skip(s, end, "\n", &tmp);
- s = skip(s, he, ": \r\n", &k);
- s = skip(s, he, "\r\n", &v);
- if (k.len == tmp.len) continue;
- while (v.len > 0 && v.ptr[v.len - 1] == ' ') v.len--; // Trim spaces
- if (k.len == 0) break;
- // MG_INFO(("--HH [%.*s] [%.*s] [%.*s]", (int) tmp.len - 1, tmp.ptr,
- //(int) k.len, k.ptr, (int) v.len, v.ptr));
- h[i].name = k;
- h[i].value = v;
- }
+static bool ff_remove(const char *path) {
+ return f_unlink(path) == FR_OK;
}
-int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
- int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len);
- const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL
- struct mg_str *cl;
+static bool ff_mkdir(const char *path) {
+ return f_mkdir(path) == FR_OK;
+}
- memset(hm, 0, sizeof(*hm));
- if (req_len <= 0) return req_len;
+struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read,
+ ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir};
+#endif
- hm->message.ptr = hm->head.ptr = s;
- hm->body.ptr = end;
- hm->head.len = (size_t) req_len;
- hm->chunk.ptr = end;
- hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_packed.c"
+#endif
- // Parse request line
- s = skip(s, end, " ", &hm->method);
- s = skip(s, end, " ", &hm->uri);
- s = skip(s, end, "\r\n", &hm->proto);
- // Sanity check. Allow protocol/reason to be empty
- if (hm->method.len == 0 || hm->uri.len == 0) return -1;
- // If URI contains '?' character, setup query string
- if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) {
- hm->query.ptr = qs + 1;
- hm->query.len = (size_t) (&hm->uri.ptr[hm->uri.len] - (qs + 1));
- hm->uri.len = (size_t) (qs - hm->uri.ptr);
- }
- mg_http_parse_headers(s, end, hm->headers,
- sizeof(hm->headers) / sizeof(hm->headers[0]));
- if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
- hm->body.len = (size_t) mg_to64(*cl);
- hm->message.len = (size_t) req_len + hm->body.len;
- }
+struct packed_file {
+ const char *data;
+ size_t size;
+ size_t pos;
+};
- // mg_http_parse() is used to parse both HTTP requests and HTTP
- // responses. If HTTP response does not have Content-Length set, then
- // body is read until socket is closed, i.e. body.len is infinite (~0).
- //
- // For HTTP requests though, according to
- // http://tools.ietf.org/html/rfc7231#section-8.1.3,
- // only POST and PUT methods have defined body semantics.
- // Therefore, if Content-Length is not specified and methods are
- // not one of PUT or POST, set body length to 0.
- //
- // So, if it is HTTP request, and Content-Length is not set,
- // and method is not (PUT or POST) then reset body length to zero.
- is_response = mg_ncasecmp(hm->method.ptr, "HTTP/", 5) == 0;
- if (hm->body.len == (size_t) ~0 && !is_response &&
- mg_vcasecmp(&hm->method, "PUT") != 0 &&
- mg_vcasecmp(&hm->method, "POST") != 0) {
- hm->body.len = 0;
- hm->message.len = (size_t) req_len;
- }
+#if MG_ENABLE_PACKED_FS
+#else
+const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {
+ *size = 0, *mtime = 0;
+ (void) path;
+ return NULL;
+}
+const char *mg_unlist(size_t no) {
+ (void) no;
+ return NULL;
+}
+#endif
- // The 204 (No content) responses also have 0 body length
- if (hm->body.len == (size_t) ~0 && is_response &&
- mg_vcasecmp(&hm->uri, "204") == 0) {
- hm->body.len = 0;
- hm->message.len = (size_t) req_len;
- }
+struct mg_str mg_unpacked(const char *path) {
+ size_t len = 0;
+ const char *buf = mg_unpack(path, &len, NULL);
+ return mg_str_n(buf, len);
+}
- return req_len;
+static int is_dir_prefix(const char *prefix, size_t n, const char *path) {
+ // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n]));
+ return n < strlen(path) && strncmp(prefix, path, n) == 0 &&
+ (n == 0 || path[n] == '/' || path[n - 1] == '/');
}
-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);
- 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
+static int packed_stat(const char *path, size_t *size, time_t *mtime) {
+ const char *p;
+ size_t i, n = strlen(path);
+ if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file
+ // Scan all files. If `path` is a dir prefix for any of them, it's a dir
+ for (i = 0; (p = mg_unlist(i)) != NULL; i++) {
+ if (is_dir_prefix(path, n, p)) return MG_FS_DIR;
}
- mg_send(c, "\r\n", 2);
+ return 0;
}
-void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_http_vprintf_chunk(c, fmt, &ap);
- va_end(ap);
+static void packed_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+ char buf[MG_PATH_MAX], tmp[sizeof(buf)];
+ const char *path, *begin, *end;
+ size_t i, n = strlen(dir);
+ tmp[0] = '\0'; // Previously listed entry
+ for (i = 0; (path = mg_unlist(i)) != NULL; i++) {
+ if (!is_dir_prefix(dir, n, path)) continue;
+ begin = &path[n + 1];
+ end = strchr(begin, '/');
+ if (end == NULL) end = begin + strlen(begin);
+ mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin);
+ buf[sizeof(buf) - 1] = '\0';
+ // If this entry has been already listed, skip
+ // NOTE: we're assuming that file list is sorted alphabetically
+ if (strcmp(buf, tmp) == 0) continue;
+ fn(buf, userdata); // Not yet listed, call user function
+ strcpy(tmp, buf); // And save this entry as listed
+ }
}
-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 (len == 0) c->is_resp = 0;
+static void *packed_open(const char *path, int flags) {
+ size_t size = 0;
+ const char *data = mg_unpack(path, &size, NULL);
+ struct packed_file *fp = NULL;
+ if (data == NULL) return NULL;
+ if (flags & MG_FS_WRITE) return NULL;
+ if ((fp = (struct packed_file *) calloc(1, sizeof(*fp))) != NULL) {
+ fp->size = size;
+ fp->data = data;
+ }
+ return (void *) fp;
}
-// clang-format off
-static const char *mg_http_status_code_str(int status_code) {
- switch (status_code) {
- case 100: return "Continue";
- case 201: return "Created";
- case 202: return "Accepted";
- case 204: return "No Content";
- case 206: return "Partial Content";
- case 301: return "Moved Permanently";
- case 302: return "Found";
- case 304: return "Not Modified";
- case 400: return "Bad Request";
- case 401: return "Unauthorized";
- case 403: return "Forbidden";
- case 404: return "Not Found";
- case 418: return "I'm a teapot";
- case 500: return "Internal Server Error";
- case 501: return "Not Implemented";
- default: return "OK";
- }
+static void packed_close(void *fp) {
+ if (fp != NULL) free(fp);
}
-// clang-format on
-void mg_http_reply(struct mg_connection *c, int code, const char *headers,
- const char *fmt, ...) {
- va_list ap;
- size_t len;
- mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code,
- mg_http_status_code_str(code), headers == NULL ? "" : headers);
- len = c->send.len;
- va_start(ap, fmt);
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
- va_end(ap);
- if (c->send.len > 15) {
- mg_snprintf((char *) &c->send.buf[len - 14], 11, "%010lu",
- (unsigned long) (c->send.len - len));
- c->is_resp = 0;
- c->send.buf[len - 4] = '\r'; // Change ending 0 to space
- }
- c->is_resp = 0;
+static size_t packed_read(void *fd, void *buf, size_t len) {
+ struct packed_file *fp = (struct packed_file *) fd;
+ if (fp->pos + len > fp->size) len = fp->size - fp->pos;
+ memcpy(buf, &fp->data[fp->pos], len);
+ fp->pos += len;
+ return len;
}
-static void http_cb(struct mg_connection *, int, void *, void *);
-static void restore_http_cb(struct mg_connection *c) {
- mg_fs_close((struct mg_fd *) c->pfn_data);
- c->pfn_data = NULL;
- c->pfn = http_cb;
- c->is_resp = 0;
+static size_t packed_write(void *fd, const void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
}
-char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
-char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
- mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size);
- return buf;
+static size_t packed_seek(void *fd, size_t offset) {
+ struct packed_file *fp = (struct packed_file *) fd;
+ fp->pos = offset;
+ if (fp->pos > fp->size) fp->pos = fp->size;
+ return fp->pos;
}
-static void static_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
- struct mg_fd *fd = (struct mg_fd *) fn_data;
- // Read to send IO buffer directly, avoid extra on-stack buffer
- size_t n, max = MG_IO_SIZE, space;
- size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
- sizeof(size_t) * sizeof(size_t)];
- if (c->send.size < max) mg_iobuf_resize(&c->send, max);
- if (c->send.len >= c->send.size) return; // Rate limit
- if ((space = c->send.size - c->send.len) > *cl) space = *cl;
- n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space);
- c->send.len += n;
- *cl -= n;
- if (n == 0) restore_http_cb(c);
- } else if (ev == MG_EV_CLOSE) {
- restore_http_cb(c);
- }
- (void) ev_data;
+static bool packed_rename(const char *from, const char *to) {
+ (void) from, (void) to;
+ return false;
}
-// Known mime types. Keep it outside guess_content_type() function, since
-// some environments don't like it defined there.
-// clang-format off
-static struct mg_str s_known_types[] = {
- MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"),
- MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"),
- MG_C_STR("gif"), MG_C_STR("image/gif"),
- MG_C_STR("png"), MG_C_STR("image/png"),
- MG_C_STR("jpg"), MG_C_STR("image/jpeg"),
- MG_C_STR("jpeg"), MG_C_STR("image/jpeg"),
- MG_C_STR("woff"), MG_C_STR("font/woff"),
- MG_C_STR("ttf"), MG_C_STR("font/ttf"),
- MG_C_STR("svg"), MG_C_STR("image/svg+xml"),
- MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"),
- MG_C_STR("avi"), MG_C_STR("video/x-msvideo"),
- MG_C_STR("csv"), MG_C_STR("text/csv"),
- MG_C_STR("doc"), MG_C_STR("application/msword"),
- MG_C_STR("exe"), MG_C_STR("application/octet-stream"),
- MG_C_STR("gz"), MG_C_STR("application/gzip"),
- MG_C_STR("ico"), MG_C_STR("image/x-icon"),
- MG_C_STR("json"), MG_C_STR("application/json"),
- MG_C_STR("mov"), MG_C_STR("video/quicktime"),
- MG_C_STR("mp3"), MG_C_STR("audio/mpeg"),
- MG_C_STR("mp4"), MG_C_STR("video/mp4"),
- MG_C_STR("mpeg"), MG_C_STR("video/mpeg"),
- MG_C_STR("pdf"), MG_C_STR("application/pdf"),
- MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("tgz"), MG_C_STR("application/tar-gz"),
- MG_C_STR("wav"), MG_C_STR("audio/wav"),
- MG_C_STR("webp"), MG_C_STR("image/webp"),
- MG_C_STR("zip"), MG_C_STR("application/zip"),
- MG_C_STR("3gp"), MG_C_STR("video/3gpp"),
- {0, 0},
-};
-// clang-format on
+static bool packed_remove(const char *path) {
+ (void) path;
+ return false;
+}
-static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
- struct mg_str k, v, s = mg_str(extra);
- size_t i = 0;
+static bool packed_mkdir(const char *path) {
+ (void) path;
+ return false;
+}
- // Shrink path to its extension only
- while (i < path.len && path.ptr[path.len - i - 1] != '.') i++;
- path.ptr += path.len - i;
- path.len = i;
+struct mg_fs mg_fs_packed = {
+ packed_stat, packed_list, packed_open, packed_close, packed_read,
+ packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir};
- // Process user-provided mime type overrides, if any
- while (mg_commalist(&s, &k, &v)) {
- if (mg_strcmp(path, k) == 0) return v;
- }
+#ifdef MG_ENABLE_LINES
+#line 1 "src/fs_posix.c"
+#endif
- // Process built-in mime types
- for (i = 0; s_known_types[i].ptr != NULL; i += 2) {
- if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1];
- }
- return mg_str("text/plain; charset=utf-8");
-}
+#if MG_ENABLE_POSIX_FS
-static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
- size_t i, numparsed = 0;
- // MG_INFO(("%.*s", (int) s->len, s->ptr));
- for (i = 0; i + 6 < s->len; i++) {
- if (memcmp(&s->ptr[i], "bytes=", 6) == 0) {
- struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6);
- if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
- *a = mg_to64(p);
- // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
- while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--;
- if (p.len && p.ptr[0] == '-') p.ptr++, p.len--;
- *b = mg_to64(p);
- if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
- // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
- break;
- }
- }
- return (int) numparsed;
-}
+#ifndef MG_STAT_STRUCT
+#define MG_STAT_STRUCT stat
+#endif
-void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
- const char *path,
- const struct mg_http_serve_opts *opts) {
- char etag[64], tmp[MG_PATH_MAX];
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_fd *fd = path == NULL ? NULL : mg_fs_open(fs, path, MG_FS_READ);
- size_t size = 0;
- time_t mtime = 0;
- struct mg_str *inm = NULL;
- struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
- bool gzip = false;
+#ifndef MG_STAT_FUNC
+#define MG_STAT_FUNC stat
+#endif
- // If file does not exist, we try to open file PATH.gz - and if such
- // pre-compressed .gz file exists, serve it with the Content-Encoding: gzip
- // Note - we ignore Accept-Encoding, cause we don't have a choice
- if (fd == NULL) {
- MG_DEBUG(("NULL [%s]", path));
- mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
- if ((fd = mg_fs_open(fs, tmp, MG_FS_READ)) != NULL) {
- gzip = true;
- path = tmp;
- } else if (opts->page404 != NULL) {
- // No precompressed file, serve 404
- fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
- mime = guess_content_type(mg_str(path), opts->mime_types);
- path = opts->page404;
+static int p_stat(const char *path, size_t *size, time_t *mtime) {
+#if !defined(S_ISDIR)
+ MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
+ return 0;
+#else
+#if MG_ARCH == MG_ARCH_WIN32
+ struct _stati64 st;
+ wchar_t tmp[MG_PATH_MAX];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
+ if (_wstati64(tmp, &st) != 0) return 0;
+ // If path is a symlink, windows reports 0 in st.st_size.
+ // Get a real file size by opening it and jumping to the end
+ if (st.st_size == 0 && (st.st_mode & _S_IFREG)) {
+ FILE *fp = _wfopen(tmp, L"rb");
+ if (fp != NULL) {
+ fseek(fp, 0, SEEK_END);
+ if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+
+ fclose(fp);
}
}
+#else
+ struct MG_STAT_STRUCT st;
+ if (MG_STAT_FUNC(path, &st) != 0) return 0;
+#endif
+ if (size) *size = (size_t) st.st_size;
+ if (mtime) *mtime = st.st_mtime;
+ return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0);
+#endif
+}
- if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
- mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
- mg_fs_close(fd);
- // NOTE: mg_http_etag() call should go first!
- } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
- (inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
- mg_vcasecmp(inm, etag) == 0) {
- mg_fs_close(fd);
- mg_http_reply(c, 304, opts->extra_headers, "");
- } else {
- int n, status = 200;
- char range[100];
- int64_t r1 = 0, r2 = 0, cl = (int64_t) size;
+#if MG_ARCH == MG_ARCH_WIN32
+struct dirent {
+ char d_name[MAX_PATH];
+};
- // Handle Range header
- struct mg_str *rh = mg_http_get_header(hm, "Range");
- range[0] = '\0';
- if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) {
- // If range is specified like "400-", set second limit to content len
- if (n == 1) r2 = cl - 1;
- if (r1 > r2 || r2 >= cl) {
- status = 416;
- cl = 0;
- mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n",
- (int64_t) size);
- } else {
- status = 206;
- cl = r2 - r1 + 1;
- mg_snprintf(range, sizeof(range),
- "Content-Range: bytes %lld-%lld/%lld\r\n", r1, r1 + cl - 1,
- (int64_t) size);
- fs->sk(fd->fd, (size_t) r1);
- }
- }
- mg_printf(c,
- "HTTP/1.1 %d %s\r\n"
- "Content-Type: %.*s\r\n"
- "Etag: %s\r\n"
- "Content-Length: %llu\r\n"
- "%s%s%s\r\n",
- status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
- etag, cl, gzip ? "Content-Encoding: gzip\r\n" : "", range,
- opts->extra_headers ? opts->extra_headers : "");
- if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
- c->is_draining = 1;
- c->is_resp = 0;
- mg_fs_close(fd);
- } else {
- // Track to-be-sent content length at the end of c->data, aligned
- size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
- sizeof(size_t) * sizeof(size_t)];
- c->pfn = static_cb;
- c->pfn_data = fd;
- *clp = (size_t) cl;
- }
+typedef struct win32_dir {
+ HANDLE handle;
+ WIN32_FIND_DATAW info;
+ struct dirent result;
+} DIR;
+
+#if 0
+int gettimeofday(struct timeval *tv, void *tz) {
+ FILETIME ft;
+ unsigned __int64 tmpres = 0;
+
+ if (tv != NULL) {
+ GetSystemTimeAsFileTime(&ft);
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+ tmpres /= 10; // convert into microseconds
+ tmpres -= (int64_t) 11644473600000000;
+ tv->tv_sec = (long) (tmpres / 1000000UL);
+ tv->tv_usec = (long) (tmpres % 1000000UL);
}
+ (void) tz;
+ return 0;
}
+#endif
-struct printdirentrydata {
- struct mg_connection *c;
- struct mg_http_message *hm;
- const struct mg_http_serve_opts *opts;
- const char *dir;
-};
+static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
+ int ret;
+ char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;
+ strncpy(buf, path, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ // Trim trailing slashes. Leave backslash for paths like "X:\"
+ p = buf + strlen(buf) - 1;
+ while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
+ memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
+ ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
+ // Convert back to Unicode. If doubly-converted string does not match the
+ // original, something is fishy, reject.
+ WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
+ NULL, NULL);
+ if (strcmp(buf, buf2) != 0) {
+ wbuf[0] = L'\0';
+ ret = 0;
+ }
+ return ret;
+}
-static void printdirentry(const char *name, void *userdata) {
- struct printdirentrydata *d = (struct printdirentrydata *) userdata;
- struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
- size_t size = 0;
- time_t t = 0;
- char path[MG_PATH_MAX], sz[40], mod[40];
- int flags, n = 0;
+DIR *opendir(const char *name) {
+ DIR *d = NULL;
+ wchar_t wpath[MAX_PATH];
+ DWORD attrs;
- // MG_DEBUG(("[%s] [%s]", d->dir, name));
- if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
- sizeof(path)) {
- MG_ERROR(("%s truncated", name));
- } else if ((flags = fs->st(path, &size, &t)) == 0) {
- MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno));
+ if (name == NULL) {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
} else {
- const char *slash = flags & MG_FS_DIR ? "/" : "";
- if (flags & MG_FS_DIR) {
- mg_snprintf(sz, sizeof(sz), "%s", "[DIR]");
+ to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
+ attrs = GetFileAttributesW(wpath);
+ if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+ (void) wcscat(wpath, L"\\*");
+ d->handle = FindFirstFileW(wpath, &d->info);
+ d->result.d_name[0] = '\0';
} else {
- mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size);
+ free(d);
+ d = NULL;
}
-#if defined(MG_HTTP_DIRLIST_TIME)
- char time_str[30];
- struct tm *time_info = localtime(&t);
- strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
- mg_snprintf(mod, sizeof(mod), "%s", time_str);
-#elif defined(MG_HTTP_DIRLIST_TIME_UTC)
- char time_str[30];
- struct tm *time_info = gmtime(&t);
- strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
- mg_snprintf(mod, sizeof(mod), "%s", time_str);
-#else
- mg_snprintf(mod, sizeof(mod), "%ld", (unsigned long) t);
-#endif
- n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
- mg_printf(d->c,
- " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
- "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
- n, path, slash, name, slash, (unsigned long) t, mod,
- flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
}
+ return d;
}
-static void listdir(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts, char *dir) {
- const char *sort_js_code =
- "<script>function srt(tb, sc, so, d) {"
- "var tr = Array.prototype.slice.call(tb.rows, 0),"
- "tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
- "n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
- "t1 = a.cells[2].getAttribute('name'), "
- "t2 = b.cells[2].getAttribute('name'); "
- "return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
- "n1 ? parseInt(n2) - parseInt(n1) : "
- "c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
- const char *sort_js_code2 =
- "for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
- "if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
- "};"
- "window.onload = function() {"
- "var tb = document.getElementById('tb');"
- "var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
- "var sc = m[1], so = m[2]; document.onclick = function(ev) { "
- "var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
- "sc = c; ev.preventDefault();}};"
- "srt(tb, sc, so, true);"
- "}"
- "</script>";
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct printdirentrydata d = {c, hm, opts, dir};
- char tmp[10], buf[MG_PATH_MAX];
- size_t off, n;
- int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0);
- struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
-
- mg_printf(c,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/html; charset=utf-8\r\n"
- "%s"
- "Content-Length: \r\n\r\n",
- opts->extra_headers == NULL ? "" : opts->extra_headers);
- off = c->send.len; // Start of body
- mg_printf(c,
- "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
- "<style>th,td {text-align: left; padding-right: 1em; "
- "font-family: monospace; }</style></head>"
- "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
- "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
- "<a href=\"#\" rel=\"1\">Modified</a></th>"
- "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
- "<tr><td colspan=\"3\"><hr></td></tr>"
- "</thead>"
- "<tbody id=\"tb\">\n",
- (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len,
- uri.ptr);
- mg_printf(c, "%s",
- " <tr><td><a href=\"..\">..</a></td>"
- "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
-
- fs->ls(dir, printdirentry, &d);
- mg_printf(c,
- "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
- "</table><address>Mongoose v.%s</address></body></html>\n",
- MG_VERSION);
- n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
- if (n > sizeof(tmp)) n = 0;
- memcpy(c->send.buf + off - 12, tmp, n); // Set content length
- c->is_resp = 0; // Mark response end
+int closedir(DIR *d) {
+ int result = 0;
+ if (d != NULL) {
+ if (d->handle != INVALID_HANDLE_VALUE)
+ result = FindClose(d->handle) ? 0 : -1;
+ free(d);
+ } else {
+ result = -1;
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+ return result;
}
-// Resolve requested file into `path` and return its fs->st() result
-static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, struct mg_str url, struct mg_str dir,
- char *path, size_t path_size) {
- int flags, tmp;
- // Append URI to the root_dir, and sanitize it
- size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
- if (n > path_size) n = path_size;
- path[path_size - 1] = '\0';
- if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
- mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
- path_size - n, 0);
- path[path_size - 1] = '\0'; // Double-check
- mg_remove_double_dots(path);
- n = strlen(path);
- while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
- flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL);
- MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.ptr, path,
- flags));
- if (flags == 0) {
- // Do nothing - let's caller decide
- } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
- hm->uri.ptr[hm->uri.len - 1] != '/') {
- mg_printf(c,
- "HTTP/1.1 301 Moved\r\n"
- "Location: %.*s/\r\n"
- "Content-Length: 0\r\n"
+struct dirent *readdir(DIR *d) {
+ struct dirent *result = NULL;
+ if (d != NULL) {
+ memset(&d->result, 0, sizeof(d->result));
+ if (d->handle != INVALID_HANDLE_VALUE) {
+ result = &d->result;
+ WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name,
+ sizeof(result->d_name), NULL, NULL);
+ if (!FindNextFileW(d->handle, &d->info)) {
+ FindClose(d->handle);
+ d->handle = INVALID_HANDLE_VALUE;
+ }
+ } else {
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+ } else {
+ SetLastError(ERROR_BAD_ARGUMENTS);
+ }
+ return result;
+}
+#endif
+
+static void p_list(const char *dir, void (*fn)(const char *, void *),
+ void *userdata) {
+#if MG_ENABLE_DIRLIST
+ struct dirent *dp;
+ DIR *dirp;
+ if ((dirp = (opendir(dir))) == NULL) return;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
+ fn(dp->d_name, userdata);
+ }
+ closedir(dirp);
+#else
+ (void) dir, (void) fn, (void) userdata;
+#endif
+}
+
+static void *p_open(const char *path, int flags) {
+#if MG_ARCH == MG_ARCH_WIN32
+ const char *mode = flags == MG_FS_READ ? "rb" : "a+b";
+ wchar_t b1[MG_PATH_MAX], b2[10];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
+ MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
+ return (void *) _wfopen(b1, b2);
+#else
+ const char *mode = flags == MG_FS_READ ? "rbe" : "a+be"; // e for CLOEXEC
+ return (void *) fopen(path, mode);
+#endif
+}
+
+static void p_close(void *fp) {
+ fclose((FILE *) fp);
+}
+
+static size_t p_read(void *fp, void *buf, size_t len) {
+ return fread(buf, 1, len, (FILE *) fp);
+}
+
+static size_t p_write(void *fp, const void *buf, size_t len) {
+ return fwrite(buf, 1, len, (FILE *) fp);
+}
+
+static size_t p_seek(void *fp, size_t offset) {
+#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
+ (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
+ (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
+ if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0;
+#else
+ if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0;
+#endif
+ return (size_t) ftell((FILE *) fp);
+}
+
+static bool p_rename(const char *from, const char *to) {
+ return rename(from, to) == 0;
+}
+
+static bool p_remove(const char *path) {
+ return remove(path) == 0;
+}
+
+static bool p_mkdir(const char *path) {
+ return mkdir(path, 0775) == 0;
+}
+
+#else
+
+static int p_stat(const char *path, size_t *size, time_t *mtime) {
+ (void) path, (void) size, (void) mtime;
+ return 0;
+}
+static void p_list(const char *path, void (*fn)(const char *, void *),
+ void *userdata) {
+ (void) path, (void) fn, (void) userdata;
+}
+static void *p_open(const char *path, int flags) {
+ (void) path, (void) flags;
+ return NULL;
+}
+static void p_close(void *fp) {
+ (void) fp;
+}
+static size_t p_read(void *fd, void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+static size_t p_write(void *fd, const void *buf, size_t len) {
+ (void) fd, (void) buf, (void) len;
+ return 0;
+}
+static size_t p_seek(void *fd, size_t offset) {
+ (void) fd, (void) offset;
+ return (size_t) ~0;
+}
+static bool p_rename(const char *from, const char *to) {
+ (void) from, (void) to;
+ return false;
+}
+static bool p_remove(const char *path) {
+ (void) path;
+ return false;
+}
+static bool p_mkdir(const char *path) {
+ (void) path;
+ return false;
+}
+#endif
+
+struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
+ p_write, p_seek, p_rename, p_remove, p_mkdir};
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/http.c"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
+ int diff = 0;
+ if (len > 0) do {
+ int c = *s1++, d = *s2++;
+ if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+ if (d >= 'A' && d <= 'Z') d += 'a' - 'A';
+ diff = c - d;
+ } while (diff == 0 && s1[-1] != '\0' && --len > 0);
+ return diff;
+}
+
+bool mg_to_size_t(struct mg_str str, size_t *val);
+bool mg_to_size_t(struct mg_str str, size_t *val) {
+ size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0;
+ while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++;
+ if (i < str.len && str.buf[i] == '-') return false;
+ while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') {
+ size_t digit = (size_t) (str.buf[i] - '0');
+ if (result > max2) return false; // Overflow
+ result *= 10;
+ if (result > max - digit) return false; // Overflow
+ result += digit;
+ i++, ndigits++;
+ }
+ while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++;
+ if (ndigits == 0) return false; // #2322: Content-Length = 1 * DIGIT
+ if (i != str.len) return false; // Ditto
+ *val = (size_t) result;
+ return true;
+}
+
+// Chunk deletion marker is the MSB in the "processed" counter
+#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
+
+// Multipart POST example:
+// --xyz
+// Content-Disposition: form-data; name="val"
+//
+// abcdef
+// --xyz
+// Content-Disposition: form-data; name="foo"; filename="a.txt"
+// Content-Type: text/plain
+//
+// hello world
+//
+// --xyz--
+size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
+ struct mg_http_part *part) {
+ struct mg_str cd = mg_str_n("Content-Disposition", 19);
+ const char *s = body.buf;
+ size_t b = ofs, h1, h2, b1, b2, max = body.len;
+
+ // Init part params
+ if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
+
+ // Skip boundary
+ while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
+ if (b <= ofs || b + 2 >= max) return 0;
+ // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
+
+ // Skip headers
+ h1 = h2 = b + 2;
+ for (;;) {
+ while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
+ if (h2 == h1) break;
+ if (h2 + 2 >= max) return 0;
+ // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
+ if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
+ mg_ncasecmp(&s[h1], cd.buf, cd.len) == 0) {
+ struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
+ part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
+ part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
+ }
+ h1 = h2 = h2 + 2;
+ }
+ b1 = b2 = h2 + 2;
+ while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
+ memcmp(&s[b2 + 2], s, b - ofs) == 0))
+ b2++;
+
+ if (b2 + 2 >= max) return 0;
+ if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
+ // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
+ return b2 + 2;
+}
+
+void mg_http_bauth(struct mg_connection *c, const char *user,
+ const char *pass) {
+ struct mg_str u = mg_str(user), p = mg_str(pass);
+ size_t need = c->send.len + 36 + (u.len + p.len) * 2;
+ if (c->send.size < need) mg_iobuf_resize(&c->send, need);
+ if (c->send.size >= need) {
+ size_t i, n = 0;
+ char *buf = (char *) &c->send.buf[c->send.len];
+ memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send!
+ for (i = 0; i < u.len; i++) {
+ n = mg_base64_update(((unsigned char *) u.buf)[i], buf + 21, n);
+ }
+ if (p.len > 0) {
+ n = mg_base64_update(':', buf + 21, n);
+ for (i = 0; i < p.len; i++) {
+ n = mg_base64_update(((unsigned char *) p.buf)[i], buf + 21, n);
+ }
+ }
+ n = mg_base64_final(buf + 21, n);
+ c->send.len += 21 + (size_t) n + 2;
+ memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2);
+ } else {
+ MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need));
+ }
+}
+
+struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) {
+ struct mg_str entry, k, v, result = mg_str_n(NULL, 0);
+ while (mg_span(buf, &entry, &buf, '&')) {
+ if (mg_span(entry, &k, &v, '=') && name.len == k.len &&
+ mg_ncasecmp(name.buf, k.buf, k.len) == 0) {
+ result = v;
+ break;
+ }
+ }
+ return result;
+}
+
+int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
+ size_t dst_len) {
+ int len;
+ if (dst != NULL && dst_len > 0) {
+ dst[0] = '\0'; // If destination buffer is valid, always nul-terminate it
+ }
+ if (dst == NULL || dst_len == 0) {
+ len = -2; // Bad destination
+ } else if (buf->buf == NULL || name == NULL || buf->len == 0) {
+ len = -1; // Bad source
+ } else {
+ struct mg_str v = mg_http_var(*buf, mg_str(name));
+ if (v.buf == NULL) {
+ len = -4; // Name does not exist
+ } else {
+ len = mg_url_decode(v.buf, v.len, dst, dst_len, 1);
+ if (len < 0) len = -3; // Failed to decode
+ }
+ }
+ return len;
+}
+
+static bool isx(int c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F');
+}
+
+int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
+ int is_form_url_encoded) {
+ size_t i, j;
+ for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) {
+ if (src[i] == '%') {
+ // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len
+ if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) {
+ mg_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t));
+ i += 2;
+ } else {
+ return -1;
+ }
+ } else if (is_form_url_encoded && src[i] == '+') {
+ dst[j] = ' ';
+ } else {
+ dst[j] = src[i];
+ }
+ }
+ if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
+ return i >= src_len && j < dst_len ? (int) j : -1;
+}
+
+static bool isok(uint8_t c) {
+ return c == '\n' || c == '\r' || c >= ' ';
+}
+
+int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
+ size_t i;
+ for (i = 0; i < buf_len; i++) {
+ if (!isok(buf[i])) return -1;
+ if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') ||
+ (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n'))
+ return (int) i + 1;
+ }
+ return 0;
+}
+struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) {
+ size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]);
+ for (i = 0; i < max && h->headers[i].name.len > 0; i++) {
+ struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value;
+ if (n == k->len && mg_ncasecmp(k->buf, name, n) == 0) return v;
+ }
+ return NULL;
+}
+
+// Is it a valid utf-8 continuation byte
+static bool vcb(uint8_t c) {
+ return (c & 0xc0) == 0x80;
+}
+
+// Get character length (valid utf-8). Used to parse method, URI, headers
+static size_t clen(const char *s, const char *end) {
+ const unsigned char *u = (unsigned char *) s, c = *u;
+ long n = (long) (end - s);
+ if (c > ' ' && c < '~') return 1; // Usual ascii printed char
+ if ((c & 0xe0) == 0xc0 && n > 1 && vcb(u[1])) return 2; // 2-byte UTF8
+ if ((c & 0xf0) == 0xe0 && n > 2 && vcb(u[1]) && vcb(u[2])) return 3;
+ if ((c & 0xf8) == 0xf0 && n > 3 && vcb(u[1]) && vcb(u[2]) && vcb(u[3]))
+ return 4;
+ return 0;
+}
+
+// Skip until the newline. Return advanced `s`, or NULL on error
+static const char *skiptorn(const char *s, const char *end, struct mg_str *v) {
+ v->buf = (char *) s;
+ while (s < end && s[0] != '\n' && s[0] != '\r') s++, v->len++; // To newline
+ if (s >= end || (s[0] == '\r' && s[1] != '\n')) return NULL; // Stray \r
+ if (s < end && s[0] == '\r') s++; // Skip \r
+ if (s >= end || *s++ != '\n') return NULL; // Skip \n
+ return s;
+}
+
+static bool mg_http_parse_headers(const char *s, const char *end,
+ struct mg_http_header *h, size_t max_hdrs) {
+ size_t i, n;
+ for (i = 0; i < max_hdrs; i++) {
+ struct mg_str k = {NULL, 0}, v = {NULL, 0};
+ if (s >= end) return false;
+ if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) break;
+ k.buf = (char *) s;
+ while (s < end && s[0] != ':' && (n = clen(s, end)) > 0) s += n, k.len += n;
+ if (k.len == 0) return false; // Empty name
+ 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
+ if ((s = skiptorn(s, end, &v)) == NULL) return false;
+ while (v.len > 0 && v.buf[v.len - 1] == ' ') 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
+ }
+ return true;
+}
+
+int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
+ int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len);
+ const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL
+ const struct mg_str *cl;
+ size_t n;
+
+ memset(hm, 0, sizeof(*hm));
+ if (req_len <= 0) return req_len;
+
+ hm->message.buf = hm->head.buf = (char *) s;
+ hm->body.buf = (char *) end;
+ hm->head.len = (size_t) req_len;
+ hm->message.len = hm->body.len = (size_t) -1; // Set body length to infinite
+
+ // Parse request line
+ hm->method.buf = (char *) s;
+ while (s < end && (n = clen(s, end)) > 0) s += n, hm->method.len += n;
+ while (s < end && s[0] == ' ') s++; // Skip spaces
+ hm->uri.buf = (char *) s;
+ while (s < end && (n = clen(s, end)) > 0) s += n, hm->uri.len += n;
+ while (s < end && s[0] == ' ') s++; // Skip spaces
+ if ((s = skiptorn(s, end, &hm->proto)) == NULL) return false;
+
+ // If URI contains '?' character, setup query string
+ if ((qs = (const char *) memchr(hm->uri.buf, '?', hm->uri.len)) != NULL) {
+ hm->query.buf = (char *) qs + 1;
+ hm->query.len = (size_t) (&hm->uri.buf[hm->uri.len] - (qs + 1));
+ hm->uri.len = (size_t) (qs - hm->uri.buf);
+ }
+
+ // Sanity check. Allow protocol/reason to be empty
+ // Do this check after hm->method.len and hm->uri.len are finalised
+ if (hm->method.len == 0 || hm->uri.len == 0) return -1;
+
+ if (!mg_http_parse_headers(s, end, hm->headers,
+ sizeof(hm->headers) / sizeof(hm->headers[0])))
+ return -1; // error when parsing
+ if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
+ if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
+ hm->message.len = (size_t) req_len + hm->body.len;
+ }
+
+ // mg_http_parse() is used to parse both HTTP requests and HTTP
+ // responses. If HTTP response does not have Content-Length set, then
+ // body is read until socket is closed, i.e. body.len is infinite (~0).
+ //
+ // For HTTP requests though, according to
+ // http://tools.ietf.org/html/rfc7231#section-8.1.3,
+ // only POST and PUT methods have defined body semantics.
+ // Therefore, if Content-Length is not specified and methods are
+ // not one of PUT or POST, set body length to 0.
+ //
+ // So, if it is HTTP request, and Content-Length is not set,
+ // and method is not (PUT or POST) then reset body length to zero.
+ is_response = mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0;
+ if (hm->body.len == (size_t) ~0 && !is_response &&
+ mg_strcasecmp(hm->method, mg_str("PUT")) != 0 &&
+ mg_strcasecmp(hm->method, mg_str("POST")) != 0) {
+ hm->body.len = 0;
+ hm->message.len = (size_t) req_len;
+ }
+
+ // The 204 (No content) responses also have 0 body length
+ if (hm->body.len == (size_t) ~0 && is_response &&
+ mg_strcasecmp(hm->uri, mg_str("204")) == 0) {
+ hm->body.len = 0;
+ hm->message.len = (size_t) req_len;
+ }
+ if (hm->message.len < (size_t) req_len) return -1; // Overflow protection
+
+ return req_len;
+}
+
+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);
+ 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);
+}
+
+void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_http_vprintf_chunk(c, fmt, &ap);
+ va_end(ap);
+}
+
+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 (len == 0) c->is_resp = 0;
+}
+
+// clang-format off
+static const char *mg_http_status_code_str(int status_code) {
+ switch (status_code) {
+ case 100: return "Continue";
+ case 101: return "Switching Protocols";
+ case 102: return "Processing";
+ case 200: return "OK";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 203: return "Non-authoritative Information";
+ case 204: return "No Content";
+ case 205: return "Reset Content";
+ case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 208: return "Already Reported";
+ case 226: return "IM Used";
+ case 300: return "Multiple Choices";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 303: return "See Other";
+ case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 307: return "Temporary Redirect";
+ case 308: return "Permanent Redirect";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 402: return "Payment Required";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
+ case 413: return "Payload Too Large";
+ case 414: return "Request-URI Too Long";
+ case 415: return "Unsupported Media Type";
+ case 416: return "Requested Range Not Satisfiable";
+ case 417: return "Expectation Failed";
+ case 418: return "I'm a teapot";
+ case 421: return "Misdirected Request";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 426: return "Upgrade Required";
+ case 428: return "Precondition Required";
+ case 429: return "Too Many Requests";
+ case 431: return "Request Header Fields Too Large";
+ case 444: return "Connection Closed Without Response";
+ case 451: return "Unavailable For Legal Reasons";
+ case 499: return "Client Closed Request";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
+ case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "HTTP Version Not Supported";
+ case 506: return "Variant Also Negotiates";
+ case 507: return "Insufficient Storage";
+ case 508: return "Loop Detected";
+ case 510: return "Not Extended";
+ case 511: return "Network Authentication Required";
+ case 599: return "Network Connect Timeout Error";
+ default: return "";
+ }
+}
+// clang-format on
+
+void mg_http_reply(struct mg_connection *c, int code, const char *headers,
+ const char *fmt, ...) {
+ va_list ap;
+ size_t len;
+ mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code,
+ mg_http_status_code_str(code), headers == NULL ? "" : headers);
+ len = c->send.len;
+ va_start(ap, fmt);
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
+ va_end(ap);
+ if (c->send.len > 16) {
+ size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu",
+ (unsigned long) (c->send.len - len));
+ c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
+ }
+ c->is_resp = 0;
+}
+
+static void http_cb(struct mg_connection *, int, void *);
+static void restore_http_cb(struct mg_connection *c) {
+ mg_fs_close((struct mg_fd *) c->pfn_data);
+ c->pfn_data = NULL;
+ c->pfn = http_cb;
+ c->is_resp = 0;
+}
+
+char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
+char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
+ mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size);
+ return buf;
+}
+
+static void static_cb(struct mg_connection *c, int ev, void *ev_data) {
+ if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
+ struct mg_fd *fd = (struct mg_fd *) c->pfn_data;
+ // Read to send IO buffer directly, avoid extra on-stack buffer
+ size_t n, max = MG_IO_SIZE, space;
+ size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
+ sizeof(size_t) * sizeof(size_t)];
+ if (c->send.size < max) mg_iobuf_resize(&c->send, max);
+ if (c->send.len >= c->send.size) return; // Rate limit
+ if ((space = c->send.size - c->send.len) > *cl) space = *cl;
+ n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space);
+ c->send.len += n;
+ *cl -= n;
+ if (n == 0) restore_http_cb(c);
+ } else if (ev == MG_EV_CLOSE) {
+ restore_http_cb(c);
+ }
+ (void) ev_data;
+}
+
+// Known mime types. Keep it outside guess_content_type() function, since
+// some environments don't like it defined there.
+// clang-format off
+#define MG_C_STR(a) { (char *) (a), sizeof(a) - 1 }
+static struct mg_str s_known_types[] = {
+ MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"),
+ MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"),
+ MG_C_STR("gif"), MG_C_STR("image/gif"),
+ MG_C_STR("png"), MG_C_STR("image/png"),
+ MG_C_STR("jpg"), MG_C_STR("image/jpeg"),
+ MG_C_STR("jpeg"), MG_C_STR("image/jpeg"),
+ MG_C_STR("woff"), MG_C_STR("font/woff"),
+ MG_C_STR("ttf"), MG_C_STR("font/ttf"),
+ MG_C_STR("svg"), MG_C_STR("image/svg+xml"),
+ MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"),
+ MG_C_STR("avi"), MG_C_STR("video/x-msvideo"),
+ MG_C_STR("csv"), MG_C_STR("text/csv"),
+ MG_C_STR("doc"), MG_C_STR("application/msword"),
+ MG_C_STR("exe"), MG_C_STR("application/octet-stream"),
+ MG_C_STR("gz"), MG_C_STR("application/gzip"),
+ MG_C_STR("ico"), MG_C_STR("image/x-icon"),
+ MG_C_STR("json"), MG_C_STR("application/json"),
+ MG_C_STR("mov"), MG_C_STR("video/quicktime"),
+ MG_C_STR("mp3"), MG_C_STR("audio/mpeg"),
+ MG_C_STR("mp4"), MG_C_STR("video/mp4"),
+ MG_C_STR("mpeg"), MG_C_STR("video/mpeg"),
+ MG_C_STR("pdf"), MG_C_STR("application/pdf"),
+ MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"),
+ MG_C_STR("tgz"), MG_C_STR("application/tar-gz"),
+ MG_C_STR("wav"), MG_C_STR("audio/wav"),
+ MG_C_STR("webp"), MG_C_STR("image/webp"),
+ MG_C_STR("zip"), MG_C_STR("application/zip"),
+ MG_C_STR("3gp"), MG_C_STR("video/3gpp"),
+ {0, 0},
+};
+// 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);
+ size_t i = 0;
+
+ // Shrink path to its extension only
+ while (i < path.len && path.buf[path.len - i - 1] != '.') i++;
+ path.buf += path.len - i;
+ path.len = i;
+
+ // 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;
+ }
+
+ // Process built-in mime types
+ for (i = 0; s_known_types[i].buf != NULL; i += 2) {
+ if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1];
+ }
+
+ return mg_str("text/plain; charset=utf-8");
+}
+
+static int getrange(struct mg_str *s, size_t *a, size_t *b) {
+ size_t i, numparsed = 0;
+ for (i = 0; i + 6 < s->len; i++) {
+ struct mg_str k, v = mg_str_n(s->buf + i + 6, s->len - i - 6);
+ if (memcmp(&s->buf[i], "bytes=", 6) != 0) continue;
+ if (mg_span(v, &k, &v, '-')) {
+ if (mg_to_size_t(k, a)) numparsed++;
+ if (v.len > 0 && mg_to_size_t(v, b)) numparsed++;
+ } else {
+ if (mg_to_size_t(v, a)) numparsed++;
+ }
+ break;
+ }
+ return (int) numparsed;
+}
+
+void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
+ const char *path,
+ const struct mg_http_serve_opts *opts) {
+ char etag[64], tmp[MG_PATH_MAX];
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct mg_fd *fd = NULL;
+ size_t size = 0;
+ time_t mtime = 0;
+ struct mg_str *inm = NULL;
+ struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
+ bool gzip = false;
+
+ if (path != NULL) {
+ // If a browser sends us "Accept-Encoding: gzip", try to open .gz first
+ struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
+ if (ae != NULL) {
+ char *ae_ = mg_mprintf("%.*s", ae->len, ae->buf);
+ if (ae_ != NULL && strstr(ae_, "gzip") != NULL) {
+ mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
+ fd = mg_fs_open(fs, tmp, MG_FS_READ);
+ if (fd != NULL) gzip = true, path = tmp;
+ }
+ free(ae_);
+ }
+ // No luck opening .gz? Open what we've told to open
+ if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
+ }
+
+ // Failed to open, and page404 is configured? Open it, then
+ if (fd == NULL && opts->page404 != NULL) {
+ fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
+ path = opts->page404;
+ mime = guess_content_type(mg_str(path), opts->mime_types);
+ }
+
+ if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
+ mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
+ mg_fs_close(fd);
+ // NOTE: mg_http_etag() call should go first!
+ } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
+ (inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
+ mg_strcasecmp(*inm, mg_str(etag)) == 0) {
+ mg_fs_close(fd);
+ mg_http_reply(c, 304, opts->extra_headers, "");
+ } else {
+ int n, status = 200;
+ char range[100];
+ size_t r1 = 0, r2 = 0, cl = size;
+
+ // Handle Range header
+ struct mg_str *rh = mg_http_get_header(hm, "Range");
+ range[0] = '\0';
+ if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) {
+ // If range is specified like "400-", set second limit to content len
+ if (n == 1) r2 = cl - 1;
+ if (r1 > r2 || r2 >= cl) {
+ status = 416;
+ cl = 0;
+ mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n",
+ (int64_t) size);
+ } else {
+ status = 206;
+ cl = r2 - r1 + 1;
+ mg_snprintf(range, sizeof(range),
+ "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1,
+ (uint64_t) (r1 + cl - 1), (uint64_t) size);
+ fs->sk(fd->fd, r1);
+ }
+ }
+ mg_printf(c,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: %.*s\r\n"
+ "Etag: %s\r\n"
+ "Content-Length: %llu\r\n"
+ "%s%s%s\r\n",
+ status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
+ 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 {
+ // Track to-be-sent content length at the end of c->data, aligned
+ size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
+ sizeof(size_t) * sizeof(size_t)];
+ c->pfn = static_cb;
+ c->pfn_data = fd;
+ *clp = cl;
+ }
+ }
+}
+
+struct printdirentrydata {
+ struct mg_connection *c;
+ struct mg_http_message *hm;
+ const struct mg_http_serve_opts *opts;
+ const char *dir;
+};
+
+#if MG_ENABLE_DIRLIST
+static void printdirentry(const char *name, void *userdata) {
+ struct printdirentrydata *d = (struct printdirentrydata *) userdata;
+ struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
+ size_t size = 0;
+ time_t t = 0;
+ char path[MG_PATH_MAX], sz[40], mod[40];
+ int flags, n = 0;
+
+ // MG_DEBUG(("[%s] [%s]", d->dir, name));
+ if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
+ sizeof(path)) {
+ MG_ERROR(("%s truncated", name));
+ } else if ((flags = fs->st(path, &size, &t)) == 0) {
+ MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno));
+ } else {
+ const char *slash = flags & MG_FS_DIR ? "/" : "";
+ if (flags & MG_FS_DIR) {
+ mg_snprintf(sz, sizeof(sz), "%s", "[DIR]");
+ } else {
+ mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size);
+ }
+#if defined(MG_HTTP_DIRLIST_TIME_FMT)
+ {
+ char time_str[40];
+ struct tm *time_info = localtime(&t);
+ strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
+ mg_snprintf(mod, sizeof(mod), "%s", time_str);
+ }
+#else
+ mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t);
+#endif
+ n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
+ mg_printf(d->c,
+ " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
+ "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
+ n, path, slash, name, slash, (unsigned long) t, mod,
+ flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
+ }
+}
+
+static void listdir(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts, char *dir) {
+ const char *sort_js_code =
+ "<script>function srt(tb, sc, so, d) {"
+ "var tr = Array.prototype.slice.call(tb.rows, 0),"
+ "tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
+ "n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
+ "t1 = a.cells[2].getAttribute('name'), "
+ "t2 = b.cells[2].getAttribute('name'); "
+ "return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
+ "n1 ? parseInt(n2) - parseInt(n1) : "
+ "c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
+ const char *sort_js_code2 =
+ "for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
+ "if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
+ "};"
+ "window.onload = function() {"
+ "var tb = document.getElementById('tb');"
+ "var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
+ "var sc = m[1], so = m[2]; document.onclick = function(ev) { "
+ "var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
+ "sc = c; ev.preventDefault();}};"
+ "srt(tb, sc, so, true);"
+ "}"
+ "</script>";
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct printdirentrydata d = {c, hm, opts, dir};
+ char tmp[10], buf[MG_PATH_MAX];
+ size_t off, n;
+ int len = mg_url_decode(hm->uri.buf, hm->uri.len, buf, sizeof(buf), 0);
+ struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
+
+ mg_printf(c,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "%s"
+ "Content-Length: \r\n\r\n",
+ opts->extra_headers == NULL ? "" : opts->extra_headers);
+ off = c->send.len; // Start of body
+ mg_printf(c,
+ "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
+ "<style>th,td {text-align: left; padding-right: 1em; "
+ "font-family: monospace; }</style></head>"
+ "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
+ "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
+ "<a href=\"#\" rel=\"1\">Modified</a></th>"
+ "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
+ "<tr><td colspan=\"3\"><hr></td></tr>"
+ "</thead>"
+ "<tbody id=\"tb\">\n",
+ (int) uri.len, uri.buf, sort_js_code, sort_js_code2, (int) uri.len,
+ uri.buf);
+ mg_printf(c, "%s",
+ " <tr><td><a href=\"..\">..</a></td>"
+ "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
+
+ fs->ls(dir, printdirentry, &d);
+ mg_printf(c,
+ "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
+ "</table><address>Mongoose v.%s</address></body></html>\n",
+ MG_VERSION);
+ n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
+ if (n > sizeof(tmp)) n = 0;
+ memcpy(c->send.buf + off - 12, tmp, n); // Set content length
+ c->is_resp = 0; // Mark response end
+}
+#endif
+
+// Resolve requested file into `path` and return its fs->st() result
+static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
+ struct mg_fs *fs, struct mg_str url, struct mg_str dir,
+ char *path, size_t path_size) {
+ int flags, tmp;
+ // Append URI to the root_dir, and sanitize it
+ size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.buf);
+ if (n + 2 >= path_size) {
+ mg_http_reply(c, 400, "", "Exceeded path size");
+ return -1;
+ }
+ path[path_size - 1] = '\0';
+ // Terminate root dir with slash
+ if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0';
+ if (url.len < hm->uri.len) {
+ mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n,
+ path_size - n, 0);
+ }
+ path[path_size - 1] = '\0'; // Double-check
+ if (!mg_path_is_sane(mg_str_n(path, path_size))) {
+ mg_http_reply(c, 400, "", "Invalid path");
+ return -1;
+ }
+ n = strlen(path);
+ while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
+ flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
+ : fs->st(path, NULL, NULL);
+ MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
+ flags));
+ if (flags == 0) {
+ // Do nothing - let's caller decide
+ } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
+ hm->uri.buf[hm->uri.len - 1] != '/') {
+ mg_printf(c,
+ "HTTP/1.1 301 Moved\r\n"
+ "Location: %.*s/\r\n"
+ "Content-Length: 0\r\n"
"\r\n",
- (int) hm->uri.len, hm->uri.ptr);
+ (int) hm->uri.len, hm->uri.buf);
c->is_resp = 0;
flags = -1;
} else if (flags & MG_FS_DIR) {
@@ -2057,3318 +3036,11754 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
path[n + 1 + strlen(MG_HTTP_INDEX)] =
'\0'; // Remove appended .gz in index file name
} else {
- path[n] = '\0'; // Remove appended index file name
+ path[n] = '\0'; // Remove appended index file name
+ }
+ }
+ return flags;
+}
+
+static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts, char *path,
+ size_t path_size) {
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
+ struct mg_str k, v, part, s = mg_str(opts->root_dir), u = {NULL, 0}, p = u;
+ while (mg_span(s, &part, &s, ',')) {
+ if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0);
+ if (v.len == 0) v = k, k = mg_str("/"), u = k, p = v;
+ if (hm->uri.len < k.len) continue;
+ if (mg_strcmp(k, mg_str_n(hm->uri.buf, k.len)) != 0) continue;
+ u = k, p = v;
+ }
+ return uri_to_path2(c, hm, fs, u, p, path, path_size);
+}
+
+void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
+ const struct mg_http_serve_opts *opts) {
+ char path[MG_PATH_MAX];
+ const char *sp = opts->ssi_pattern;
+ int flags = uri_to_path(c, hm, opts, path, sizeof(path));
+ if (flags < 0) {
+ // Do nothing: the response has already been sent by uri_to_path()
+ } else if (flags & MG_FS_DIR) {
+#if MG_ENABLE_DIRLIST
+ listdir(c, hm, opts, path);
+#else
+ mg_http_reply(c, 403, "", "Forbidden\n");
+#endif
+ } else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
+ mg_http_serve_ssi(c, opts->root_dir, path);
+ } else {
+ mg_http_serve_file(c, hm, path, opts);
+ }
+}
+
+static bool mg_is_url_safe(int c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
+}
+
+size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
+ size_t i, n = 0;
+ for (i = 0; i < sl; i++) {
+ int c = *(unsigned char *) &s[i];
+ if (n + 4 >= len) return 0;
+ if (mg_is_url_safe(c)) {
+ buf[n++] = s[i];
+ } else {
+ mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]);
+ n += 3;
+ }
+ }
+ if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
+ if (len > 0) buf[len - 1] = '\0'; // Always.
+ return n;
+}
+
+void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen,
+ char *pass, size_t passlen) {
+ struct mg_str *v = mg_http_get_header(hm, "Authorization");
+ user[0] = pass[0] = '\0';
+ if (v != NULL && v->len > 6 && memcmp(v->buf, "Basic ", 6) == 0) {
+ char buf[256];
+ size_t n = mg_base64_decode(v->buf + 6, v->len - 6, buf, sizeof(buf));
+ const char *p = (const char *) memchr(buf, ':', n > 0 ? n : 0);
+ if (p != NULL) {
+ mg_snprintf(user, userlen, "%.*s", p - buf, buf);
+ mg_snprintf(pass, passlen, "%.*s", n - (size_t) (p - buf) - 1, p + 1);
+ }
+ } else if (v != NULL && v->len > 7 && memcmp(v->buf, "Bearer ", 7) == 0) {
+ mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->buf + 7);
+ } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
+ struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
+ if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.buf);
+ } else {
+ mg_http_get_var(&hm->query, "access_token", pass, passlen);
+ }
+}
+
+static struct mg_str stripquotes(struct mg_str s) {
+ return s.len > 1 && s.buf[0] == '"' && s.buf[s.len - 1] == '"'
+ ? mg_str_n(s.buf + 1, s.len - 2)
+ : s;
+}
+
+struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
+ size_t i;
+ for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) {
+ if (s.buf[i + v.len] == '=' && memcmp(&s.buf[i], v.buf, v.len) == 0) {
+ const char *p = &s.buf[i + v.len + 1], *b = p, *x = &s.buf[s.len];
+ int q = p < x && *p == '"' ? 1 : 0;
+ while (p < x &&
+ (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ','))
+ p++;
+ // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.buf, (int) v.len,
+ // v.buf, (int) (p - b), b));
+ return stripquotes(mg_str_n(b, (size_t) (p - b + q)));
+ }
+ }
+ return mg_str_n(NULL, 0);
+}
+
+long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
+ struct mg_fs *fs, const char *dir, size_t max_size) {
+ char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX];
+ long res = 0, offset;
+ mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
+ mg_http_get_var(&hm->query, "file", file, sizeof(file));
+ offset = strtol(buf, NULL, 0);
+ mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file);
+ if (hm->body.len == 0) {
+ mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
+ } else if (file[0] == '\0') {
+ mg_http_reply(c, 400, "", "file required");
+ res = -1;
+ } else if (mg_path_is_sane(mg_str(file)) == false) {
+ mg_http_reply(c, 400, "", "%s: invalid file", file);
+ res = -2;
+ } else if (offset < 0) {
+ mg_http_reply(c, 400, "", "offset required");
+ res = -3;
+ } else if ((size_t) offset + hm->body.len > max_size) {
+ mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
+ (unsigned long) max_size);
+ res = -4;
+ } else {
+ struct mg_fd *fd;
+ size_t current_size = 0;
+ MG_DEBUG(("%s -> %lu bytes @ %ld", path, hm->body.len, offset));
+ if (offset == 0) fs->rm(path); // If offset if 0, truncate file
+ fs->st(path, ¤t_size, NULL);
+ if (offset > 0 && current_size != (size_t) offset) {
+ mg_http_reply(c, 400, "", "%s: offset mismatch", path);
+ res = -5;
+ } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
+ mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
+ res = -6;
+ } else {
+ res = offset + (long) fs->wr(fd->fd, hm->body.buf, hm->body.len);
+ mg_fs_close(fd);
+ mg_http_reply(c, 200, "", "%ld", res);
+ }
+ }
+ return res;
+}
+
+int mg_http_status(const struct mg_http_message *hm) {
+ return atoi(hm->uri.buf);
+}
+
+static bool is_hex_digit(int c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F');
+}
+
+static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
+ int i = 0, n = 0;
+ if (len < 3) return 0;
+ while (i < len && is_hex_digit(buf[i])) i++;
+ if (i == 0) return -1; // Error, no length specified
+ if (i > (int) sizeof(int) * 2) return -1; // Chunk length is too big
+ if (len < i + 1 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error
+ if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(int)) == false)
+ return -1; // Decode chunk length, overflow
+ if (n < 0) return -1; // Error. TODO(): some checks now redundant
+ if (n > len - i - 4) return 0; // Chunk not yet fully buffered
+ if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error
+ *pl = i + 2, *dl = n;
+ return i + 2 + n + 2;
+}
+
+static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
+ if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
+ struct mg_http_message hm;
+ size_t ofs = 0; // Parsing offset
+ while (c->is_resp == 0 && ofs < c->recv.len) {
+ const char *buf = (char *) c->recv.buf + ofs;
+ int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
+ struct mg_str *te; // Transfer - encoding header
+ bool is_chunked = false;
+ if (n < 0) {
+ // We don't use mg_error() here, to avoid closing pipelined requests
+ // prematurely, see #2592
+ MG_ERROR(("HTTP parse, %lu bytes", c->recv.len));
+ c->is_draining = 1;
+ mg_hexdump(buf, c->recv.len - ofs > 16 ? 16 : c->recv.len - ofs);
+ c->recv.len = 0;
+ return;
+ }
+ if (n == 0) break; // Request is not buffered yet
+ mg_call(c, MG_EV_HTTP_HDRS, &hm); // Got all HTTP headers
+ 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);
+ }
+ if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
+ if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
+ is_chunked = true;
+ } else {
+ mg_error(c, "Invalid Transfer-Encoding"); // See #2460
+ return;
+ }
+ } else if (mg_http_get_header(&hm, "Content-length") == NULL) {
+ // #2593: HTTP packets must contain either Transfer-Encoding or
+ // Content-length
+ bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0;
+ bool require_content_len = false;
+ if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 ||
+ mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) {
+ // POST and PUT should include an entity body. Therefore, they should
+ // 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;
+ } 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.
+ int status = mg_http_status(&hm);
+ require_content_len = status >= 200 && status != 204 && status != 304;
+ }
+ if (require_content_len) {
+ mg_http_reply(c, 411, "", "");
+ MG_ERROR(("%s", "Content length missing from request"));
+ }
+ }
+
+ if (is_chunked) {
+ // For chunked data, strip off prefixes and suffixes from chunks
+ // and relocate them right after the headers, then report a message
+ char *s = (char *) c->recv.buf + ofs + n;
+ int o = 0, pl, dl, cl, len = (int) (c->recv.len - ofs - (size_t) n);
+
+ // Find zero-length chunk (the end of the body)
+ while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0 && dl) o += cl;
+ if (cl == 0) break; // No zero-len chunk, buffer more data
+ if (cl < 0) {
+ mg_error(c, "Invalid chunk");
+ break;
+ }
+
+ // Zero chunk found. Second pass: strip + relocate
+ o = 0, hm.body.len = 0, hm.message.len = (size_t) n;
+ while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0) {
+ memmove(s + hm.body.len, s + o + pl, (size_t) dl);
+ o += cl, hm.body.len += (size_t) dl, hm.message.len += (size_t) dl;
+ if (dl == 0) break;
+ }
+ ofs += (size_t) (n + o);
+ } else { // Normal, non-chunked data
+ size_t len = c->recv.len - ofs - (size_t) n;
+ if (hm.body.len > len) break; // Buffer more data
+ ofs += (size_t) n + hm.body.len;
+ }
+
+ 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 (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data
+ }
+ (void) ev_data;
+}
+
+static void mg_hfn(struct mg_connection *c, int ev, void *ev_data) {
+ if (ev == MG_EV_HTTP_MSG) {
+ struct mg_http_message *hm = (struct mg_http_message *) ev_data;
+ if (mg_match(hm->uri, mg_str("/quit"), NULL)) {
+ mg_http_reply(c, 200, "", "ok\n");
+ c->is_draining = 1;
+ c->data[0] = 'X';
+ } else if (mg_match(hm->uri, mg_str("/debug"), NULL)) {
+ int level = (int) mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
+ mg_log_set(level);
+ mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
+ } else {
+ mg_http_reply(c, 200, "", "hi\n");
+ }
+ } else if (ev == MG_EV_CLOSE) {
+ if (c->data[0] == 'X') *(bool *) c->fn_data = true;
+ }
+}
+
+void mg_hello(const char *url) {
+ struct mg_mgr mgr;
+ bool done = false;
+ mg_mgr_init(&mgr);
+ if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) done = true;
+ while (done == false) mg_mgr_poll(&mgr, 100);
+ mg_mgr_free(&mgr);
+}
+
+struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = http_cb;
+ return c;
+}
+
+struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = http_cb;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/iobuf.c"
+#endif
+
+
+
+
+
+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;
+ new_size = roundup(new_size, io->align);
+ if (new_size == 0) {
+ mg_bzero(io->buf, io->size);
+ free(io->buf);
+ io->buf = NULL;
+ io->len = io->size = 0;
+ } else if (new_size != io->size) {
+ // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the
+ // porting to some obscure platforms like FreeRTOS
+ void *p = calloc(1, new_size);
+ if (p != NULL) {
+ size_t len = new_size < io->len ? new_size : io->len;
+ if (len > 0 && io->buf != NULL) memmove(p, io->buf, len);
+ mg_bzero(io->buf, io->size);
+ free(io->buf);
+ io->buf = (unsigned char *) p;
+ io->size = new_size;
+ } else {
+ ok = 0;
+ 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) {
+ io->buf = NULL;
+ io->align = align;
+ io->size = io->len = 0;
+ return mg_iobuf_resize(io, size);
+}
+
+size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf,
+ size_t len) {
+ size_t new_size = roundup(io->len + len, io->align);
+ mg_iobuf_resize(io, new_size); // Attempt to resize
+ if (new_size != io->size) len = 0; // Resize failure, append nothing
+ if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs);
+ if (buf != NULL) memmove(io->buf + ofs, buf, len);
+ if (ofs > io->len) io->len += ofs - io->len;
+ io->len += len;
+ return len;
+}
+
+size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) {
+ if (ofs > io->len) ofs = io->len;
+ if (ofs + len > io->len) len = io->len - ofs;
+ if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len);
+ if (io->buf) mg_bzero(io->buf + io->len - len, len);
+ io->len -= len;
+ return len;
+}
+
+void mg_iobuf_free(struct mg_iobuf *io) {
+ mg_iobuf_resize(io, 0);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/json.c"
+#endif
+
+
+
+
+static const char *escapeseq(int esc) {
+ return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\"";
+}
+
+static char json_esc(int c, int esc) {
+ const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc);
+ for (p = esc1; *p != '\0'; p++) {
+ if (*p == c) return esc2[p - esc1];
+ }
+ return 0;
+}
+
+static int mg_pass_string(const char *s, int len) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) {
+ i++;
+ } else if (s[i] == '\0') {
+ return MG_JSON_INVALID;
+ } else if (s[i] == '"') {
+ return i;
+ }
+ }
+ return MG_JSON_INVALID;
+}
+
+static double mg_atod(const char *p, int len, int *numlen) {
+ double d = 0.0;
+ int i = 0, sign = 1;
+
+ // Sign
+ if (i < len && *p == '-') {
+ sign = -1, i++;
+ } else if (i < len && *p == '+') {
+ i++;
+ }
+
+ // Decimal
+ for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
+ d *= 10.0;
+ d += p[i] - '0';
+ }
+ d *= sign;
+
+ // Fractional
+ if (i < len && p[i] == '.') {
+ double frac = 0.0, base = 0.1;
+ i++;
+ for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
+ frac += base * (p[i] - '0');
+ base /= 10.0;
+ }
+ d += frac * sign;
+ }
+
+ // Exponential
+ if (i < len && (p[i] == 'e' || p[i] == 'E')) {
+ int j, exp = 0, minus = 0;
+ i++;
+ if (i < len && p[i] == '-') minus = 1, i++;
+ if (i < len && p[i] == '+') i++;
+ while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
+ exp = exp * 10 + (p[i++] - '0');
+ if (minus) exp = -exp;
+ for (j = 0; j < exp; j++) d *= 10.0;
+ for (j = 0; j < -exp; j++) d /= 10.0;
+ }
+
+ if (numlen != NULL) *numlen = i;
+ return d;
+}
+
+// Iterate over object or array elements
+size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key,
+ struct mg_str *val) {
+ if (ofs >= obj.len) {
+ ofs = 0; // Out of boundaries, stop scanning
+ } else if (obj.len < 2 || (*obj.buf != '{' && *obj.buf != '[')) {
+ ofs = 0; // Not an array or object, stop
+ } else {
+ struct mg_str sub = mg_str_n(obj.buf + ofs, obj.len - ofs);
+ if (ofs == 0) ofs++, sub.buf++, sub.len--;
+ if (*obj.buf == '[') { // Iterate over an array
+ int n = 0, o = mg_json_get(sub, "$", &n);
+ if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
+ ofs = 0; // Error parsing key, stop scanning
+ } else {
+ if (key) *key = mg_str_n(NULL, 0);
+ if (val) *val = mg_str_n(sub.buf + o, (size_t) n);
+ ofs = (size_t) (&sub.buf[o + n] - obj.buf);
+ }
+ } else { // Iterate over an object
+ int n = 0, o = mg_json_get(sub, "$", &n);
+ if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
+ ofs = 0; // Error parsing key, stop scanning
+ } else {
+ if (key) *key = mg_str_n(sub.buf + o, (size_t) n);
+ sub.buf += o + n, sub.len -= (size_t) (o + n);
+ while (sub.len > 0 && *sub.buf != ':') sub.len--, sub.buf++;
+ if (sub.len > 0 && *sub.buf == ':') sub.len--, sub.buf++;
+ n = 0, o = mg_json_get(sub, "$", &n);
+ if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
+ ofs = 0; // Error parsing value, stop scanning
+ } else {
+ if (val) *val = mg_str_n(sub.buf + o, (size_t) n);
+ ofs = (size_t) (&sub.buf[o + n] - obj.buf);
+ }
+ }
+ }
+ // MG_INFO(("SUB ofs %u %.*s", ofs, sub.len, sub.buf));
+ while (ofs && ofs < obj.len &&
+ (obj.buf[ofs] == ' ' || obj.buf[ofs] == '\t' ||
+ obj.buf[ofs] == '\n' || obj.buf[ofs] == '\r')) {
+ ofs++;
+ }
+ if (ofs && ofs < obj.len && obj.buf[ofs] == ',') ofs++;
+ if (ofs > obj.len) ofs = 0;
+ }
+ return ofs;
+}
+
+int mg_json_get(struct mg_str json, const char *path, int *toklen) {
+ const char *s = json.buf;
+ int len = (int) json.len;
+ enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
+ unsigned char nesting[MG_JSON_MAX_DEPTH];
+ int i = 0; // Current offset in `s`
+ int j = 0; // Offset in `s` we're looking for (return value)
+ int depth = 0; // Current depth (nesting level)
+ int ed = 0; // Expected depth
+ int pos = 1; // Current position in `path`
+ int ci = -1, ei = -1; // Current and expected index in array
+
+ if (toklen) *toklen = 0;
+ if (path[0] != '$') return MG_JSON_INVALID;
+
+#define MG_CHECKRET(x) \
+ do { \
+ if (depth == ed && path[pos] == '\0' && ci == ei) { \
+ if (toklen) *toklen = i - j + 1; \
+ return j; \
+ } \
+ } while (0)
+
+// In the ascii table, the distance between `[` and `]` is 2.
+// Ditto for `{` and `}`. Hence +2 in the code below.
+#define MG_EOO(x) \
+ do { \
+ if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \
+ if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \
+ depth--; \
+ MG_CHECKRET(x); \
+ } while (0)
+
+ for (i = 0; i < len; i++) {
+ unsigned char c = ((unsigned char *) s)[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
+ switch (expecting) {
+ case S_VALUE:
+ // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei);
+ if (depth == ed) j = i;
+ if (c == '{') {
+ if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
+ if (depth == ed && path[pos] == '.' && ci == ei) {
+ // If we start the object, reset array indices
+ ed++, pos++, ci = ei = -1;
+ }
+ nesting[depth++] = c;
+ expecting = S_KEY;
+ break;
+ } else if (c == '[') {
+ if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
+ if (depth == ed && path[pos] == '[' && ei == ci) {
+ ed++, pos++, ci = 0;
+ for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) {
+ ei *= 10;
+ ei += path[pos] - '0';
+ }
+ if (path[pos] != 0) pos++;
+ }
+ nesting[depth++] = c;
+ break;
+ } else if (c == ']' && depth > 0) { // Empty array
+ MG_EOO(']');
+ } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
+ i += 3;
+ } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
+ i += 3;
+ } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
+ i += 4;
+ } else if (c == '-' || ((c >= '0' && c <= '9'))) {
+ int numlen = 0;
+ mg_atod(&s[i], len - i, &numlen);
+ i += numlen - 1;
+ } else if (c == '"') {
+ int n = mg_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ i += n + 1;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ MG_CHECKRET('V');
+ if (depth == ed && ei >= 0) ci++;
+ expecting = S_COMMA_OR_EOO;
+ break;
+
+ case S_KEY:
+ if (c == '"') {
+ int n = mg_pass_string(&s[i + 1], len - i - 1);
+ if (n < 0) return n;
+ if (i + 1 + n >= len) return MG_JSON_NOT_FOUND;
+ if (depth < ed) return MG_JSON_NOT_FOUND;
+ if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND;
+ // printf("K %s [%.*s] [%.*s] %d %d %d %d %d\n", path, pos, path, n,
+ // &s[i + 1], n, depth, ed, ci, ei);
+ // NOTE(cpq): in the check sequence below is important.
+ // strncmp() must go first: it fails fast if the remaining length
+ // of the path is smaller than `n`.
+ if (depth == ed && path[pos - 1] == '.' &&
+ strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 &&
+ (path[pos + n] == '\0' || path[pos + n] == '.' ||
+ path[pos + n] == '[')) {
+ pos += n;
+ }
+ i += n + 1;
+ expecting = S_COLON;
+ } else if (c == '}') { // Empty object
+ MG_EOO('}');
+ expecting = S_COMMA_OR_EOO;
+ if (depth == ed && ei >= 0) ci++;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+
+ case S_COLON:
+ if (c == ':') {
+ expecting = S_VALUE;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+
+ case S_COMMA_OR_EOO:
+ if (depth <= 0) {
+ return MG_JSON_INVALID;
+ } else if (c == ',') {
+ expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
+ } else if (c == ']' || c == '}') {
+ if (depth == ed && c == '}' && path[pos - 1] == '.')
+ return MG_JSON_NOT_FOUND;
+ if (depth == ed && c == ']' && path[pos - 1] == ',')
+ return MG_JSON_NOT_FOUND;
+ MG_EOO('O');
+ if (depth == ed && ei >= 0) ci++;
+ } else {
+ return MG_JSON_INVALID;
+ }
+ break;
+ }
+ }
+ return MG_JSON_NOT_FOUND;
+}
+
+struct mg_str mg_json_get_tok(struct mg_str json, const char *path) {
+ int len = 0, ofs = mg_json_get(json, path, &len);
+ return mg_str_n(ofs < 0 ? NULL : json.buf + ofs,
+ (size_t) (len < 0 ? 0 : len));
+}
+
+bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
+ int n, toklen, found = 0;
+ if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
+ (json.buf[n] == '-' || (json.buf[n] >= '0' && json.buf[n] <= '9'))) {
+ if (v != NULL) *v = mg_atod(json.buf + n, toklen, NULL);
+ found = 1;
+ }
+ return found;
+}
+
+bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
+ int found = 0, off = mg_json_get(json, path, NULL);
+ if (off >= 0 && (json.buf[off] == 't' || json.buf[off] == 'f')) {
+ if (v != NULL) *v = json.buf[off] == 't';
+ found = 1;
+ }
+ return found;
+}
+
+bool mg_json_unescape(struct mg_str s, char *to, size_t n) {
+ size_t i, j;
+ for (i = 0, j = 0; i < s.len && j < n; i++, j++) {
+ if (s.buf[i] == '\\' && i + 5 < s.len && s.buf[i + 1] == 'u') {
+ // \uXXXX escape. We process simple one-byte chars \u00xx within ASCII
+ // range. More complex chars would require dragging in a UTF8 library,
+ // which is too much for us
+ if (mg_str_to_num(mg_str_n(s.buf + i + 2, 4), 16, &to[j],
+ sizeof(uint8_t)) == false)
+ return false;
+ i += 5;
+ } else if (s.buf[i] == '\\' && i + 1 < s.len) {
+ char c = json_esc(s.buf[i + 1], 0);
+ if (c == 0) return false;
+ to[j] = c;
+ i++;
+ } else {
+ to[j] = s.buf[i];
+ }
+ }
+ if (j >= n) return false;
+ if (n > 0) to[j] = '\0';
+ return true;
+}
+
+char *mg_json_get_str(struct mg_str json, const char *path) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && len > 1 && json.buf[off] == '"') {
+ if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
+ !mg_json_unescape(mg_str_n(json.buf + off + 1, (size_t) (len - 2)),
+ result, (size_t) len)) {
+ free(result);
+ result = NULL;
+ }
+ }
+ return result;
+}
+
+char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && json.buf[off] == '"' && len > 1 &&
+ (result = (char *) calloc(1, (size_t) len)) != NULL) {
+ size_t k = mg_base64_decode(json.buf + off + 1, (size_t) (len - 2), result,
+ (size_t) len);
+ if (slen != NULL) *slen = (int) k;
+ }
+ return result;
+}
+
+char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
+ char *result = NULL;
+ int len = 0, off = mg_json_get(json, path, &len);
+ if (off >= 0 && json.buf[off] == '"' && len > 1 &&
+ (result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
+ int i;
+ for (i = 0; i < len - 2; i += 2) {
+ mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16, &result[i >> 1],
+ sizeof(uint8_t));
+ }
+ result[len / 2 - 1] = '\0';
+ if (slen != NULL) *slen = len / 2 - 1;
+ }
+ return result;
+}
+
+long mg_json_get_long(struct mg_str json, const char *path, long dflt) {
+ double dv;
+ long result = dflt;
+ if (mg_json_get_num(json, path, &dv)) result = (long) dv;
+ return result;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/log.c"
+#endif
+
+
+
+
+
+int mg_log_level = MG_LL_INFO;
+static mg_pfn_t s_log_func = mg_pfn_stdout;
+static void *s_log_func_param = NULL;
+
+void mg_log_set_fn(mg_pfn_t fn, void *param) {
+ s_log_func = fn;
+ s_log_func_param = param;
+}
+
+static void logc(unsigned char c) {
+ s_log_func((char) c, s_log_func_param);
+}
+
+static void logs(const char *buf, size_t len) {
+ size_t i;
+ for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]);
+}
+
+#if MG_ENABLE_CUSTOM_LOG
+// Let user define their own mg_log_prefix() and mg_log()
+#else
+void mg_log_prefix(int level, const char *file, int line, const char *fname) {
+ const char *p = strrchr(file, '/');
+ char buf[41];
+ size_t n;
+ if (p == NULL) p = strrchr(file, '\\');
+ n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level,
+ p == NULL ? file : p + 1, line, fname);
+ if (n > sizeof(buf) - 2) n = sizeof(buf) - 2;
+ while (n < sizeof(buf)) buf[n++] = ' ';
+ logs(buf, n - 1);
+}
+
+void mg_log(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap);
+ va_end(ap);
+ logs("\r\n", 2);
+}
+#endif
+
+static unsigned char nibble(unsigned c) {
+ return (unsigned char) (c < 10 ? c + '0' : c + 'W');
+}
+
+#define ISPRINT(x) ((x) >= ' ' && (x) <= '~')
+void mg_hexdump(const void *buf, size_t len) {
+ const unsigned char *p = (const unsigned char *) buf;
+ unsigned char ascii[16], alen = 0;
+ size_t i;
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0) {
+ // Print buffered ascii chars
+ if (i > 0) logs(" ", 2), logs((char *) ascii, 16), logc('\n'), alen = 0;
+ // Print hex address, then \t
+ logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)),
+ logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3);
+ }
+ logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5
+ logc(' '); // Space after hex number
+ ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf
+ }
+ while (alen < 16) logs(" ", 3), ascii[alen++] = ' ';
+ logs(" ", 2), logs((char *) ascii, 16), logc('\n');
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/md5.c"
+#endif
+
+
+
+// This code implements the MD5 message-digest algorithm.
+// The algorithm is due to Ron Rivest. This code was
+// written by Colin Plumb in 1993, no copyright is claimed.
+// This code is in the public domain; do with it what you wish.
+//
+// Equivalent code is available from RSA Data Security, Inc.
+// This code has been tested against that, and is equivalent,
+// except that you don't need to include two pages of legalese
+// with every copy.
+//
+// To compute the message digest of a chunk of bytes, declare an
+// MD5Context structure, pass it to MD5Init, call MD5Update as
+// needed on buffers full of bytes, and then call MD5Final, which
+// will fill a supplied 16-byte array with the digest.
+
+#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5
+
+static void mg_byte_reverse(unsigned char *buf, unsigned longs) {
+ if (MG_BIG_ENDIAN) {
+ do {
+ uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+ } else {
+ (void) buf, (void) longs; // Little endian. Do nothing
+ }
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+ (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void mg_md5_init(mg_md5_ctx *ctx) {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) {
+ uint32_t t;
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
+ ctx->bits[1] += (uint32_t) len >> 29;
+
+ t = (t >> 3) & 0x3f;
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ memcpy(ctx->in, buf, len);
+}
+
+void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
+ unsigned count;
+ unsigned char *p;
+ uint32_t *a;
+
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ p = ctx->in + count;
+ *p++ = 0x80;
+ count = 64 - 1 - count;
+ if (count < 8) {
+ memset(p, 0, count);
+ mg_byte_reverse(ctx->in, 16);
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ memset(ctx->in, 0, 56);
+ } else {
+ memset(p, 0, count - 8);
+ }
+ mg_byte_reverse(ctx->in, 14);
+
+ a = (uint32_t *) ctx->in;
+ a[14] = ctx->bits[0];
+ a[15] = ctx->bits[1];
+
+ mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ mg_byte_reverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(*ctx));
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/mqtt.c"
+#endif
+
+
+
+
+
+
+
+
+#define MQTT_CLEAN_SESSION 0x02
+#define MQTT_HAS_WILL 0x04
+#define MQTT_WILL_RETAIN 0x20
+#define MQTT_HAS_PASSWORD 0x40
+#define MQTT_HAS_USER_NAME 0x80
+
+struct mg_mqtt_pmap {
+ uint8_t id;
+ uint8_t type;
+};
+
+static const struct mg_mqtt_pmap s_prop_map[] = {
+ {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
+ {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT},
+ {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
+ {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING},
+ {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT},
+ {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE},
+ {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR},
+ {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT},
+ {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE},
+ {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) {
+ uint8_t buf[1 + sizeof(len)], *vlen = &buf[1];
+ buf[0] = (uint8_t) ((cmd << 4) | flags);
+ do {
+ *vlen = len % 0x80;
+ len /= 0x80;
+ if (len > 0) *vlen |= 0x80;
+ vlen++;
+ } while (len > 0 && vlen < &buf[sizeof(buf)]);
+ 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));
+}
+
+static void mg_send_u32(struct mg_connection *c, uint32_t value) {
+ mg_send(c, &value, sizeof(value));
+}
+
+static uint8_t varint_size(size_t length) {
+ uint8_t bytes_needed = 0;
+ do {
+ bytes_needed++;
+ length /= 0x80;
+ } while (length > 0);
+ return bytes_needed;
+}
+
+static size_t encode_varint(uint8_t *buf, size_t value) {
+ size_t len = 0;
+
+ do {
+ uint8_t b = (uint8_t) (value % 128);
+ value /= 128;
+ if (value > 0) b |= 0x80;
+ buf[len++] = b;
+ } while (value > 0);
+
+ return len;
+}
+
+static size_t decode_varint(const uint8_t *buf, size_t len, size_t *value) {
+ size_t multiplier = 1, offset;
+ *value = 0;
+
+ for (offset = 0; offset < 4 && offset < len; offset++) {
+ uint8_t encoded_byte = buf[offset];
+ *value += (encoded_byte & 0x7f) * multiplier;
+ multiplier *= 128;
+
+ if ((encoded_byte & 0x80) == 0) return offset + 1;
+ }
+
+ return 0;
+}
+
+static int mqtt_prop_type_by_id(uint8_t prop_id) {
+ size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]);
+ for (i = 0; i < num_properties; ++i) {
+ if (s_prop_map[i].id == prop_id) return s_prop_map[i].type;
+ }
+ return -1; // Property ID not found
+}
+
+// Returns the size of the properties section, without the
+// size of the content's length
+static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) {
+ size_t i, size = 0;
+ for (i = 0; i < count; i++) {
+ size++; // identifier
+ switch (mqtt_prop_type_by_id(props[i].id)) {
+ case MQTT_PROP_TYPE_STRING_PAIR:
+ size += (uint32_t) (props[i].val.len + props[i].key.len +
+ 2 * sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_STRING:
+ size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_BINARY_DATA:
+ size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ size += varint_size((uint32_t) props[i].iv);
+ break;
+ case MQTT_PROP_TYPE_INT:
+ size += (uint32_t) sizeof(uint32_t);
+ break;
+ case MQTT_PROP_TYPE_SHORT:
+ size += (uint32_t) sizeof(uint16_t);
+ break;
+ case MQTT_PROP_TYPE_BYTE:
+ size += (uint32_t) sizeof(uint8_t);
+ break;
+ default:
+ return size; // cannot parse further down
+ }
+ }
+
+ return size;
+}
+
+// returns the entire size of the properties section, including the
+// size of the variable length of the content
+static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) {
+ size_t size = get_properties_length(props, count);
+ size += varint_size(size);
+ return size;
+}
+
+static void 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);
+ for (i = 0; i < nprops; i++) {
+ mg_send(c, &props[i].id, sizeof(props[i].id));
+ 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);
+ break;
+ case MQTT_PROP_TYPE_BYTE:
+ mg_send(c, &props[i].iv, sizeof(uint8_t));
+ break;
+ case MQTT_PROP_TYPE_SHORT:
+ mg_send_u16(c, mg_htons((uint16_t) props[i].iv));
+ break;
+ case MQTT_PROP_TYPE_INT:
+ mg_send_u32(c, mg_htonl((uint32_t) props[i].iv));
+ 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);
+ 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);
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ len = encode_varint(buf_v, props[i].iv);
+ mg_send(c, buf_v, (size_t) len);
+ break;
+ }
+ }
+}
+
+size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
+ size_t ofs) {
+ uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs;
+ uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len;
+ size_t new_pos = ofs, len;
+ prop->id = i[0];
+
+ if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size)
+ return 0;
+ i++, new_pos++;
+
+ switch (mqtt_prop_type_by_id(prop->id)) {
+ case MQTT_PROP_TYPE_STRING_PAIR:
+ prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->key.buf = (char *) i + 2;
+ i += 2 + prop->key.len;
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.buf = (char *) i + 2;
+ new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len;
+ break;
+ case MQTT_PROP_TYPE_BYTE:
+ prop->iv = (uint8_t) i[0];
+ new_pos++;
+ break;
+ case MQTT_PROP_TYPE_SHORT:
+ prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ new_pos += sizeof(uint16_t);
+ break;
+ case MQTT_PROP_TYPE_INT:
+ prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) |
+ ((uint32_t) i[2] << 8) | i[3];
+ new_pos += sizeof(uint32_t);
+ break;
+ case MQTT_PROP_TYPE_STRING:
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.buf = (char *) i + 2;
+ new_pos += 2 + prop->val.len;
+ break;
+ case MQTT_PROP_TYPE_BINARY_DATA:
+ prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
+ prop->val.buf = (char *) i + 2;
+ new_pos += 2 + prop->val.len;
+ break;
+ case MQTT_PROP_TYPE_VARIABLE_INT:
+ len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv);
+ new_pos = (!len) ? 0 : new_pos + len;
+ break;
+ default:
+ new_pos = 0;
+ }
+
+ return new_pos;
+}
+
+void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ char client_id[21];
+ struct mg_str cid = opts->client_id;
+ size_t total_len = 7 + 1 + 2 + 2;
+ uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
+
+ if (cid.len == 0) {
+ mg_random_str(client_id, sizeof(client_id) - 1);
+ client_id[sizeof(client_id) - 1] = '\0';
+ cid = mg_str(client_id);
+ }
+
+ if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1)
+ c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag
+ hdr[7] = (uint8_t) ((opts->qos & 3) << 3); // Connection flags
+ if (opts->user.len > 0) {
+ total_len += 2 + (uint32_t) opts->user.len;
+ hdr[7] |= MQTT_HAS_USER_NAME;
+ }
+ if (opts->pass.len > 0) {
+ total_len += 2 + (uint32_t) opts->pass.len;
+ hdr[7] |= MQTT_HAS_PASSWORD;
+ }
+ if (opts->topic.len > 0) { // allow zero-length msgs, message.len is size_t
+ total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len;
+ hdr[7] |= MQTT_HAS_WILL;
+ }
+ if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION;
+ if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN;
+ total_len += (uint32_t) cid.len;
+ if (c->is_mqtt5) {
+ total_len += get_props_size(opts->props, opts->num_props);
+ if (hdr[7] & MQTT_HAS_WILL)
+ 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 (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+
+ mg_send_u16(c, mg_htons((uint16_t) cid.len));
+ mg_send(c, cid.buf, cid.len);
+
+ if (hdr[7] & MQTT_HAS_WILL) {
+ if (c->is_mqtt5)
+ mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props);
+
+ 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);
+ }
+}
+
+uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ uint16_t id = opts->retransmit_id;
+ uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0));
+ size_t len = 2 + opts->topic.len + opts->message.len;
+ MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) opts->topic.len,
+ (char *) opts->topic.buf, (int) opts->message.len,
+ (char *) opts->message.buf));
+ if (opts->qos > 0) len += 2;
+ 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 (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 (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props);
+
+ if (opts->message.len > 0) mg_send(c, opts->message.buf, opts->message.len);
+ return id;
+}
+
+void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
+ uint8_t qos_ = opts->qos & 3;
+ 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 (++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);
+
+ 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_));
+}
+
+int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
+ struct mg_mqtt_message *m) {
+ uint8_t lc = 0, *p, *end;
+ uint32_t n = 0, len_len = 0;
+
+ memset(m, 0, sizeof(*m));
+ m->dgram.buf = (char *) buf;
+ if (len < 2) return MQTT_INCOMPLETE;
+ m->cmd = (uint8_t) (buf[0] >> 4);
+ m->qos = (buf[0] >> 1) & 3;
+
+ n = len_len = 0;
+ p = (uint8_t *) buf + 1;
+ while ((size_t) (p - buf) < len) {
+ lc = *((uint8_t *) p++);
+ n += (uint32_t) ((lc & 0x7f) << 7 * len_len);
+ len_len++;
+ if (!(lc & 0x80)) break;
+ if (len_len >= 4) return MQTT_MALFORMED;
+ }
+ end = p + n;
+ if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE;
+ m->dgram.len = (size_t) (end - buf);
+
+ switch (m->cmd) {
+ case MQTT_CMD_CONNACK:
+ if (end - p < 2) return MQTT_MALFORMED;
+ m->ack = p[1];
+ break;
+ case MQTT_CMD_PUBACK:
+ case MQTT_CMD_PUBREC:
+ case MQTT_CMD_PUBREL:
+ case MQTT_CMD_PUBCOMP:
+ case MQTT_CMD_SUBSCRIBE:
+ case MQTT_CMD_SUBACK:
+ case MQTT_CMD_UNSUBSCRIBE:
+ case MQTT_CMD_UNSUBACK:
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ p += 2;
+ break;
+ case MQTT_CMD_PUBLISH: {
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ m->topic.buf = (char *) p + 2;
+ p += 2 + m->topic.len;
+ if (p > end) return MQTT_MALFORMED;
+ if (m->qos > 0) {
+ if (p + 2 > end) return MQTT_MALFORMED;
+ m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
+ p += 2;
+ }
+ if (p > end) return MQTT_MALFORMED;
+ if (version == 5 && p + 2 < end) {
+ len_len =
+ (uint32_t) decode_varint(p, (size_t) (end - p), &m->props_size);
+ if (!len_len) return MQTT_MALFORMED;
+ m->props_start = (size_t) (p + len_len - buf);
+ p += len_len + m->props_size;
+ }
+ if (p > end) return MQTT_MALFORMED;
+ m->data.buf = (char *) p;
+ m->data.len = (size_t) (end - p);
+ break;
+ }
+ default:
+ break;
+ }
+ return MQTT_OK;
+}
+
+static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) {
+ if (ev == MG_EV_READ) {
+ for (;;) {
+ uint8_t version = c->is_mqtt5 ? 5 : 4;
+ struct mg_mqtt_message mm;
+ int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm);
+ if (rc == MQTT_MALFORMED) {
+ MG_ERROR(("%lu MQTT malformed message", c->id));
+ c->is_closing = 1;
+ break;
+ } else if (rc == MQTT_OK) {
+ MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
+ (int) mm.dgram.len, (int) mm.data.len, mm.data.buf));
+ switch (mm.cmd) {
+ case MQTT_CMD_CONNACK:
+ mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
+ if (mm.ack == 0) {
+ MG_DEBUG(("%lu Connected", c->id));
+ } else {
+ MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack));
+ c->is_closing = 1;
+ }
+ break;
+ case MQTT_CMD_PUBLISH: {
+ /*MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) mm.topic.len,
+ mm.topic.buf, (int) mm.data.len, mm.data.buf));*/
+ if (mm.qos > 0) {
+ uint16_t id = mg_ntohs(mm.id);
+ 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 (c->is_mqtt5) {
+ uint16_t zero = 0;
+ mg_send(c, &zero, sizeof(zero));
+ }
+ }
+ mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff
+ break;
+ }
+ 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
+ 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));
+ break;
+ }
+ }
+ mg_call(c, MG_EV_MQTT_CMD, &mm);
+ mg_iobuf_del(&c->recv, 0, mm.dgram.len);
+ } else {
+ break;
+ }
+ }
+ }
+ (void) ev_data;
+}
+
+void mg_mqtt_ping(struct mg_connection *nc) {
+ mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0);
+}
+
+void mg_mqtt_pong(struct mg_connection *nc) {
+ mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0);
+}
+
+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 (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);
+ }
+}
+
+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(mgr, url, fn, fn_data);
+ if (c != NULL) {
+ struct mg_mqtt_opts empty;
+ memset(&empty, 0, sizeof(empty));
+ mg_mqtt_login(c, opts == NULL ? &empty : opts);
+ c->pfn = mqtt_cb;
+ }
+ return c;
+}
+
+struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
+ if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/net.c"
+#endif
+
+
+
+
+
+
+
+
+
+size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) {
+ size_t old = c->send.len;
+ mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
+ return c->send.len - old;
+}
+
+size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
+ size_t len = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ len = mg_vprintf(c, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
+ uint32_t localhost = mg_htonl(0x7f000001);
+ if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
+ memcpy(addr->ip, &localhost, sizeof(uint32_t));
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
+ if (str.len > 0) return false;
+ memset(addr->ip, 0, sizeof(addr->ip));
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
+ uint8_t data[4] = {0, 0, 0, 0};
+ size_t i, num_dots = 0;
+ for (i = 0; i < str.len; i++) {
+ if (str.buf[i] >= '0' && str.buf[i] <= '9') {
+ int octet = data[num_dots] * 10 + (str.buf[i] - '0');
+ if (octet > 255) return false;
+ data[num_dots] = (uint8_t) octet;
+ } else if (str.buf[i] == '.') {
+ if (num_dots >= 3 || i == 0 || str.buf[i - 1] == '.') return false;
+ num_dots++;
+ } else {
+ return false;
+ }
+ }
+ if (num_dots != 3 || str.buf[i - 1] == '.') return false;
+ memcpy(&addr->ip, data, sizeof(data));
+ addr->is_ip6 = false;
+ return true;
+}
+
+static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
+ int i;
+ uint32_t ipv4;
+ if (str.len < 14) return false;
+ if (str.buf[0] != ':' || str.buf[1] != ':' || str.buf[6] != ':') return false;
+ for (i = 2; i < 6; i++) {
+ if (str.buf[i] != 'f' && str.buf[i] != 'F') return false;
+ }
+ // struct mg_str s = mg_str_n(&str.buf[7], str.len - 7);
+ if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false;
+ memcpy(&ipv4, addr->ip, sizeof(ipv4));
+ memset(addr->ip, 0, sizeof(addr->ip));
+ addr->ip[10] = addr->ip[11] = 255;
+ memcpy(&addr->ip[12], &ipv4, 4);
+ addr->is_ip6 = true;
+ return true;
+}
+
+static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
+ size_t i, j = 0, n = 0, dc = 42;
+ addr->scope_id = 0;
+ if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2;
+ if (mg_v4mapped(str, addr)) return true;
+ for (i = 0; i < str.len; i++) {
+ 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
+ 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));
+ addr->ip[n] = (uint8_t) ((val >> 8) & 255);
+ addr->ip[n + 1] = (uint8_t) (val & 255);
+ } else if (str.buf[i] == ':') {
+ j = i + 1;
+ if (i > 0 && str.buf[i - 1] == ':') {
+ dc = n; // Double colon
+ if (i > 1 && str.buf[i - 2] == ':') return false;
+ } else if (i > 0) {
+ n += 2;
+ }
+ if (n > 14) return false;
+ addr->ip[n] = addr->ip[n + 1] = 0; // For trailing ::
+ } else if (str.buf[i] == '%') { // Scope ID, last in string
+ return mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10,
+ &addr->scope_id, sizeof(uint8_t));
+ } else {
+ return false;
+ }
+ }
+ if (n < 14 && dc == 42) return false;
+ if (n < 14) {
+ memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2);
+ memset(&addr->ip[dc], 0, 14 - n);
+ }
+
+ addr->is_ip6 = true;
+ return true;
+}
+
+bool mg_aton(struct mg_str str, struct mg_addr *addr) {
+ // MG_INFO(("[%.*s]", (int) str.len, str.buf));
+ return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
+ mg_aton6(str, addr);
+}
+
+struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
+ struct mg_connection *c =
+ (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize);
+ if (c != NULL) {
+ c->mgr = mgr;
+ c->send.align = c->recv.align = c->rtls.align = MG_IO_SIZE;
+ c->id = ++mgr->nextid;
+ MG_PROF_INIT(c);
+ }
+ return c;
+}
+
+void mg_close_conn(struct mg_connection *c) {
+ mg_resolve_cancel(c); // Close any pending DNS query
+ LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
+ if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
+ if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
+ // Order of operations is important. `MG_EV_CLOSE` event must be fired
+ // before we deallocate received data, see #1331
+ mg_call(c, MG_EV_CLOSE, NULL);
+ MG_DEBUG(("%lu %ld closed", c->id, c->fd));
+ MG_PROF_DUMP(c);
+ MG_PROF_FREE(c);
+
+ mg_tls_free(c);
+ mg_iobuf_free(&c->recv);
+ mg_iobuf_free(&c->send);
+ mg_iobuf_free(&c->rtls);
+ mg_bzero((unsigned char *) c, sizeof(*c));
+ free(c);
+}
+
+struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = NULL;
+ if (url == NULL || url[0] == '\0') {
+ MG_ERROR(("null url"));
+ } else if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("OOM"));
+ } else {
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->is_udp = (strncmp(url, "udp:", 4) == 0);
+ c->fd = (void *) (size_t) MG_INVALID_SOCKET;
+ c->fn = fn;
+ c->is_client = true;
+ c->fn_data = fn_data;
+ MG_DEBUG(("%lu %ld %s", c->id, c->fd, url));
+ mg_call(c, MG_EV_OPEN, (void *) url);
+ mg_resolve(c, url);
+ }
+ return c;
+}
+
+struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = NULL;
+ if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("OOM %s", url));
+ } else if (!mg_open_listener(c, url)) {
+ MG_ERROR(("Failed: %s, errno %d", url, errno));
+ MG_PROF_FREE(c);
+ free(c);
+ c = NULL;
+ } else {
+ c->is_listening = 1;
+ c->is_udp = strncmp(url, "udp:", 4) == 0;
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->fn = fn;
+ c->fn_data = fn_data;
+ mg_call(c, MG_EV_OPEN, NULL);
+ if (mg_url_is_ssl(url)) c->is_tls = 1; // Accepted connection must
+ MG_DEBUG(("%lu %ld %s", c->id, c->fd, url));
+ }
+ return c;
+}
+
+struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
+ mg_event_handler_t fn, void *fn_data) {
+ struct mg_connection *c = mg_alloc_conn(mgr);
+ if (c != NULL) {
+ c->fd = (void *) (size_t) fd;
+ c->fn = fn;
+ c->fn_data = fn_data;
+ MG_EPOLL_ADD(c);
+ mg_call(c, MG_EV_OPEN, NULL);
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ }
+ return c;
+}
+
+struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
+ unsigned flags, void (*fn)(void *), void *arg) {
+ struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
+ if (t != NULL) {
+ mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
+ t->id = mgr->timerid++;
+ }
+ return t;
+}
+
+long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
+ if (c->rtls.len == 0) return MG_IO_WAIT;
+ if (len > c->rtls.len) len = c->rtls.len;
+ memcpy(buf, c->rtls.buf, len);
+ mg_iobuf_del(&c->rtls, 0, len);
+ return (long) len;
+}
+
+void mg_mgr_free(struct mg_mgr *mgr) {
+ struct mg_connection *c;
+ struct mg_timer *tmp, *t = mgr->timers;
+ while (t != NULL) tmp = t->next, free(t), t = tmp;
+ mgr->timers = NULL; // Important. Next call to poll won't touch timers
+ for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
+ mg_mgr_poll(mgr, 0);
+#if MG_ENABLE_FREERTOS_TCP
+ FreeRTOS_DeleteSocketSet(mgr->ss);
+#endif
+ MG_DEBUG(("All connections closed"));
+#if MG_ENABLE_EPOLL
+ if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1;
+#endif
+ mg_tls_ctx_free(mgr);
+}
+
+void mg_mgr_init(struct mg_mgr *mgr) {
+ memset(mgr, 0, sizeof(*mgr));
+#if MG_ENABLE_EPOLL
+ if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
+ MG_ERROR(("epoll_create1 errno %d", errno));
+#else
+ mgr->epoll_fd = -1;
+#endif
+#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
+ // clang-format off
+ { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
+ // clang-format on
+#elif MG_ENABLE_FREERTOS_TCP
+ mgr->ss = FreeRTOS_CreateSocketSet();
+#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
+ // Ignore SIGPIPE signal, so if client cancels the request, it
+ // won't kill the whole process.
+ signal(SIGPIPE, SIG_IGN);
+#elif MG_ENABLE_TCPIP_DRIVER_INIT && defined(MG_TCPIP_DRIVER_INIT)
+ MG_TCPIP_DRIVER_INIT(mgr);
+#endif
+ mgr->pipe = MG_INVALID_SOCKET;
+ mgr->dnstimeout = 3000;
+ mgr->dns4.url = "udp://8.8.8.8:53";
+ mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
+ mg_tls_ctx_init(mgr);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/net_builtin.c"
+#endif
+
+
+#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP
+#define MG_EPHEMERAL_PORT_BASE 32768
+#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
+
+#ifndef MIP_TCP_KEEPALIVE_MS
+#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
+#endif
+
+#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
+#define MIP_TCP_ARP_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
+
+struct connstate {
+ uint32_t seq, ack; // TCP seq/ack counters
+ uint64_t timer; // TCP keep-alive / ACK timer
+ uint32_t acked; // Last ACK-ed number
+ size_t unacked; // Not acked bytes
+ uint8_t mac[6]; // Peer MAC address
+ 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
+#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response
+#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
+#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
+ uint8_t tmiss; // Number of keep-alive misses
+ struct mg_iobuf raw; // For TLS only. Incoming raw data
+};
+
+#pragma pack(push, 1)
+
+struct lcp {
+ uint8_t addr, ctrl, proto[2], code, id, len[2];
+};
+
+struct eth {
+ uint8_t dst[6]; // Destination MAC address
+ uint8_t src[6]; // Source MAC address
+ uint16_t type; // Ethernet type
+};
+
+struct ip {
+ uint8_t ver; // Version
+ uint8_t tos; // Unused
+ uint16_t len; // Length
+ uint16_t id; // Unused
+ uint16_t frag; // Fragmentation
+#define IP_FRAG_OFFSET_MSK 0xFF1F
+#define IP_MORE_FRAGS_MSK 0x20
+ uint8_t ttl; // Time to live
+ uint8_t proto; // Upper level protocol
+ uint16_t csum; // Checksum
+ uint32_t src; // Source IP
+ uint32_t dst; // Destination IP
+};
+
+struct ip6 {
+ uint8_t ver; // Version
+ uint8_t opts[3]; // Options
+ uint16_t len; // Length
+ uint8_t proto; // Upper level protocol
+ uint8_t ttl; // Time to live
+ uint8_t src[16]; // Source IP
+ uint8_t dst[16]; // Destination IP
+};
+
+struct icmp {
+ uint8_t type;
+ uint8_t code;
+ uint16_t csum;
+};
+
+struct arp {
+ uint16_t fmt; // Format of hardware address
+ uint16_t pro; // Format of protocol address
+ uint8_t hlen; // Length of hardware address
+ uint8_t plen; // Length of protocol address
+ uint16_t op; // Operation
+ uint8_t sha[6]; // Sender hardware address
+ uint32_t spa; // Sender protocol address
+ uint8_t tha[6]; // Target hardware address
+ uint32_t tpa; // Target protocol address
+};
+
+struct tcp {
+ uint16_t sport; // Source port
+ uint16_t dport; // Destination port
+ uint32_t seq; // Sequence number
+ uint32_t ack; // Acknowledgement number
+ uint8_t off; // Data offset
+ uint8_t flags; // TCP flags
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+#define TH_ECE 0x40
+#define TH_CWR 0x80
+ uint16_t win; // Window
+ uint16_t csum; // Checksum
+ uint16_t urp; // Urgent pointer
+};
+
+struct udp {
+ uint16_t sport; // Source port
+ uint16_t dport; // Destination port
+ uint16_t len; // UDP length
+ uint16_t csum; // UDP checksum
+};
+
+struct dhcp {
+ uint8_t op, htype, hlen, hops;
+ uint32_t xid;
+ uint16_t secs, flags;
+ uint32_t ciaddr, yiaddr, siaddr, giaddr;
+ uint8_t hwaddr[208];
+ uint32_t magic;
+ uint8_t options[32];
+};
+
+#pragma pack(pop)
+
+struct pkt {
+ struct mg_str raw; // Raw packet data
+ struct mg_str pay; // Payload data
+ struct eth *eth;
+ struct llc *llc;
+ struct arp *arp;
+ struct ip *ip;
+ struct ip6 *ip6;
+ struct icmp *icmp;
+ struct tcp *tcp;
+ struct udp *udp;
+ struct dhcp *dhcp;
+};
+
+static void send_syn(struct mg_connection *c);
+
+static void mkpay(struct pkt *pkt, void *p) {
+ pkt->pay =
+ mg_str_n((char *) p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (char *) p));
+}
+
+static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
+ size_t i;
+ const uint8_t *p = (const uint8_t *) buf;
+ for (i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8);
+ return sum;
+}
+
+static uint16_t csumfin(uint32_t sum) {
+ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
+ return mg_htons(~sum & 0xffff);
+}
+
+static uint16_t ipcsum(const void *buf, size_t len) {
+ uint32_t sum = csumup(0, buf, len);
+ return csumfin(sum);
+}
+
+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_SYN ? MIP_TCP_SYN_MS
+ : type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS
+ : MIP_TCP_KEEPALIVE_MS;
+ s->timer = ifp->now + n;
+ s->ttype = type;
+ MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
+}
+
+static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) {
+ size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp);
+ if (n == len) ifp->nsent++;
+ return n;
+}
+
+static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
+ struct eth *eth = (struct eth *) ifp->tx.buf;
+ struct arp *arp = (struct arp *) (eth + 1);
+ memset(eth->dst, 255, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src));
+ eth->type = mg_htons(0x806);
+ memset(arp, 0, sizeof(*arp));
+ arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6,
+ arp->plen = 4;
+ arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
+ memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
+ ether_output(ifp, PDIFF(eth, arp + 1));
+}
+
+static void onstatechange(struct mg_tcpip_if *ifp) {
+ if (ifp->state == MG_TCPIP_STATE_READY) {
+ 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);
+ } 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"));
+ }
+}
+
+static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint8_t proto, uint32_t ip_src, uint32_t ip_dst,
+ size_t plen) {
+ struct eth *eth = (struct eth *) ifp->tx.buf;
+ struct ip *ip = (struct ip *) (eth + 1);
+ memcpy(eth->dst, mac_dst, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC
+ eth->type = mg_htons(0x800);
+ memset(ip, 0, sizeof(*ip));
+ ip->ver = 0x45; // Version 4, header length 5 words
+ ip->frag = 0x40; // Don't fragment
+ ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
+ ip->ttl = 64;
+ ip->proto = proto;
+ ip->src = ip_src;
+ ip->dst = ip_dst;
+ ip->csum = ipcsum(ip, sizeof(*ip));
+ return ip;
+}
+
+static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
+ uint16_t sport, uint32_t ip_dst, uint16_t dport,
+ const void *buf, size_t len) {
+ struct ip *ip =
+ tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp));
+ struct udp *udp = (struct udp *) (ip + 1);
+ // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
+ udp->sport = sport;
+ udp->dport = dport;
+ udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
+ udp->csum = 0;
+ uint32_t cs = csumup(0, udp, sizeof(*udp));
+ cs = csumup(cs, buf, len);
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
+ udp->csum = csumfin(cs);
+ memmove(udp + 1, buf, len);
+ // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
+ ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len);
+}
+
+static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
+ uint32_t ip_dst, uint8_t *opts, size_t optslen,
+ bool ciaddr) {
+ // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6
+ struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
+ dhcp.magic = mg_htonl(0x63825363);
+ memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac));
+ memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
+ memcpy(&dhcp.options, opts, optslen);
+ if (ciaddr) dhcp.ciaddr = ip_src;
+ tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
+ sizeof(dhcp));
+}
+
+static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
+
+// RFC-2131 #4.3.6, #4.4.1
+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
+ };
+ 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);
+ MG_DEBUG(("DHCP req sent"));
+}
+
+// RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast)
+static void tx_dhcp_request_re(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
+ uint32_t ip_src, uint32_t ip_dst) {
+ uint8_t opts[] = {
+ 53, 1, 3, // Type: DHCP request
+ 255 // End of options
+ };
+ tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts), true);
+ MG_DEBUG(("DHCP req sent"));
+}
+
+static void tx_dhcp_discover(struct mg_tcpip_if *ifp) {
+ uint8_t opts[] = {
+ 53, 1, 1, // Type: DHCP discover
+ 55, 2, 1, 3, // Parameters: ip, mask
+ 255 // End of options
+ };
+ tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false);
+ MG_DEBUG(("DHCP discover sent. Our MAC: %M", mg_print_mac, ifp->mac));
+}
+
+static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
+ bool lsn) {
+ struct mg_connection *c = NULL;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ if (c->is_arplooking && pkt->arp &&
+ memcmp(&pkt->arp->spa, c->rem.ip, sizeof(pkt->arp->spa)) == 0)
+ break;
+ if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
+ if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
+ lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport))
+ break;
+ }
+ return 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
+ // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4,
+ // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa));
+ struct eth *eth = (struct eth *) ifp->tx.buf;
+ struct arp *arp = (struct arp *) (eth + 1);
+ memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
+ memcpy(eth->src, ifp->mac, sizeof(eth->src));
+ eth->type = mg_htons(0x806);
+ *arp = *pkt->arp;
+ arp->op = mg_htons(2);
+ memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha));
+ memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha));
+ arp->tpa = pkt->arp->spa;
+ arp->spa = ifp->ip;
+ MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_mac,
+ &ifp->mac));
+ ether_output(ifp, PDIFF(eth, arp + 1));
+ } else if (pkt->arp->op == mg_htons(2)) {
+ if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
+ if (pkt->arp->spa == ifp->gw) {
+ // Got response for the GW ARP request. Set ifp->gwmac
+ memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
+ } else {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ if (c != NULL && c->is_arplooking) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, pkt->arp->sha, sizeof(s->mac));
+ 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);
+ }
+ }
+ }
+}
+
+static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ // MG_DEBUG(("ICMP %d", (int) len));
+ if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
+ size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
+ size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
+ if (plen > space) plen = space;
+ struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
+ sizeof(struct icmp) + plen);
+ struct icmp *icmp = (struct icmp *) (ip + 1);
+ memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
+ memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
+ icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
+ ether_output(ifp, hlen + plen);
+ }
+}
+
+static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ uint32_t ip = 0, gw = 0, mask = 0, lease = 0;
+ uint8_t msgtype = 0, state = ifp->state;
+ // perform size check first, then access fields
+ uint8_t *p = pkt->dhcp->options,
+ *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
+ if (end < (uint8_t *) (pkt->dhcp + 1)) return;
+ if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
+ while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9
+ if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
+ memcpy(&mask, p + 2, sizeof(mask));
+ } 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 (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
+ memcpy(&lease, p + 2, sizeof(lease));
+ lease = mg_ntohl(lease);
+ } else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type
+ msgtype = p[2];
+ }
+ p += p[1] + 2;
+ }
+ // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4)
+ if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP
+ ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0;
+ } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw &&
+ lease) { // DHCPOFFER
+ // select IP, (4.4.1) (fallback to IP source addr on foul play)
+ tx_dhcp_request_sel(ifp, ip,
+ pkt->dhcp->siaddr ? pkt->dhcp->siaddr : pkt->ip->src);
+ ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
+ } else if (msgtype == 5) { // DHCPACK
+ if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
+ ifp->lease_expire = ifp->now + lease * 1000;
+ MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
+ // assume DHCP server = router until ARP resolves
+ memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
+ ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
+ ifp->state = MG_TCPIP_STATE_READY; // BOUND state
+ uint64_t rand;
+ mg_random(&rand, sizeof(rand));
+ srand((unsigned int) (rand + mg_millis()));
+ } 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));
+ } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4)
+ }
+ if (ifp->state != state) onstatechange(ifp);
+}
+
+// Simple DHCP server that assigns a next IP address: ifp->ip + 1
+static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ uint8_t op = 0, *p = pkt->dhcp->options,
+ *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len];
+ if (end < (uint8_t *) (pkt->dhcp + 1)) return;
+ // struct dhcp *req = pkt->dhcp;
+ struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
+ res.yiaddr = ifp->ip;
+ ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
+ while (p + 1 < end && p[0] != 255) { // Parse options
+ if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
+ op = p[2];
+ }
+ p += p[1] + 2;
+ }
+ if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
+ uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
+ uint8_t opts[] = {
+ 53, 1, msg, // Message type
+ 1, 4, 0, 0, 0, 0, // Subnet mask
+ 54, 4, 0, 0, 0, 0, // Server ID
+ 12, 3, 'm', 'i', 'p', // Host name: "mip"
+ 51, 4, 255, 255, 255, 255, // Lease time
+ 255 // End of options
+ };
+ memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
+ memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
+ memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
+ memcpy(&res.options, opts, sizeof(opts));
+ res.magic = pkt->dhcp->magic;
+ res.xid = pkt->dhcp->xid;
+ if (ifp->enable_get_gateway) {
+ ifp->gw = res.yiaddr;
+ memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
+ }
+ tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
+ op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
+ }
+}
+
+static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
+ if (c == NULL) {
+ // No UDP listener on this port. Should send ICMP, but keep silent.
+ } else {
+ c->rem.port = pkt->udp->sport;
+ memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
+ struct connstate *s = (struct connstate *) (c + 1);
+ memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
+ if (c->recv.len >= MG_MAX_RECV_SIZE) {
+ mg_error(c, "max_recv_buf_size reached");
+ } else if (c->recv.size - c->recv.len < pkt->pay.len &&
+ !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } else {
+ memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
+ c->recv.len += pkt->pay.len;
+ mg_call(c, MG_EV_READ, &pkt->pay.len);
+ }
+ }
+}
+
+static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
+ uint8_t flags, uint16_t sport, uint16_t dport,
+ uint32_t seq, uint32_t ack, const void *buf, size_t len) {
+#if 0
+ uint8_t opts[] = {2, 4, 5, 0xb4, 4, 2, 0, 0}; // MSS = 1460, SACK permitted
+ if (flags & TH_SYN) {
+ // Handshake? Set MSS
+ buf = opts;
+ len = sizeof(opts);
+ }
+#endif
+ struct ip *ip =
+ tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
+ struct tcp *tcp = (struct tcp *) (ip + 1);
+ memset(tcp, 0, sizeof(*tcp));
+ if (buf != NULL && len) memmove(tcp + 1, buf, len);
+ tcp->sport = sport;
+ tcp->dport = dport;
+ tcp->seq = seq;
+ tcp->ack = ack;
+ tcp->flags = flags;
+ tcp->win = mg_htons(MIP_TCP_WIN);
+ tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4);
+ // if (flags & TH_SYN) tcp->off = 0x70; // Handshake? header size 28 bytes
+
+ uint32_t cs = 0;
+ uint16_t n = (uint16_t) (sizeof(*tcp) + len);
+ uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
+ cs = csumup(cs, tcp, n);
+ cs = csumup(cs, &ip->src, sizeof(ip->src));
+ cs = csumup(cs, &ip->dst, sizeof(ip->dst));
+ cs = csumup(cs, pseudo, sizeof(pseudo));
+ tcp->csum = csumfin(cs);
+ MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
+ mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst,
+ mg_ntohs(tcp->dport), tcp->flags, len));
+ // mg_hexdump(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len);
+ return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len);
+}
+
+static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
+ uint8_t flags, uint32_t seq, const void *buf,
+ size_t len) {
+ uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
+ return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
+ pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
+ buf, len);
+}
+
+static struct mg_connection *accept_conn(struct mg_connection *lsn,
+ struct pkt *pkt) {
+ struct mg_connection *c = mg_alloc_conn(lsn->mgr);
+ if (c == NULL) {
+ MG_ERROR(("OOM"));
+ return NULL;
+ }
+ struct connstate *s = (struct connstate *) (c + 1);
+ s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
+ memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t));
+ c->rem.port = pkt->tcp->sport;
+ MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
+ LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
+ c->is_accepted = 1;
+ c->is_hexdumping = lsn->is_hexdumping;
+ c->pfn = lsn->pfn;
+ c->loc = lsn->loc;
+ c->pfn_data = lsn->pfn_data;
+ c->fn = lsn->fn;
+ c->fn_data = lsn->fn_data;
+ mg_call(c, MG_EV_OPEN, NULL);
+ mg_call(c, MG_EV_ACCEPT, NULL);
+ return c;
+}
+
+static size_t trim_len(struct mg_connection *c, size_t len) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8;
+ size_t max_headers_len =
+ eth_h_len + ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len);
+ size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_h_len;
+
+ // If the frame exceeds the available buffer, trim the length
+ if (len + max_headers_len > ifp->tx.len) {
+ len = ifp->tx.len - max_headers_len;
+ }
+ // Ensure the MTU isn't lower than the minimum allowed value
+ if (ifp->mtu < min_mtu) {
+ MG_ERROR(("MTU is lower than minimum, capping to %lu", min_mtu));
+ ifp->mtu = (uint16_t) min_mtu;
+ }
+ // If the total packet size exceeds the MTU, trim the length
+ if (len + max_headers_len - eth_h_len > ifp->mtu) {
+ len = ifp->mtu - max_headers_len + eth_h_len;
+ if (c->is_udp) {
+ MG_ERROR(("UDP datagram exceeds MTU. Truncating it."));
+ }
+ }
+
+ return len;
+}
+
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ struct connstate *s = (struct connstate *) (c + 1);
+ uint32_t dst_ip = *(uint32_t *) c->rem.ip;
+ len = trim_len(c, len);
+ if (c->is_udp) {
+ tx_udp(ifp, s->mac, ifp->ip, c->loc.port, dst_ip, c->rem.port, buf, len);
+ } else {
+ size_t sent =
+ tx_tcp(ifp, s->mac, dst_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
+ if (sent == 0) {
+ return MG_IO_WAIT;
+ } else if (sent == (size_t) -1) {
+ return MG_IO_ERR;
+ } else {
+ s->seq += (uint32_t) len;
+ if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
+ }
+ }
+ return (long) len;
+}
+
+static void handle_tls_recv(struct mg_connection *c, struct mg_iobuf *io) {
+ 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;
+ mg_call(c, MG_EV_READ, &n);
+ }
+}
+
+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 ? &c->rtls : &c->recv;
+ uint32_t seq = mg_ntohl(pkt->tcp->seq);
+ uint32_t rem_ip;
+ memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
+ if (pkt->tcp->flags & TH_FIN) {
+ // If we initiated the closure, we reply with ACK upon receiving FIN
+ // If we didn't initiate it, we reply with FIN as part of the normal TCP
+ // closure process
+ uint8_t flags = TH_ACK;
+ s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1);
+ if (c->is_draining && s->ttype == MIP_TTYPE_FIN) {
+ if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ?
+ s->seq++; // Yes. Increment our SEQ
+ } else { // Otherwise,
+ s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK
+ }
+ } else {
+ flags |= TH_FIN;
+ c->is_draining = 1;
+ settmout(c, MIP_TTYPE_FIN);
+ }
+ tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, flags,
+ c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
+ } else if (pkt->pay.len == 0) {
+ // TODO(cpq): handle this peer's ACK
+ } else if (seq != s->ack) {
+ uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
+ if (s->ack == ack) {
+ MG_VERBOSE(("ignoring duplicate pkt"));
+ } else {
+ MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
+ tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
+ c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "",
+ 0);
+ }
+ } else if (io->size - io->len < pkt->pay.len &&
+ !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
+ mg_error(c, "oom");
+ } 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 c->rtls 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 c->rtls
+ memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
+ io->len += pkt->pay.len;
+
+ MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
+ // Advance ACK counter
+ s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
+ s->unacked += pkt->pay.len;
+ // size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
+ if (s->unacked > MIP_TCP_WIN / 2 && s->acked != s->ack) {
+ // Send ACK immediately
+ MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
+ tx_tcp((struct mg_tcpip_if *) c->mgr->priv, s->mac, rem_ip, TH_ACK,
+ c->loc.port, c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL,
+ 0);
+ s->unacked = 0;
+ s->acked = s->ack;
+ if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
+ } else {
+ // if not already running, setup a timer to send an ACK later
+ if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
+ }
+
+ if (c->is_tls && c->is_tls_hs) {
+ mg_tls_handshake(c);
+ } else if (c->is_tls) {
+ // TLS connection. Make room for decrypted data in c->recv
+ io = &c->recv;
+ 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
+ handle_tls_recv(c, io);
+ }
+ } else {
+ // Plain text connection, data is already in c->recv, trigger
+ // MG_EV_READ
+ mg_call(c, MG_EV_READ, &pkt->pay.len);
+ }
+ }
+}
+
+static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
+#if 0
+ MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
+#endif
+ 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;
+ tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
+ c->is_connecting = 0; // Client connected
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ mg_call(c, MG_EV_CONNECT, NULL); // Let user know
+ } 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);
+ } else if (c != NULL && pkt->tcp->flags & TH_RST) {
+ mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
+ } else if (c != NULL) {
+#if 0
+ MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
+ mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
+ mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
+ mg_hexdump(pkt->pay.buf, pkt->pay.len);
+#endif
+ s->tmiss = 0; // Reset missed keep-alive counter
+ if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
+ settmout(c,
+ MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
+ read_conn(c, pkt); // Override timer with ACK timeout if needed
+ } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
+ tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else if (pkt->tcp->flags & TH_RST) {
+ if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
+ // ignore RST if not connected
+ } else if (pkt->tcp->flags & TH_SYN) {
+ // Use peer's source port as ISN, in order to recognise the handshake
+ uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
+ tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
+ } else if (pkt->tcp->flags & TH_FIN) {
+ tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
+ accept_conn(c, pkt);
+ } else if (!c->is_accepted) { // no peer
+ tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
+ } else {
+ // MG_VERBOSE(("dropped silently.."));
+ }
+}
+
+static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ if (pkt->ip->frag & IP_MORE_FRAGS_MSK || pkt->ip->frag & IP_FRAG_OFFSET_MSK) {
+ if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1);
+ if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1);
+ struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
+ if (c) mg_error(c, "Received fragmented packet");
+ } else if (pkt->ip->proto == 1) {
+ pkt->icmp = (struct icmp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->icmp)) return;
+ mkpay(pkt, pkt->icmp + 1);
+ rx_icmp(ifp, pkt);
+ } else if (pkt->ip->proto == 17) {
+ pkt->udp = (struct udp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->udp)) return;
+ mkpay(pkt, pkt->udp + 1);
+ MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
+ mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
+ mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
+ if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68)) {
+ pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp + 1);
+ rx_dhcp_client(ifp, pkt);
+ } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
+ pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
+ mkpay(pkt, pkt->dhcp + 1);
+ rx_dhcp_server(ifp, pkt);
+ } else {
+ rx_udp(ifp, pkt);
+ }
+ } else if (pkt->ip->proto == 6) {
+ pkt->tcp = (struct tcp *) (pkt->ip + 1);
+ if (pkt->pay.len < sizeof(*pkt->tcp)) return;
+ mkpay(pkt, pkt->tcp + 1);
+ uint16_t iplen = mg_ntohs(pkt->ip->len);
+ uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
+ if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
+ MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
+ mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
+ mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
+ rx_tcp(ifp, pkt);
+ }
+}
+
+static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
+ // MG_DEBUG(("IP %d", (int) len));
+ if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
+ pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
+ if (pkt->pay.len < sizeof(*pkt->icmp)) return;
+ mkpay(pkt, pkt->icmp + 1);
+ rx_icmp(ifp, pkt);
+ } else if (pkt->ip6->proto == 17) {
+ pkt->udp = (struct udp *) (pkt->ip6 + 1);
+ if (pkt->pay.len < sizeof(*pkt->udp)) return;
+ // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
+ // mg_htons(udp->dport)));
+ mkpay(pkt, pkt->udp + 1);
+ }
+}
+
+static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
+ struct pkt pkt;
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.raw.buf = (char *) buf;
+ pkt.raw.len = len;
+ pkt.eth = (struct eth *) buf;
+ // mg_hexdump(buf, len > 16 ? 16: len);
+ if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
+ if (ifp->enable_mac_check &&
+ memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
+ memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0)
+ return;
+ if (ifp->enable_crc32_check && len > 4) {
+ len -= 4; // TODO(scaprile): check on bigendian
+ uint32_t crc = mg_crc32(0, (const char *) buf, len);
+ if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return;
+ }
+ if (pkt.eth->type == mg_htons(0x806)) {
+ pkt.arp = (struct arp *) (pkt.eth + 1);
+ if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
+ rx_arp(ifp, &pkt);
+ } else if (pkt.eth->type == mg_htons(0x86dd)) {
+ pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
+ if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
+ mkpay(&pkt, pkt.ip6 + 1);
+ rx_ip6(ifp, &pkt);
+ } else if (pkt.eth->type == mg_htons(0x800)) {
+ pkt.ip = (struct ip *) (pkt.eth + 1);
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
+ // Truncate frame to what IP header tells us
+ if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
+ pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
+ }
+ if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
+ if ((pkt.ip->ver >> 4) != 4) return; // Not IP
+ mkpay(&pkt, pkt.ip + 1);
+ rx_ip(ifp, &pkt);
+ } else {
+ MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type)));
+ if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(buf, len >= 32 ? 32 : len);
+ }
+}
+
+static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
+ struct mg_connection *c;
+ bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, now);
+ ifp->now = now;
+
+#if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
+ if (expired_1000ms) {
+ const char *names[] = {"down", "up", "req", "ready"};
+ MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
+ names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
+ ifp->ndrop, ifp->nerr));
+ }
+#endif
+ // Handle physical interface up/down status
+ if (expired_1000ms && ifp->driver->up) {
+ bool up = ifp->driver->up(ifp);
+ bool current = ifp->state != MG_TCPIP_STATE_DOWN;
+ if (up != current) {
+ ifp->state = up == false ? MG_TCPIP_STATE_DOWN
+ : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP
+ : MG_TCPIP_STATE_READY;
+ if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
+ onstatechange(ifp);
+ }
+ if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down"));
+ }
+ if (ifp->state == MG_TCPIP_STATE_DOWN) return;
+
+ // DHCP RFC-2131 (4.4)
+ if (ifp->state == MG_TCPIP_STATE_UP && expired_1000ms) {
+ tx_dhcp_discover(ifp); // INIT (4.4.1)
+ } else if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY &&
+ ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
+ if (ifp->now >= ifp->lease_expire) {
+ ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP
+ onstatechange(ifp);
+ } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire &&
+ ((ifp->now / 1000) % 60) == 0) {
+ // hack: 30 min before deadline, try to rebind (4.3.6) every min
+ tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff);
+ } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
+ }
+
+ // Read data from the network
+ if (ifp->driver->rx != NULL) { // Polling driver. We must call it
+ size_t len =
+ ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp);
+ if (len > 0) {
+ ifp->nrecv++;
+ mg_tcpip_rx(ifp, ifp->recv_queue.buf, len);
+ }
+ } else { // Interrupt-based driver. Fills recv queue itself
+ char *buf;
+ size_t len = mg_queue_next(&ifp->recv_queue, &buf);
+ if (len > 0) {
+ mg_tcpip_rx(ifp, buf, len);
+ mg_queue_del(&ifp->recv_queue, len);
+ }
+ }
+
+ // Process timeouts
+ for (c = ifp->mgr->conns; c != NULL; c = c->next) {
+ if (c->is_udp || 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) {
+ 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) {
+ c->is_closing = 1;
+ continue;
+ } else {
+ if (s->tmiss++ > 2) {
+ mg_error(c, "keepalive");
+ } else {
+ MG_VERBOSE(("%lu keepalive", c->id));
+ tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0);
+ }
+ }
+
+ settmout(c, MIP_TTYPE_KEEPALIVE);
+ }
+ }
+}
+
+// This function executes in interrupt context, thus it should copy data
+// somewhere fast. Note that newlib's malloc is not thread safe, thus use
+// our lock-free queue with preallocated buffer to copy data and return asap
+void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
+ char *p;
+ if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) {
+ memcpy(p, buf, len);
+ mg_queue_add(&ifp->recv_queue, len);
+ ifp->nrecv++;
+ } else {
+ ifp->ndrop++;
+ }
+}
+
+void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
+ // If MAC address is not set, make a random one
+ if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 &&
+ ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) {
+ ifp->mac[0] = 0x02; // Locally administered, unicast
+ mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1);
+ MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac));
+ }
+
+ if (ifp->driver->init && !ifp->driver->init(ifp)) {
+ MG_ERROR(("driver init failed"));
+ } else {
+ size_t framesize = 1540;
+ ifp->tx.buf = (char *) calloc(1, framesize), ifp->tx.len = framesize;
+ if (ifp->recv_queue.size == 0)
+ ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192;
+ ifp->recv_queue.buf = (char *) calloc(1, ifp->recv_queue.size);
+ ifp->timer_1000ms = mg_millis();
+ mgr->priv = ifp;
+ ifp->mgr = mgr;
+ ifp->mtu = MG_TCPIP_MTU_DEFAULT;
+ mgr->extraconnsize = sizeof(struct connstate);
+ if (ifp->ip == 0) ifp->enable_dhcp_client = true;
+ memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast
+ mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
+ ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
+ // MG_EPHEMERAL_PORT_BASE to 65535
+ if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
+ }
+}
+
+void mg_tcpip_free(struct mg_tcpip_if *ifp) {
+ free(ifp->recv_queue.buf);
+ free(ifp->tx.buf);
+}
+
+static void send_syn(struct mg_connection *c) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ uint32_t rem_ip;
+ memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
+ tx_tcp(ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL,
+ 0);
+}
+
+void mg_connect_resolved(struct mg_connection *c) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ uint32_t rem_ip;
+ memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
+ c->is_resolving = 0;
+ if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
+ memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t));
+ c->loc.port = mg_htons(ifp->eport++);
+ 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);
+ 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))) {
+ // 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;
+ } 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;
+ }
+ }
+}
+
+bool mg_open_listener(struct mg_connection *c, const char *url) {
+ c->loc.port = mg_htons(mg_url_port(url));
+ return true;
+}
+
+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);
+ if (len == MG_IO_ERR) {
+ mg_error(c, "tx err");
+ } else if (len > 0) {
+ mg_iobuf_del(&c->send, 0, (size_t) len);
+ mg_call(c, MG_EV_WRITE, &len);
+ }
+}
+
+static void init_closure(struct mg_connection *c) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ if (c->is_udp == false && c->is_listening == false &&
+ c->is_connecting == false) { // For TCP conns,
+ struct mg_tcpip_if *ifp =
+ (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
+ uint32_t rem_ip;
+ memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t));
+ tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
+ mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
+ settmout(c, MIP_TTYPE_FIN);
+ }
+}
+
+static void close_conn(struct mg_connection *c) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ mg_iobuf_free(&s->raw); // For TLS connections, release raw data
+ mg_close_conn(c);
+}
+
+static bool can_write(struct mg_connection *c) {
+ return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
+ c->is_tls_hs == 0 && c->is_arplooking == 0;
+}
+
+void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) mgr->priv;
+ struct mg_connection *c, *tmp;
+ uint64_t now = mg_millis();
+ mg_timer_poll(&mgr->timers, now);
+ if (ifp == NULL || ifp->driver == NULL) return;
+ mg_tcpip_poll(ifp, now);
+ for (c = mgr->conns; c != NULL; c = tmp) {
+ tmp = c->next;
+ struct connstate *s = (struct connstate *) (c + 1);
+ mg_call(c, MG_EV_POLL, &now);
+ MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't',
+ c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
+ c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
+ if (c->is_tls && mg_tls_pending(c) > 0)
+ handle_tls_recv(c, (struct mg_iobuf *) &c->rtls);
+ if (can_write(c)) write_conn(c);
+ if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN)
+ init_closure(c);
+ if (c->is_closing) close_conn(c);
+ }
+ (void) ms;
+}
+
+bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
+ bool res = false;
+ uint32_t rem_ip;
+ 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) {
+ struct connstate *s = (struct connstate *) (c + 1);
+ len = trim_len(c, len); // Trimming length if necessary
+ tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len);
+ res = true;
+ } else {
+ res = mg_iobuf_add(&c->send, c->send.len, buf, len);
+ }
+ return res;
+}
+#endif // MG_ENABLE_TCPIP
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ota_dummy.c"
+#endif
+
+
+
+#if MG_OTA == MG_OTA_NONE
+bool mg_ota_begin(size_t new_firmware_size) {
+ (void) new_firmware_size;
+ return true;
+}
+bool mg_ota_write(const void *buf, size_t len) {
+ (void) buf, (void) len;
+ return true;
+}
+bool mg_ota_end(void) {
+ return true;
+}
+bool mg_ota_commit(void) {
+ return true;
+}
+bool mg_ota_rollback(void) {
+ return true;
+}
+int mg_ota_status(int fw) {
+ (void) fw;
+ return 0;
+}
+uint32_t mg_ota_crc32(int fw) {
+ (void) fw;
+ return 0;
+}
+uint32_t mg_ota_timestamp(int fw) {
+ (void) fw;
+ return 0;
+}
+size_t mg_ota_size(int fw) {
+ (void) fw;
+ return 0;
+}
+MG_IRAM void mg_ota_boot(void) {
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ota_esp32.c"
+#endif
+
+
+#if MG_ARCH == MG_ARCH_ESP32 && MG_OTA == MG_OTA_ESP32
+
+static const esp_partition_t *s_ota_update_partition;
+static esp_ota_handle_t s_ota_update_handle;
+static bool s_ota_success;
+
+// Those empty macros do nothing, but mark places in the code which could
+// potentially trigger a watchdog reboot due to the log flash erase operation
+#define disable_wdt()
+#define enable_wdt()
+
+bool mg_ota_begin(size_t new_firmware_size) {
+ if (s_ota_update_partition != NULL) {
+ MG_ERROR(("Update in progress. Call mg_ota_end() ?"));
+ return false;
+ } else {
+ s_ota_success = false;
+ disable_wdt();
+ s_ota_update_partition = esp_ota_get_next_update_partition(NULL);
+ esp_err_t err = esp_ota_begin(s_ota_update_partition, new_firmware_size,
+ &s_ota_update_handle);
+ enable_wdt();
+ MG_DEBUG(("esp_ota_begin(): %d", err));
+ s_ota_success = (err == ESP_OK);
+ }
+ return s_ota_success;
+}
+
+bool mg_ota_write(const void *buf, size_t len) {
+ disable_wdt();
+ esp_err_t err = esp_ota_write(s_ota_update_handle, buf, len);
+ enable_wdt();
+ MG_INFO(("esp_ota_write(): %d", err));
+ s_ota_success = err == ESP_OK;
+ return s_ota_success;
+}
+
+bool mg_ota_end(void) {
+ esp_err_t err = esp_ota_end(s_ota_update_handle);
+ MG_DEBUG(("esp_ota_end(%p): %d", s_ota_update_handle, err));
+ if (s_ota_success && err == ESP_OK) {
+ err = esp_ota_set_boot_partition(s_ota_update_partition);
+ s_ota_success = (err == ESP_OK);
+ }
+ MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success));
+ s_ota_update_partition = NULL;
+ return s_ota_success;
+}
+
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ota_flash.c"
+#endif
+
+
+
+
+
+// This OTA implementation uses the internal flash API outlined in device.h
+// It splits flash into 2 equal partitions, and stores OTA status in the
+// last sector of the partition.
+
+#if MG_OTA == MG_OTA_FLASH
+
+#define MG_OTADATA_KEY 0xb07afed0
+
+static char *s_addr; // Current address to write to
+static size_t s_size; // Firmware size to flash. In-progress indicator
+static uint32_t s_crc32; // Firmware checksum
+
+struct mg_otadata {
+ uint32_t crc32, size, timestamp, status;
+};
+
+bool mg_ota_begin(size_t new_firmware_size) {
+ bool ok = false;
+ if (s_size) {
+ MG_ERROR(("OTA already in progress. Call mg_ota_end()"));
+ } else {
+ size_t half = mg_flash_size() / 2, max = half - mg_flash_sector_size();
+ s_crc32 = 0;
+ s_addr = (char *) mg_flash_start() + half;
+ MG_DEBUG(("Firmware %lu bytes, max %lu", new_firmware_size, max));
+ if (new_firmware_size < max) {
+ ok = true;
+ s_size = new_firmware_size;
+ MG_INFO(("Starting OTA, firmware size %lu", s_size));
+ } else {
+ MG_ERROR(("Firmware %lu is too big to fit %lu", new_firmware_size, max));
+ }
+ }
+ return ok;
+}
+
+bool mg_ota_write(const void *buf, size_t len) {
+ bool ok = false;
+ if (s_size == 0) {
+ MG_ERROR(("OTA is not started, call mg_ota_begin()"));
+ } else {
+ size_t align = mg_flash_write_align();
+ size_t len_aligned_down = MG_ROUND_DOWN(len, align);
+ if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down);
+ if (len_aligned_down < len) {
+ size_t left = len - len_aligned_down;
+ char tmp[align];
+ memset(tmp, 0xff, sizeof(tmp));
+ memcpy(tmp, (char *) buf + len_aligned_down, left);
+ ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp));
+ }
+ s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC
+ MG_DEBUG(("%#x %p %lu -> %d", s_addr - len, buf, len, ok));
+ s_addr += len;
+ }
+ return ok;
+}
+
+MG_IRAM static uint32_t mg_fwkey(int fw) {
+ uint32_t key = MG_OTADATA_KEY + fw;
+ int bank = mg_flash_bank();
+ if (bank == 2 && fw == MG_FIRMWARE_PREVIOUS) key--;
+ if (bank == 2 && fw == MG_FIRMWARE_CURRENT) key++;
+ return key;
+}
+
+bool mg_ota_end(void) {
+ char *base = (char *) mg_flash_start() + mg_flash_size() / 2;
+ bool ok = false;
+ if (s_size) {
+ size_t size = s_addr - base;
+ uint32_t crc32 = mg_crc32(0, base, s_size);
+ if (size == s_size && crc32 == s_crc32) {
+ uint32_t now = (uint32_t) (mg_now() / 1000);
+ struct mg_otadata od = {crc32, size, now, MG_OTA_FIRST_BOOT};
+ uint32_t key = mg_fwkey(MG_FIRMWARE_PREVIOUS);
+ ok = mg_flash_save(NULL, key, &od, sizeof(od));
+ }
+ MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size,
+ size, ok ? "ok" : "fail"));
+ s_size = 0;
+ if (ok) ok = mg_flash_swap_bank();
+ }
+ MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail"));
+ return ok;
+}
+
+MG_IRAM static struct mg_otadata mg_otadata(int fw) {
+ uint32_t key = mg_fwkey(fw);
+ struct mg_otadata od = {};
+ MG_INFO(("Loading %s OTA data", fw == MG_FIRMWARE_CURRENT ? "curr" : "prev"));
+ mg_flash_load(NULL, key, &od, sizeof(od));
+ // MG_DEBUG(("Loaded OTA data. fw %d, bank %d, key %p", fw, bank, key));
+ // mg_hexdump(&od, sizeof(od));
+ return od;
+}
+
+int mg_ota_status(int fw) {
+ struct mg_otadata od = mg_otadata(fw);
+ return od.status;
+}
+uint32_t mg_ota_crc32(int fw) {
+ struct mg_otadata od = mg_otadata(fw);
+ return od.crc32;
+}
+uint32_t mg_ota_timestamp(int fw) {
+ struct mg_otadata od = mg_otadata(fw);
+ return od.timestamp;
+}
+size_t mg_ota_size(int fw) {
+ struct mg_otadata od = mg_otadata(fw);
+ return od.size;
+}
+
+MG_IRAM bool mg_ota_commit(void) {
+ bool ok = true;
+ struct mg_otadata od = mg_otadata(MG_FIRMWARE_CURRENT);
+ if (od.status != MG_OTA_COMMITTED) {
+ od.status = MG_OTA_COMMITTED;
+ MG_INFO(("Committing current firmware, OD size %lu", sizeof(od)));
+ ok = mg_flash_save(NULL, mg_fwkey(MG_FIRMWARE_CURRENT), &od, sizeof(od));
+ }
+ return ok;
+}
+
+bool mg_ota_rollback(void) {
+ MG_DEBUG(("Rolling firmware back"));
+ if (mg_flash_bank() == 0) {
+ // No dual bank support. Mark previous firmware as FIRST_BOOT
+ struct mg_otadata prev = mg_otadata(MG_FIRMWARE_PREVIOUS);
+ prev.status = MG_OTA_FIRST_BOOT;
+ return mg_flash_save(NULL, MG_OTADATA_KEY + MG_FIRMWARE_PREVIOUS, &prev,
+ sizeof(prev));
+ } else {
+ return mg_flash_swap_bank();
+ }
+}
+
+MG_IRAM void mg_ota_boot(void) {
+ MG_INFO(("Booting. Flash bank: %d", mg_flash_bank()));
+ struct mg_otadata curr = mg_otadata(MG_FIRMWARE_CURRENT);
+ struct mg_otadata prev = mg_otadata(MG_FIRMWARE_PREVIOUS);
+
+ if (curr.status == MG_OTA_FIRST_BOOT) {
+ if (prev.status == MG_OTA_UNAVAILABLE) {
+ MG_INFO(("Setting previous firmware state to committed"));
+ prev.status = MG_OTA_COMMITTED;
+ mg_flash_save(NULL, mg_fwkey(MG_FIRMWARE_PREVIOUS), &prev, sizeof(prev));
+ }
+ curr.status = MG_OTA_UNCOMMITTED;
+ MG_INFO(("First boot, setting status to UNCOMMITTED"));
+ mg_flash_save(NULL, mg_fwkey(MG_FIRMWARE_CURRENT), &curr, sizeof(curr));
+ } else if (prev.status == MG_OTA_FIRST_BOOT && mg_flash_bank() == 0) {
+ // Swap paritions. Pray power does not disappear
+ size_t fs = mg_flash_size(), ss = mg_flash_sector_size();
+ char *partition1 = mg_flash_start();
+ char *partition2 = mg_flash_start() + fs / 2;
+ size_t ofs, max = fs / 2 - ss; // Set swap size to the whole partition
+
+ if (curr.status != MG_OTA_UNAVAILABLE &&
+ prev.status != MG_OTA_UNAVAILABLE) {
+ // We know exact sizes of both firmwares.
+ // Shrink swap size to the MAX(firmware1, firmware2)
+ size_t sz = curr.size > prev.size ? curr.size : prev.size;
+ if (sz > 0 && sz < max) max = sz;
+ }
+
+ // MG_OTA_FIRST_BOOT -> MG_OTA_UNCOMMITTED
+ prev.status = MG_OTA_UNCOMMITTED;
+ mg_flash_save(NULL, MG_OTADATA_KEY + MG_FIRMWARE_CURRENT, &prev,
+ sizeof(prev));
+ mg_flash_save(NULL, MG_OTADATA_KEY + MG_FIRMWARE_PREVIOUS, &curr,
+ sizeof(curr));
+
+ MG_INFO(("Swapping partitions, size %u (%u sectors)", max, max / ss));
+ MG_INFO(("Do NOT power off..."));
+ mg_log_level = MG_LL_NONE;
+
+ // We use the last sector of partition2 for OTA data/config storage
+ // Therefore we can use last sector of partition1 for swapping
+ char *tmpsector = partition1 + fs / 2 - ss; // Last sector of partition1
+ (void) tmpsector;
+ for (ofs = 0; ofs < max; ofs += ss) {
+ // mg_flash_erase(tmpsector);
+ mg_flash_write(tmpsector, partition1 + ofs, ss);
+ // mg_flash_erase(partition1 + ofs);
+ mg_flash_write(partition1 + ofs, partition2 + ofs, ss);
+ // mg_flash_erase(partition2 + ofs);
+ mg_flash_write(partition2 + ofs, tmpsector, ss);
+ }
+ mg_device_reset();
+ }
+}
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/printf.c"
+#endif
+
+
+
+
+size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) {
+ size_t len = mg_snprintf(NULL, 0, fmt, ap);
+ char *buf;
+ if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) {
+ len = 0; // Nah. Not enough space
+ } else {
+ len = mg_vsnprintf((char *) buf, len + 1, fmt, ap);
+ mg_queue_add(q, len);
+ }
+ return len;
+}
+
+size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
+ va_list ap;
+ size_t len;
+ va_start(ap, fmt);
+ len = mg_queue_vprintf(q, fmt, &ap);
+ va_end(ap);
+ return len;
+}
+
+static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
+ struct mg_iobuf *io = (struct mg_iobuf *) param;
+ if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2);
+ if (io->len + 2 <= io->size) {
+ io->buf[io->len++] = (uint8_t) ch;
+ io->buf[io->len] = 0;
+ } else if (io->len < io->size) {
+ io->buf[io->len++] = 0; // Guarantee to 0-terminate
+ }
+}
+
+static void mg_putchar_iobuf_static(char ch, void *param) {
+ mg_pfn_iobuf_private(ch, param, false);
+}
+
+void mg_pfn_iobuf(char ch, void *param) {
+ mg_pfn_iobuf_private(ch, param, true);
+}
+
+size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) {
+ struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0};
+ size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap);
+ if (n < len) buf[n] = '\0';
+ return n;
+}
+
+size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) {
+ va_list ap;
+ size_t n;
+ va_start(ap, fmt);
+ n = mg_vsnprintf(buf, len, fmt, &ap);
+ va_end(ap);
+ return n;
+}
+
+char *mg_vmprintf(const char *fmt, va_list *ap) {
+ struct mg_iobuf io = {0, 0, 0, 256};
+ mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap);
+ return (char *) io.buf;
+}
+
+char *mg_mprintf(const char *fmt, ...) {
+ char *s;
+ va_list ap;
+ va_start(ap, fmt);
+ s = mg_vmprintf(fmt, &ap);
+ va_end(ap);
+ return s;
+}
+
+void mg_pfn_stdout(char c, void *param) {
+ putchar(c);
+ (void) param;
+}
+
+static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
+ return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+}
+
+static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) {
+ return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]),
+ mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]),
+ mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]),
+ mg_ntohs(p[7]));
+}
+
+size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ return print_ip4(out, arg, p);
+}
+
+size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint16_t *p = va_arg(*ap, uint16_t *);
+ return print_ip6(out, arg, p);
+}
+
+size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) {
+ struct mg_addr *addr = va_arg(*ap, struct mg_addr *);
+ if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip);
+ return print_ip4(out, arg, (uint8_t *) &addr->ip);
+}
+
+size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) {
+ struct mg_addr *a = va_arg(*ap, struct mg_addr *);
+ return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port));
+}
+
+size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) {
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
+ p[3], p[4], p[5]);
+}
+
+static char mg_esc(int c, bool esc) {
+ const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
+ for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
+ if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
+ }
+ return 0;
+}
+
+static char mg_escape(int c) {
+ return mg_esc(c, true);
+}
+
+static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf,
+ size_t len) {
+ size_t i = 0, extra = 0;
+ for (i = 0; i < len && buf[i] != '\0'; i++) {
+ char c = mg_escape(buf[i]);
+ if (c) {
+ out('\\', ptr), out(c, ptr), extra++;
+ } else {
+ out(buf[i], ptr);
+ }
+ }
+ return i + extra;
+}
+
+static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf,
+ size_t len) {
+ size_t i, j, n = 0;
+ const char *t =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ for (i = 0; i < len; i += 3) {
+ uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0,
+ c3 = i + 2 < len ? buf[i + 2] : 0;
+ char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='};
+ if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
+ if (i + 2 < len) tmp[3] = t[c3 & 63];
+ for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg);
+ n += j;
+ }
+ return n;
+}
+
+size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t bl = (size_t) va_arg(*ap, int);
+ uint8_t *p = va_arg(*ap, uint8_t *);
+ const char *hex = "0123456789abcdef";
+ size_t j;
+ for (j = 0; j < bl; j++) {
+ out(hex[(p[j] >> 4) & 0x0F], arg);
+ out(hex[p[j] & 0x0F], arg);
+ }
+ return 2 * bl;
+}
+size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t len = (size_t) va_arg(*ap, int);
+ uint8_t *buf = va_arg(*ap, uint8_t *);
+ return bcpy(out, arg, buf, len);
+}
+
+size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) {
+ size_t len = (size_t) va_arg(*ap, int);
+ char *p = va_arg(*ap, char *);
+ if (len == 0) len = p == NULL ? 0 : strlen(p);
+ return qcpy(out, arg, p, len);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/queue.c"
+#endif
+
+
+
+#if (defined(__GNUC__) && (__GNUC__ > 4) || \
+ (defined(__GNUC_MINOR__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \
+ defined(__clang__)
+#define MG_MEMORY_BARRIER() __sync_synchronize()
+#elif defined(_MSC_VER) && _MSC_VER >= 1700
+#define MG_MEMORY_BARRIER() MemoryBarrier()
+#elif !defined(MG_MEMORY_BARRIER)
+#define MG_MEMORY_BARRIER()
+#endif
+
+// Every message in a queue is prepended by a 32-bit message length (ML).
+// If ML is 0, then it is the end, and reader must wrap to the beginning.
+//
+// Queue when q->tail <= q->head:
+// |----- free -----| ML | message1 | ML | message2 | ----- free ------|
+// ^ ^ ^ ^
+// buf tail head len
+//
+// Queue when q->tail > q->head:
+// | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----|
+// ^ ^ ^ ^
+// buf head tail len
+
+void mg_queue_init(struct mg_queue *q, char *buf, size_t size) {
+ q->size = size;
+ q->buf = buf;
+ q->head = q->tail = 0;
+}
+
+static size_t mg_queue_read_len(struct mg_queue *q) {
+ uint32_t n = 0;
+ MG_MEMORY_BARRIER();
+ memcpy(&n, q->buf + q->tail, sizeof(n));
+ assert(q->tail + n + sizeof(n) <= q->size);
+ return n;
+}
+
+static void mg_queue_write_len(struct mg_queue *q, size_t len) {
+ uint32_t n = (uint32_t) len;
+ memcpy(q->buf + q->head, &n, sizeof(n));
+ MG_MEMORY_BARRIER();
+}
+
+size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) {
+ size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker
+ if (q->head >= q->tail && q->head + len + hs <= q->size) {
+ space = q->size - q->head - hs; // There is enough space
+ } else if (q->head >= q->tail && q->tail > hs) {
+ mg_queue_write_len(q, 0); // Not enough space ahead
+ q->head = 0; // Wrap head to the beginning
+ }
+ if (q->head + hs + len < q->tail) space = q->tail - q->head - hs;
+ if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t);
+ return space;
+}
+
+size_t mg_queue_next(struct mg_queue *q, char **buf) {
+ size_t len = 0;
+ if (q->tail != q->head) {
+ len = mg_queue_read_len(q);
+ if (len == 0) { // Zero (head wrapped) ?
+ q->tail = 0; // Reset tail to the start
+ if (q->head > q->tail) len = mg_queue_read_len(q); // Read again
+ }
+ }
+ if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t);
+ assert(q->tail + len <= q->size);
+ return len;
+}
+
+void mg_queue_add(struct mg_queue *q, size_t len) {
+ assert(len > 0);
+ mg_queue_write_len(q, len);
+ assert(q->head + sizeof(uint32_t) * 2 + len <= q->size);
+ q->head += len + sizeof(uint32_t);
+}
+
+void mg_queue_del(struct mg_queue *q, size_t len) {
+ q->tail += len + sizeof(uint32_t);
+ assert(q->tail + sizeof(uint32_t) <= q->size);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/rpc.c"
+#endif
+
+
+
+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->fn = fn;
+ rpc->fn_data = fn_data;
+ rpc->next = *head, *head = rpc;
+ }
+}
+
+void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) {
+ struct mg_rpc *r;
+ while ((r = *head) != NULL) {
+ if (r->fn == fn || fn == NULL) {
+ *head = r->next;
+ free((void *) r->method.buf);
+ free(r);
+ } else {
+ head = &(*head)->next;
+ }
+ }
+}
+
+static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) {
+ struct mg_rpc *h = r->head == NULL ? NULL : *r->head;
+ while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next;
+ if (h != NULL) {
+ r->rpc = h;
+ h->fn(r);
+ } else {
+ mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.buf);
+ }
+}
+
+void mg_rpc_process(struct mg_rpc_req *r) {
+ int len, off = mg_json_get(r->frame, "$.method", &len);
+ if (off > 0 && r->frame.buf[off] == '"') {
+ struct mg_str method = mg_str_n(&r->frame.buf[off + 1], (size_t) len - 2);
+ mg_rpc_call(r, method);
+ } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 ||
+ (off = mg_json_get(r->frame, "$.error", &len)) > 0) {
+ mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler
+ } else {
+ mg_rpc_err(r, -32700, "%m", mg_print_esc, (int) r->frame.len,
+ r->frame.buf); // Invalid
+ }
+}
+
+void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
+ int len, off = mg_json_get(r->frame, "$.id", &len);
+ if (off > 0) {
+ mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len,
+ &r->frame.buf[off], mg_print_esc, 0, "result");
+ mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
+ mg_xprintf(r->pfn, r->pfn_data, "}");
+ }
+}
+
+void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_rpc_vok(r, fmt, &ap);
+ va_end(ap);
+}
+
+void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
+ int len, off = mg_json_get(r->frame, "$.id", &len);
+ mg_xprintf(r->pfn, r->pfn_data, "{");
+ if (off > 0) {
+ mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len,
+ &r->frame.buf[off]);
+ }
+ mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error",
+ mg_print_esc, 0, "code", code, mg_print_esc, 0, "message");
+ mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
+ mg_xprintf(r->pfn, r->pfn_data, "}}");
+}
+
+void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ mg_rpc_verr(r, code, fmt, &ap);
+ va_end(ap);
+}
+
+static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
+ struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **);
+ size_t len = 0;
+ for (h = *head; h != NULL; h = h->next) {
+ if (h->method.len == 0) continue; // Ignore response handler
+ len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",",
+ mg_print_esc, (int) h->method.len, h->method.buf);
+ }
+ return len;
+}
+
+void mg_rpc_list(struct mg_rpc_req *r) {
+ mg_rpc_ok(r, "[%M]", print_methods, r->head);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sha1.c"
+#endif
+/* Copyright(c) By Steve Reid <steve@edmweb.com> */
+/* 100% Public Domain */
+
+
+
+union char64long16 {
+ unsigned char c[64];
+ uint32_t l[16];
+};
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static uint32_t blk0(union char64long16 *block, int i) {
+ if (MG_BIG_ENDIAN) {
+ } else {
+ block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
+ (rol(block->l[i], 8) & 0x00FF00FF);
+ }
+ return block->l[i];
+}
+
+/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
+#undef blk
+#undef R0
+#undef R1
+#undef R2
+#undef R3
+#undef R4
+
+#define blk(i) \
+ (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
+ block->l[(i + 2) & 15] ^ block->l[i & 15], \
+ 1))
+#define R0(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
+ w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w = rol(w, 30);
+
+static void mg_sha1_transform(uint32_t state[5],
+ const unsigned char *buffer) {
+ uint32_t a, b, c, d, e;
+ union char64long16 block[1];
+
+ memcpy(block, buffer, 64);
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Erase working structures. The order of operations is important,
+ * used to ensure that compiler doesn't optimize those out. */
+ memset(block, 0, sizeof(block));
+ a = b = c = d = e = 0;
+ (void) a;
+ (void) b;
+ (void) c;
+ (void) d;
+ (void) e;
+}
+
+void mg_sha1_init(mg_sha1_ctx *context) {
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data,
+ size_t len) {
+ size_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++;
+ context->count[1] += (uint32_t) (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ mg_sha1_transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ mg_sha1_transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) {
+ unsigned i;
+ unsigned char finalcount[8], c;
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ c = 0200;
+ mg_sha1_update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ mg_sha1_update(context, &c, 1);
+ }
+ mg_sha1_update(context, finalcount, 8);
+ for (i = 0; i < 20; i++) {
+ digest[i] =
+ (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sha256.c"
+#endif
+// https://github.com/B-Con/crypto-algorithms
+// Author: Brad Conte (brad AT bradconte.com)
+// Disclaimer: This code is presented "as is" without any guarantees.
+// Details: Defines the API for the corresponding SHA1 implementation.
+// Copyright: public domain
+
+
+
+#define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
+#define ch(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define ep0(x) (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22))
+#define ep1(x) (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25))
+#define sig0(x) (ror(x, 7) ^ ror(x, 18) ^ ((x) >> 3))
+#define sig1(x) (ror(x, 17) ^ ror(x, 19) ^ ((x) >> 10))
+
+static const uint32_t mg_sha256_k[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+void mg_sha256_init(mg_sha256_ctx *ctx) {
+ ctx->len = 0;
+ ctx->bits = 0;
+ ctx->state[0] = 0x6a09e667;
+ ctx->state[1] = 0xbb67ae85;
+ ctx->state[2] = 0x3c6ef372;
+ ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f;
+ ctx->state[5] = 0x9b05688c;
+ ctx->state[6] = 0x1f83d9ab;
+ ctx->state[7] = 0x5be0cd19;
+}
+
+static void mg_sha256_chunk(mg_sha256_ctx *ctx) {
+ int i, j;
+ uint32_t a, b, c, d, e, f, g, h;
+ uint32_t m[64];
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) |
+ ((uint32_t) ctx->buffer[j + 1] << 16) |
+ ((uint32_t) ctx->buffer[j + 2] << 8) |
+ ((uint32_t) ctx->buffer[j + 3]));
+ for (; i < 64; ++i)
+ m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16];
+
+ a = ctx->state[0];
+ b = ctx->state[1];
+ c = ctx->state[2];
+ d = ctx->state[3];
+ e = ctx->state[4];
+ f = ctx->state[5];
+ g = ctx->state[6];
+ h = ctx->state[7];
+
+ for (i = 0; i < 64; ++i) {
+ uint32_t t1 = h + ep1(e) + ch(e, f, g) + mg_sha256_k[i] + m[i];
+ uint32_t t2 = ep0(a) + maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + t1;
+ d = c;
+ c = b;
+ b = a;
+ a = t1 + t2;
+ }
+
+ ctx->state[0] += a;
+ ctx->state[1] += b;
+ ctx->state[2] += c;
+ ctx->state[3] += d;
+ ctx->state[4] += e;
+ ctx->state[5] += f;
+ ctx->state[6] += g;
+ ctx->state[7] += h;
+}
+
+void mg_sha256_update(mg_sha256_ctx *ctx, const unsigned char *data,
+ size_t len) {
+ size_t i;
+ for (i = 0; i < len; i++) {
+ ctx->buffer[ctx->len] = data[i];
+ if ((++ctx->len) == 64) {
+ mg_sha256_chunk(ctx);
+ ctx->bits += 512;
+ ctx->len = 0;
+ }
+ }
+}
+
+// TODO: make final reusable (remove side effects)
+void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *ctx) {
+ uint32_t i = ctx->len;
+ if (i < 56) {
+ ctx->buffer[i++] = 0x80;
+ while (i < 56) {
+ ctx->buffer[i++] = 0x00;
+ }
+ } else {
+ ctx->buffer[i++] = 0x80;
+ while (i < 64) {
+ ctx->buffer[i++] = 0x00;
+ }
+ mg_sha256_chunk(ctx);
+ memset(ctx->buffer, 0, 56);
+ }
+
+ ctx->bits += ctx->len * 8;
+ ctx->buffer[63] = (uint8_t) ((ctx->bits) & 0xff);
+ ctx->buffer[62] = (uint8_t) ((ctx->bits >> 8) & 0xff);
+ ctx->buffer[61] = (uint8_t) ((ctx->bits >> 16) & 0xff);
+ ctx->buffer[60] = (uint8_t) ((ctx->bits >> 24) & 0xff);
+ ctx->buffer[59] = (uint8_t) ((ctx->bits >> 32) & 0xff);
+ ctx->buffer[58] = (uint8_t) ((ctx->bits >> 40) & 0xff);
+ ctx->buffer[57] = (uint8_t) ((ctx->bits >> 48) & 0xff);
+ ctx->buffer[56] = (uint8_t) ((ctx->bits >> 56) & 0xff);
+ mg_sha256_chunk(ctx);
+
+ for (i = 0; i < 4; ++i) {
+ digest[i] = (uint8_t) ((ctx->state[0] >> (24 - i * 8)) & 0xff);
+ digest[i + 4] = (uint8_t) ((ctx->state[1] >> (24 - i * 8)) & 0xff);
+ digest[i + 8] = (uint8_t) ((ctx->state[2] >> (24 - i * 8)) & 0xff);
+ digest[i + 12] = (uint8_t) ((ctx->state[3] >> (24 - i * 8)) & 0xff);
+ digest[i + 16] = (uint8_t) ((ctx->state[4] >> (24 - i * 8)) & 0xff);
+ digest[i + 20] = (uint8_t) ((ctx->state[5] >> (24 - i * 8)) & 0xff);
+ digest[i + 24] = (uint8_t) ((ctx->state[6] >> (24 - i * 8)) & 0xff);
+ digest[i + 28] = (uint8_t) ((ctx->state[7] >> (24 - i * 8)) & 0xff);
+ }
+}
+
+void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
+ size_t datasz) {
+ mg_sha256_ctx ctx;
+ uint8_t k[64] = {0};
+ uint8_t o_pad[64], i_pad[64];
+ unsigned int i;
+ memset(i_pad, 0x36, sizeof(i_pad));
+ memset(o_pad, 0x5c, sizeof(o_pad));
+ if (keysz < 64) {
+ if (keysz > 0) memmove(k, key, keysz);
+ } else {
+ mg_sha256_init(&ctx);
+ mg_sha256_update(&ctx, key, keysz);
+ mg_sha256_final(k, &ctx);
+ }
+ for (i = 0; i < sizeof(k); i++) {
+ i_pad[i] ^= k[i];
+ o_pad[i] ^= k[i];
+ }
+ mg_sha256_init(&ctx);
+ mg_sha256_update(&ctx, i_pad, sizeof(i_pad));
+ mg_sha256_update(&ctx, data, datasz);
+ mg_sha256_final(dst, &ctx);
+ mg_sha256_init(&ctx);
+ mg_sha256_update(&ctx, o_pad, sizeof(o_pad));
+ mg_sha256_update(&ctx, dst, 32);
+ mg_sha256_final(dst, &ctx);
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sntp.c"
+#endif
+
+
+
+
+
+
+#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds
+#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1
+
+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;
+ return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0);
+}
+
+int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
+ int64_t res = -1;
+ int mode = len > 0 ? buf[0] & 7 : 0;
+ int version = len > 0 ? (buf[0] >> 3) & 7 : 0;
+ if (len < 48) {
+ MG_ERROR(("%s", "corrupt packet"));
+ } else if (mode != 4 && mode != 5) {
+ MG_ERROR(("%s", "not a server reply"));
+ } else if (buf[1] == 0) {
+ 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;
+ } else {
+ MG_ERROR(("unexpected version: %d", version));
+ }
+ return res;
+}
+
+static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) {
+ 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));
+ mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
+ MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
+ (unsigned) (milliseconds % 1000)));
+ }
+ mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer
+ } else if (ev == MG_EV_CONNECT) {
+ mg_sntp_request(c);
+ } else if (ev == MG_EV_CLOSE) {
+ }
+ (void) ev_data;
+}
+
+void mg_sntp_request(struct mg_connection *c) {
+ if (c->is_resolving) {
+ MG_ERROR(("%lu wait until resolved", c->id));
+ } else {
+ int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98
+ uint8_t buf[48] = {0};
+ uint32_t *t = (uint32_t *) &buf[40];
+ double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC;
+ buf[0] = (0 << 6) | (4 << 3) | 3;
+ t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET);
+ t[1] = mg_htonl((uint32_t) frac);
+ mg_send(c, buf, sizeof(buf));
+ }
+}
+
+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;
+ return c;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/sock.c"
+#endif
+
+
+
+
+
+
+
+
+
+
+
+#if MG_ENABLE_SOCKET
+
+#ifndef closesocket
+#define closesocket(x) close(x)
+#endif
+
+#define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd)
+#define S2PTR(s_) ((void *) (size_t) (s_))
+
+#ifndef MSG_NONBLOCKING
+#define MSG_NONBLOCKING 0
+#endif
+
+#ifndef AF_INET6
+#define AF_INET6 10
+#endif
+
+#ifndef MG_SOCK_ERR
+#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0)
+#endif
+
+#ifndef MG_SOCK_INTR
+#define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR)
+#endif
+
+#ifndef MG_SOCK_PENDING
+#define MG_SOCK_PENDING(errcode) \
+ (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK))
+#endif
+
+#ifndef MG_SOCK_RESET
+#define MG_SOCK_RESET(errcode) \
+ (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET))
+#endif
+
+union usa {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if MG_ENABLE_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+};
+
+static socklen_t tousa(struct mg_addr *a, union usa *usa) {
+ socklen_t len = sizeof(usa->sin);
+ memset(usa, 0, sizeof(*usa));
+ usa->sin.sin_family = AF_INET;
+ usa->sin.sin_port = a->port;
+ memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t));
+#if MG_ENABLE_IPV6
+ if (a->is_ip6) {
+ usa->sin.sin_family = AF_INET6;
+ usa->sin6.sin6_port = a->port;
+ usa->sin6.sin6_scope_id = a->scope_id;
+ memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip));
+ len = sizeof(usa->sin6);
+ }
+#endif
+ return len;
+}
+
+static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
+ a->is_ip6 = is_ip6;
+ a->port = usa->sin.sin_port;
+ memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t));
+#if MG_ENABLE_IPV6
+ if (is_ip6) {
+ memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip));
+ a->port = usa->sin6.sin6_port;
+ a->scope_id = (uint8_t) usa->sin6.sin6_scope_id;
+ }
+#endif
+}
+
+static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
+ union usa usa;
+ socklen_t n = sizeof(usa);
+ if (getsockname(fd, &usa.sa, &n) == 0) {
+ tomgaddr(&usa, addr, n != sizeof(usa.sin));
+ }
+}
+
+static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
+ if (n == MG_IO_WAIT) {
+ // Do nothing
+ } else if (n <= 0) {
+ c->is_closing = 1; // Termination. Don't call mg_error(): #1529
+ } else if (n > 0) {
+ if (c->is_hexdumping) {
+ MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc,
+ r ? "<-" : "->", mg_print_ip_port, &c->rem, n));
+ mg_hexdump(buf, (size_t) n);
+ }
+ if (r) {
+ c->recv.len += (size_t) n;
+ mg_call(c, MG_EV_READ, &n);
+ } else {
+ mg_iobuf_del(&c->send, 0, (size_t) n);
+ // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
+ if (c->send.len == 0) {
+ MG_EPOLL_MOD(c, 0);
+ }
+ mg_call(c, MG_EV_WRITE, &n);
}
}
- return flags;
}
-static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts, char *path,
- size_t path_size) {
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
- while (mg_commalist(&s, &k, &v)) {
- if (v.len == 0) v = k, k = mg_str("/");
- if (hm->uri.len < k.len) continue;
- if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
- u = k, p = v;
+long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
+ long n;
+ if (c->is_udp) {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
+ if (n > 0) setlocaddr(FD(c), &c->loc);
+ } else {
+ n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
}
- return uri_to_path2(c, hm, fs, u, p, path, path_size);
+ MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n)));
+ if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
+ if (MG_SOCK_RESET(n)) return MG_IO_RESET;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
}
-void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts) {
- char path[MG_PATH_MAX];
- const char *sp = opts->ssi_pattern;
- int flags = uri_to_path(c, hm, opts, path, sizeof(path));
- if (flags < 0) {
- // Do nothing: the response has already been sent by uri_to_path()
- } else if (flags & MG_FS_DIR) {
- listdir(c, hm, opts, path);
- } else if (flags && sp != NULL &&
- mg_globmatch(sp, strlen(sp), path, strlen(path))) {
- mg_http_serve_ssi(c, opts->root_dir, path);
+bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
+ if (c->is_udp) {
+ long n = mg_io_send(c, buf, len);
+ MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len,
+ c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n)));
+ iolog(c, (char *) buf, n, false);
+ return n > 0;
} else {
- mg_http_serve_file(c, hm, path, opts);
+ return mg_iobuf_add(&c->send, c->send.len, buf, len);
}
}
-static bool mg_is_url_safe(int c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
+static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
+#if defined(MG_CUSTOM_NONBLOCK)
+ MG_CUSTOM_NONBLOCK(fd);
+#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
+ unsigned long on = 1;
+ ioctlsocket(fd, FIONBIO, &on);
+#elif MG_ENABLE_RL
+ unsigned long on = 1;
+ ioctlsocket(fd, FIONBIO, &on);
+#elif MG_ENABLE_FREERTOS_TCP
+ const BaseType_t off = 0;
+ if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0;
+ if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0;
+#elif MG_ENABLE_LWIP
+ lwip_fcntl(fd, F_SETFL, O_NONBLOCK);
+#elif MG_ARCH == MG_ARCH_AZURERTOS
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#elif MG_ARCH == MG_ARCH_TIRTOS
+ int val = 0;
+ setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
+ // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT
+ int sz = sizeof(val);
+ getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz);
+ val /= 2; // set send low-water mark at half send buffer size
+ setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val));
+#else
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode
+ fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
+#endif
}
-size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
- size_t i, n = 0;
- for (i = 0; i < sl; i++) {
- int c = *(unsigned char *) &s[i];
- if (n + 4 >= len) return 0;
- if (mg_is_url_safe(c)) {
- buf[n++] = s[i];
+bool mg_open_listener(struct mg_connection *c, const char *url) {
+ MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
+ bool success = false;
+ c->loc.port = mg_htons(mg_url_port(url));
+ if (!mg_aton(mg_url_host(url), &c->loc)) {
+ MG_ERROR(("invalid listening URL: %s", url));
+ } else {
+ union usa usa;
+ socklen_t slen = tousa(&c->loc, &usa);
+ int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
+ int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
+ int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+ (void) on;
+
+ if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
+ MG_ERROR(("socket: %d", MG_SOCK_ERR(-1)));
+#if defined(SO_EXCLUSIVEADDRUSE)
+ } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (char *) &on, sizeof(on))) != 0) {
+ // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
+ MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc)));
+#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
+ } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
+ sizeof(on))) != 0) {
+ // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
+ // Windows, SO_REUSEADDR allows to bind a socket to a port without error
+ // even if the port is already open by another program. This is not the
+ // behavior SO_REUSEADDR was designed for, and leads to hard-to-track
+ // failure scenarios.
+ //
+ // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
+ // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
+ // won't work! (setsockopt will return EINVAL)
+ MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc)));
+#endif
+#if MG_IPV6_V6ONLY
+ // Bind only to the V6 address, not V4 address on this port
+ } else if (c->loc.is_ip6 &&
+ (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on,
+ sizeof(on))) != 0) {
+ // See #2089. Allow to bind v4 and v6 sockets on the same port
+ MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc)));
+#endif
+ } else if ((rc = bind(fd, &usa.sa, slen)) != 0) {
+ MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
+ } else if ((type == SOCK_STREAM &&
+ (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) {
+ // NOTE(lsm): FreeRTOS uses backlog value as a connection limit
+ // In case port was set to 0, get the real port number
+ MG_ERROR(("listen: %d", MG_SOCK_ERR(rc)));
} else {
- buf[n++] = '%';
- mg_hex(&s[i], 1, &buf[n]);
- n += 2;
+ setlocaddr(fd, &c->loc);
+ mg_set_non_blocking_mode(fd);
+ c->fd = S2PTR(fd);
+ MG_EPOLL_ADD(c);
+ success = true;
}
}
- if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
- if (len > 0) buf[len - 1] = '\0'; // Always.
- return n;
+ if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd);
+ return success;
}
-void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen,
- char *pass, size_t passlen) {
- struct mg_str *v = mg_http_get_header(hm, "Authorization");
- user[0] = pass[0] = '\0';
- if (v != NULL && v->len > 6 && memcmp(v->ptr, "Basic ", 6) == 0) {
- char buf[256];
- int n = mg_base64_decode(v->ptr + 6, (int) v->len - 6, buf);
- const char *p = (const char *) memchr(buf, ':', n > 0 ? (size_t) n : 0);
- if (p != NULL) {
- mg_snprintf(user, userlen, "%.*s", (int) (p - buf), buf);
- mg_snprintf(pass, passlen, "%.*s", n - (int) (p - buf) - 1, p + 1);
- }
- } else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) {
- mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->ptr + 7);
- } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
- struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
- if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.ptr);
+static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
+ long n = 0;
+ if (c->is_udp) {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
+ if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
} else {
- mg_http_get_var(&hm->query, "access_token", pass, passlen);
+ n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
}
+ MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n)));
+ if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
+ if (MG_SOCK_RESET(n)) return MG_IO_RESET;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
}
-static struct mg_str stripquotes(struct mg_str s) {
- return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"'
- ? mg_str_n(s.ptr + 1, s.len - 2)
- : s;
+static bool ioalloc(struct mg_connection *c, struct mg_iobuf *io) {
+ bool res = false;
+ if (io->len >= MG_MAX_RECV_SIZE) {
+ mg_error(c, "MG_MAX_RECV_SIZE");
+ } else if (io->size <= io->len &&
+ !mg_iobuf_resize(io, io->size + MG_IO_SIZE)) {
+ mg_error(c, "OOM");
+ } else {
+ res = true;
+ }
+ return res;
}
-struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
- size_t i;
- for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) {
- if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
- const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
- int q = p < x && *p == '"' ? 1 : 0;
- while (p < x &&
- (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ','))
- p++;
- // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
- // v.ptr, (int) (p - b), b));
- return stripquotes(mg_str_n(b, (size_t) (p - b + q)));
+// NOTE(lsm): do only one iteration of reads, cause some systems
+// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data
+static void read_conn(struct mg_connection *c) {
+ if (ioalloc(c, &c->recv)) {
+ char *buf = (char *) &c->recv.buf[c->recv.len];
+ 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 {
+ 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);
+ }
+ } else {
+ n = recv_raw(c, buf, len);
}
+ MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len,
+ c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n)));
+ iolog(c, buf, n, true);
}
- return mg_str_n(NULL, 0);
}
-bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
- return mg_match(hm->uri, mg_str(glob), NULL);
+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);
+ 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)));
+ iolog(c, buf, n, false);
}
-long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, const char *path, size_t max_size) {
- char buf[20] = "0";
- long res = 0, offset;
- mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
- offset = strtol(buf, NULL, 0);
- if (hm->body.len == 0) {
- mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
+static void close_conn(struct mg_connection *c) {
+ if (FD(c) != MG_INVALID_SOCKET) {
+#if MG_ENABLE_EPOLL
+ epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL);
+#endif
+ closesocket(FD(c));
+#if MG_ENABLE_FREERTOS_TCP
+ FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
+#endif
+ }
+ mg_close_conn(c);
+}
+
+static void connect_conn(struct mg_connection *c) {
+ union usa usa;
+ socklen_t n = sizeof(usa);
+ // Use getpeername() to test whether we have connected
+ if (getpeername(FD(c), &usa.sa, &n) == 0) {
+ c->is_connecting = 0;
+ setlocaddr(FD(c), &c->loc);
+ mg_call(c, MG_EV_CONNECT, NULL);
+ MG_EPOLL_MOD(c, 0);
+ if (c->is_tls_hs) mg_tls_handshake(c);
} else {
- struct mg_fd *fd;
- size_t current_size = 0;
- MG_DEBUG(("%s -> %d bytes @ %ld", path, (int) hm->body.len, offset));
- if (offset == 0) fs->rm(path); // If offset if 0, truncate file
- fs->st(path, ¤t_size, NULL);
- if (offset < 0) {
- mg_http_reply(c, 400, "", "offset required");
- res = -1;
- } else if (offset > 0 && current_size != (size_t) offset) {
- mg_http_reply(c, 400, "", "%s: offset mismatch", path);
- res = -2;
- } else if ((size_t) offset + hm->body.len > max_size) {
- mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
- (unsigned long) max_size);
- res = -3;
- } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
- mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
- res = -4;
+ mg_error(c, "socket error");
+ }
+}
+
+static void setsockopts(struct mg_connection *c) {
+#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \
+ MG_ARCH == MG_ARCH_TIRTOS
+ (void) c;
+#else
+ int on = 1;
+#if !defined(SOL_TCP)
+#define SOL_TCP IPPROTO_TCP
+#endif
+ if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0)
+ (void) 0;
+ if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) !=
+ 0)
+ (void) 0;
+#endif
+}
+
+void mg_connect_resolved(struct mg_connection *c) {
+ int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
+ 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->is_resolving = 0; // Clear resolving flag
+ if (FD(c) == MG_INVALID_SOCKET) {
+ mg_error(c, "socket(): %d", MG_SOCK_ERR(-1));
+ } else if (c->is_udp) {
+ MG_EPOLL_ADD(c);
+#if MG_ARCH == MG_ARCH_TIRTOS
+ union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets
+ socklen_t slen = tousa(&c->loc, &usa);
+ if ((rc = bind(c->fd, &usa.sa, slen)) != 0)
+ MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
+#endif
+ setlocaddr(FD(c), &c->loc);
+ mg_call(c, MG_EV_RESOLVE, NULL);
+ mg_call(c, MG_EV_CONNECT, NULL);
+ } else {
+ union usa usa;
+ socklen_t slen = tousa(&c->rem, &usa);
+ mg_set_non_blocking_mode(FD(c));
+ setsockopts(c);
+ MG_EPOLL_ADD(c);
+ mg_call(c, MG_EV_RESOLVE, NULL);
+ 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
+ } 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 {
- res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
- mg_fs_close(fd);
- mg_http_reply(c, 200, "", "%ld", res);
+ mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
}
}
- return res;
-}
-
-int mg_http_status(const struct mg_http_message *hm) {
- return atoi(hm->uri.ptr);
-}
-
-// If a server sends data to the client using chunked encoding, Mongoose strips
-// off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the
-// stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero
-// chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all
-// chunking prefixes/suffixes stripped.
-//
-// If a server sends data without chunked encoding, we also fire a series of
-// MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire
-// MG_EV_HTTP_MSG event in the end.
-//
-// We track total processed length in the c->pfn_data, which is a void *
-// pointer: we store a size_t value there.
-static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) {
- size_t i = 0, n;
- while (i < s.len && s.ptr[i] != '\r' && s.ptr[i] != '\n') i++;
- n = mg_unhexn(s.ptr, i);
- // MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len));
- if (s.len < i + n + 4) return false; // Chunk not yet fully buffered
- if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') return false;
- if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') return false;
- *prefixlen = i + 2;
- *datalen = n;
- return true;
}
-static bool mg_is_chunked(struct mg_http_message *hm) {
- const char *needle = "chunked";
- struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
- return te != NULL && mg_vcasecmp(te, needle) == 0;
-}
-
-void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
- size_t ofs = (size_t) (hm->chunk.ptr - (char *) c->recv.buf);
- mg_iobuf_del(&c->recv, ofs, hm->chunk.len);
- c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK);
-}
-
-static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen,
- struct mg_http_message *hm, bool *next) {
- // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ......
- // +------------------+--------------------------+----
- // | hlen | chunk1 | ......
- char *buf = (char *) &c->recv.buf[hlen], *p = buf;
- size_t len = c->recv.len - hlen;
- size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK;
- size_t mark, pl, dl, del = 0, ofs = 0;
- bool last = false;
- if (processed <= len) len -= processed, buf += processed;
- while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) {
- size_t saved = c->recv.len;
- memmove(p + processed, buf + ofs + pl, dl);
- // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p));
- hm->chunk = mg_str_n(p + processed, dl);
- mg_call(c, MG_EV_HTTP_CHUNK, hm);
- ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix
- processed += dl;
- if (c->recv.len != saved) processed -= dl, buf -= dl;
- // mg_hexdump(c->recv.buf, hlen + processed);
- last = (dl == 0);
- }
- mg_iobuf_del(&c->recv, hlen + processed, del);
- mark = ((size_t) c->pfn_data) & MG_DMARK;
- c->pfn_data = (void *) (processed | mark);
- if (last) {
- hm->body.len = processed;
- hm->message.len = hlen + processed;
- c->pfn_data = NULL;
- if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
- // MG_INFO(("LAST, mark: %lx", mark));
- // mg_hexdump(c->recv.buf, c->recv.len);
- }
+static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa,
+ socklen_t *len) {
+ MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
+ do {
+ memset(usa, 0, sizeof(*usa));
+ fd = accept(sock, &usa->sa, len);
+ } while (MG_SOCK_INTR(fd));
+ return fd;
}
-static void deliver_normal_chunks(struct mg_connection *c, size_t hlen,
- struct mg_http_message *hm, bool *next) {
- size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK;
- size_t deleted = ((size_t) c->pfn_data) & MG_DMARK;
- hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen);
- if (processed <= hm->chunk.len && !deleted) {
- hm->chunk.len -= processed;
- hm->chunk.ptr += processed;
- }
- left = hm->body.len < processed ? 0 : hm->body.len - processed;
- if (hm->chunk.len > left) hm->chunk.len = left;
- if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm);
- processed += hm->chunk.len;
- deleted = ((size_t) c->pfn_data) & MG_DMARK; // Re-evaluate after user call
- if (processed >= hm->body.len) { // Last, 0-len chunk
- hm->chunk.len = 0; // Reset length
- mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler
- c->pfn_data = NULL; // Reset processed counter
- if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
+static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
+ struct mg_connection *c = NULL;
+ union usa usa;
+ socklen_t sa_len = sizeof(usa);
+ MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len);
+ if (fd == MG_INVALID_SOCKET) {
+#if MG_ARCH == MG_ARCH_AZURERTOS || defined(__ECOS)
+ // AzureRTOS, in non-block socket mode can mark listening socket readable
+ // even it is not. See comment for 'select' func implementation in
+ // nx_bsd.c That's not an error, just should try later
+ if (errno != EAGAIN)
+#endif
+ MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1)));
+#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \
+ (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL && !MG_ENABLE_EPOLL
+ } else if ((long) fd >= FD_SETSIZE) {
+ MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE));
+ closesocket(fd);
+#endif
+ } else if ((c = mg_alloc_conn(mgr)) == NULL) {
+ MG_ERROR(("%lu OOM", lsn->id));
+ closesocket(fd);
} else {
- c->pfn_data = (void *) (processed | deleted); // if it is set
+ tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin));
+ LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+ c->fd = S2PTR(fd);
+ MG_EPOLL_ADD(c);
+ mg_set_non_blocking_mode(FD(c));
+ setsockopts(c);
+ c->is_accepted = 1;
+ c->is_hexdumping = lsn->is_hexdumping;
+ c->loc = lsn->loc;
+ c->pfn = lsn->pfn;
+ c->pfn_data = lsn->pfn_data;
+ c->fn = lsn->fn;
+ c->fn_data = lsn->fn_data;
+ MG_DEBUG(("%lu %ld accepted %M -> %M", c->id, c->fd, mg_print_ip_port,
+ &c->rem, mg_print_ip_port, &c->loc));
+ mg_call(c, MG_EV_OPEN, NULL);
+ mg_call(c, MG_EV_ACCEPT, NULL);
}
}
-static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
- if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
- struct mg_http_message hm;
- // mg_hexdump(c->recv.buf, c->recv.len);
- while (c->recv.buf != NULL && c->recv.len > 0) {
- bool next = false;
- int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
- if (hlen < 0) {
- mg_error(c, "HTTP parse:\n%.*s", (int) c->recv.len, c->recv.buf);
- break;
- }
- if (c->is_resp) break; // Response is still generated
- if (hlen == 0) break; // Request is not buffered yet
- if (ev == MG_EV_CLOSE) { // If client did not set Content-Length
- hm.message.len = c->recv.len; // and closes now, deliver a MSG
- hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr);
- }
- if (mg_is_chunked(&hm)) {
- deliver_chunked_chunks(c, (size_t) hlen, &hm, &next);
- } else {
- deliver_normal_chunks(c, (size_t) hlen, &hm, &next);
- }
- if (next) continue; // Chunks & request were deleted
- // Chunk events are delivered. If we have full body, deliver MSG
- if (c->recv.len < hm.message.len) break;
- 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
- mg_iobuf_del(&c->recv, 0, hm.message.len);
- }
- }
- (void) evd, (void) fnd;
+static bool can_read(const struct mg_connection *c) {
+ return c->is_full == false;
}
-struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = http_cb;
- return c;
+static bool can_write(const struct mg_connection *c) {
+ return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0);
}
-struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = http_cb;
- return c;
+static bool skip_iotest(const struct mg_connection *c) {
+ return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) ||
+ (can_read(c) == false && can_write(c) == false);
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/iobuf.c"
-#endif
-
-
-
-
-// Not using memset for zeroing memory, cause it can be dropped by compiler
-// See https://github.com/cesanta/mongoose/pull/1265
-static void zeromem(volatile unsigned char *buf, size_t len) {
- if (buf != NULL) {
- while (len--) *buf++ = 0;
+static void mg_iotest(struct mg_mgr *mgr, int ms) {
+#if MG_ENABLE_FREERTOS_TCP
+ struct mg_connection *c;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (skip_iotest(c)) continue;
+ if (can_read(c))
+ FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT);
+ if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE);
+ if (c->is_closing) ms = 1;
}
-}
-
-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;
- new_size = roundup(new_size, io->align);
- if (new_size == 0) {
- zeromem(io->buf, io->size);
- free(io->buf);
- io->buf = NULL;
- io->len = io->size = 0;
- } else if (new_size != io->size) {
- // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the
- // porting to some obscure platforms like FreeRTOS
- void *p = calloc(1, new_size);
- if (p != NULL) {
- size_t len = new_size < io->len ? new_size : io->len;
- if (len > 0 && io->buf != NULL) memmove(p, io->buf, len);
- zeromem(io->buf, io->size);
- free(io->buf);
- io->buf = (unsigned char *) p;
- io->size = new_size;
+ FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms));
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
+ c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
+ c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
+ if (c->fd != MG_INVALID_SOCKET)
+ FreeRTOS_FD_CLR(c->fd, mgr->ss,
+ eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
+ }
+#elif MG_ENABLE_EPOLL
+ size_t max = 1;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1;
+ if (can_write(c)) MG_EPOLL_MOD(c, 1);
+ if (c->is_closing) ms = 1;
+ max++;
+ }
+ struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0]));
+ int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms);
+ for (int i = 0; i < n; i++) {
+ struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr;
+ if (evs[i].events & EPOLLERR) {
+ mg_error(c, "socket error");
+ } else if (c->is_readable == 0) {
+ bool rd = evs[i].events & (EPOLLIN | EPOLLHUP);
+ bool wr = evs[i].events & EPOLLOUT;
+ c->is_readable = can_read(c) && rd ? 1U : 0;
+ c->is_writable = can_write(c) && wr ? 1U : 0;
+ if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
+ }
+ }
+ (void) skip_iotest;
+#elif MG_ENABLE_POLL
+ nfds_t n = 0;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++;
+ struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0]));
+ memset(fds, 0, n * sizeof(fds[0]));
+ n = 0;
+ for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ 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 {
- ok = 0;
- MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
+ 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++;
}
}
- return ok;
-}
-
-int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
- io->buf = NULL;
- io->align = align;
- io->size = io->len = 0;
- return mg_iobuf_resize(io, size);
-}
-size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf,
- size_t len) {
- size_t new_size = roundup(io->len + len, io->align);
- mg_iobuf_resize(io, new_size); // Attempt to resize
- if (new_size != io->size) len = 0; // Resize failure, append nothing
- if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs);
- if (buf != NULL) memmove(io->buf + ofs, buf, len);
- if (ofs > io->len) io->len += ofs - io->len;
- io->len += len;
- return len;
-}
+ // MG_INFO(("poll n=%d ms=%d", (int) n, ms));
+ if (poll(fds, n, ms) < 0) {
+#if MG_ARCH == MG_ARCH_WIN32
+ if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets
+#endif
+ memset(fds, 0, n * sizeof(fds[0]));
+ }
+ n = 0;
+ 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");
+ } else {
+ c->is_readable =
+ (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0);
+ c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0);
+ if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
+ }
+ n++;
+ }
+ }
+#else
+ struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp;
+ struct mg_connection *c;
+ fd_set rset, wset, eset;
+ MG_SOCKET_TYPE maxfd = 0;
+ int rc;
-size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) {
- if (ofs > io->len) ofs = io->len;
- if (ofs + len > io->len) len = io->len - ofs;
- if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len);
- if (io->buf) zeromem(io->buf + io->len - len, len);
- io->len -= len;
- return len;
-}
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ tvp = ms < 0 ? NULL : &tv;
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ c->is_readable = c->is_writable = 0;
+ if (skip_iotest(c)) continue;
+ FD_SET(FD(c), &eset);
+ if (can_read(c)) FD_SET(FD(c), &rset);
+ 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;
+ }
-void mg_iobuf_free(struct mg_iobuf *io) {
- mg_iobuf_resize(io, 0);
-}
+ if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) {
+#if MG_ARCH == MG_ARCH_WIN32
+ if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets
+#else
+ MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc)));
+#endif
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&eset);
+ }
-#ifdef MG_ENABLE_LINES
-#line 1 "src/json.c"
+ for (c = mgr->conns; c != NULL; c = c->next) {
+ if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) {
+ mg_error(c, "socket error");
+ } else {
+ c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset);
+ c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset);
+ if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
+ }
+ }
#endif
+}
+static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) {
+ socklen_t n = sizeof(usa[0].sin);
+ bool success = false;
+ sp[0] = sp[1] = MG_INVALID_SOCKET;
+ (void) memset(&usa[0], 0, sizeof(usa[0]));
+ usa[0].sin.sin_family = AF_INET;
+ *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1
+ usa[1] = usa[0];
-
-static const char *escapeseq(int esc) {
- return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\"";
+ if ((sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
+ (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != 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 && //
+ getsockname(sp[1], &usa[1].sa, &n) == 0 && //
+ connect(sp[0], &usa[1].sa, n) == 0 && //
+ connect(sp[1], &usa[0].sa, n) == 0) { //
+ success = true;
+ }
+ if (!success) {
+ if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]);
+ if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]);
+ sp[0] = sp[1] = MG_INVALID_SOCKET;
+ }
+ return success;
}
-static char json_esc(int c, int esc) {
- const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc);
- for (p = esc1; *p != '\0'; p++) {
- if (*p == c) return esc2[p - esc1];
+// mg_wakeup() event handler
+static void wufn(struct mg_connection *c, int ev, void *ev_data) {
+ if (ev == MG_EV_READ) {
+ unsigned long *id = (unsigned long *) c->recv.buf;
+ // MG_INFO(("Got data"));
+ // mg_hexdump(c->recv.buf, c->recv.len);
+ if (c->recv.len >= sizeof(*id)) {
+ struct mg_connection *t;
+ for (t = c->mgr->conns; t != NULL; t = t->next) {
+ if (t->id == *id) {
+ struct mg_str data = mg_str_n((char *) c->recv.buf + sizeof(*id),
+ c->recv.len - sizeof(*id));
+ mg_call(t, MG_EV_WAKEUP, &data);
+ }
+ }
+ }
+ c->recv.len = 0; // Consume received data
+ } else if (ev == MG_EV_CLOSE) {
+ closesocket(c->mgr->pipe); // When we're closing, close the other
+ c->mgr->pipe = MG_INVALID_SOCKET; // side of the socketpair, too
}
- return 0;
+ (void) ev_data;
}
-static int mg_pass_string(const char *s, int len) {
- int i;
- for (i = 0; i < len; i++) {
- if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) {
- i++;
- } else if (s[i] == '\0') {
- return MG_JSON_INVALID;
- } else if (s[i] == '"') {
- return i;
+bool mg_wakeup_init(struct mg_mgr *mgr) {
+ bool ok = false;
+ if (mgr->pipe == MG_INVALID_SOCKET) {
+ union usa usa[2];
+ MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET};
+ struct mg_connection *c = NULL;
+ if (!mg_socketpair(sp, usa)) {
+ MG_ERROR(("Cannot create socket pair"));
+ } else if ((c = mg_wrapfd(mgr, (int) sp[1], wufn, NULL)) == NULL) {
+ closesocket(sp[0]);
+ closesocket(sp[1]);
+ sp[0] = sp[1] = MG_INVALID_SOCKET;
+ } else {
+ tomgaddr(&usa[0], &c->rem, false);
+ MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0]));
+ mgr->pipe = sp[0];
+ ok = true;
}
}
- return MG_JSON_INVALID;
+ return ok;
}
-static double mg_atod(const char *p, int len, int *numlen) {
- double d = 0.0;
- int i = 0, sign = 1;
-
- // Sign
- if (i < len && *p == '-') {
- sign = -1, i++;
- } else if (i < len && *p == '+') {
- i++;
+bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf,
+ size_t len) {
+ if (mgr->pipe != MG_INVALID_SOCKET && conn_id > 0) {
+ char *extended_buf = (char *) alloca(len + sizeof(conn_id));
+ memcpy(extended_buf, &conn_id, sizeof(conn_id));
+ memcpy(extended_buf + sizeof(conn_id), buf, len);
+ send(mgr->pipe, extended_buf, len + sizeof(conn_id), MSG_NONBLOCKING);
+ return true;
}
+ return false;
+}
- // Decimal
- for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
- d *= 10.0;
- d += p[i] - '0';
- }
- d *= sign;
+void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
+ struct mg_connection *c, *tmp;
+ uint64_t now;
- // Fractional
- if (i < len && p[i] == '.') {
- double frac = 0.0, base = 0.1;
- i++;
- for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
- frac += base * (p[i] - '0');
- base /= 10.0;
+ mg_iotest(mgr, ms);
+ now = mg_millis();
+ mg_timer_poll(&mgr->timers, now);
+
+ for (c = mgr->conns; c != NULL; c = tmp) {
+ bool is_resp = c->is_resp;
+ tmp = c->next;
+ mg_call(c, MG_EV_POLL, &now);
+ if (is_resp && !c->is_resp) {
+ long n = 0;
+ mg_call(c, MG_EV_READ, &n);
+ }
+ MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id,
+ c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-',
+ c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c',
+ c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r',
+ c->is_closing ? 'C' : 'c', mg_tls_pending(c), c->rtls.len));
+ if (c->is_resolving || c->is_closing) {
+ // Do nothing
+ } else if (c->is_listening && c->is_udp == 0) {
+ if (c->is_readable) accept_conn(mgr, c);
+ } else if (c->is_connecting) {
+ if (c->is_readable || c->is_writable) connect_conn(c);
+ //} else if (c->is_tls_hs) {
+ // if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
+ } else {
+ if (c->is_readable) read_conn(c);
+ if (c->is_writable) write_conn(c);
}
- d += frac * sign;
- }
- // Exponential
- if (i < len && (p[i] == 'e' || p[i] == 'E')) {
- int j, exp = 0, minus = 0;
- i++;
- if (i < len && p[i] == '-') minus = 1, i++;
- if (i < len && p[i] == '+') i++;
- while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
- exp = exp * 10 + (p[i++] - '0');
- if (minus) exp = -exp;
- for (j = 0; j < exp; j++) d *= 10.0;
- for (j = 0; j < -exp; j++) d /= 10.0;
+ if (c->is_draining && c->send.len == 0) c->is_closing = 1;
+ if (c->is_closing) close_conn(c);
}
-
- if (numlen != NULL) *numlen = i;
- return d;
}
+#endif
-int mg_json_get(struct mg_str json, const char *path, int *toklen) {
- const char *s = json.ptr;
- int len = (int) json.len;
- enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
- unsigned char nesting[MG_JSON_MAX_DEPTH];
- int i = 0; // Current offset in `s`
- int j = 0; // Offset in `s` we're looking for (return value)
- int depth = 0; // Current depth (nesting level)
- int ed = 0; // Expected depth
- int pos = 1; // Current position in `path`
- int ci = -1, ei = -1; // Current and expected index in array
+#ifdef MG_ENABLE_LINES
+#line 1 "src/ssi.c"
+#endif
- if (toklen) *toklen = 0;
- if (path[0] != '$') return MG_JSON_INVALID;
-#define MG_CHECKRET(x) \
- do { \
- if (depth == ed && path[pos] == '\0' && ci == ei) { \
- if (toklen) *toklen = i - j + 1; \
- return j; \
- } \
- } while (0)
-// In the ascii table, the distance between `[` and `]` is 2.
-// Ditto for `{` and `}`. Hence +2 in the code below.
-#define MG_EOO(x) \
- do { \
- if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \
- if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \
- depth--; \
- MG_CHECKRET(x); \
- } while (0)
- for (i = 0; i < len; i++) {
- unsigned char c = ((unsigned char *) s)[i];
- if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
- switch (expecting) {
- case S_VALUE:
- // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei);
- if (depth == ed) j = i;
- if (c == '{') {
- if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
- if (depth == ed && path[pos] == '.' && ci == ei) {
- // If we start the object, reset array indices
- ed++, pos++, ci = ei = -1;
- }
- nesting[depth++] = c;
- expecting = S_KEY;
- break;
- } else if (c == '[') {
- if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
- if (depth == ed && path[pos] == '[' && ei == ci) {
- ed++, pos++, ci = 0;
- for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) {
- ei *= 10;
- ei += path[pos] - '0';
- }
- if (path[pos] != 0) pos++;
- }
- nesting[depth++] = c;
- break;
- } else if (c == ']' && depth > 0) { // Empty array
- MG_EOO(']');
- } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
- i += 3;
- } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
- i += 3;
- } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
- i += 4;
- } else if (c == '-' || ((c >= '0' && c <= '9'))) {
- int numlen = 0;
- mg_atod(&s[i], len - i, &numlen);
- i += numlen - 1;
- } else if (c == '"') {
- int n = mg_pass_string(&s[i + 1], len - i - 1);
- if (n < 0) return n;
- i += n + 1;
- } else {
- return MG_JSON_INVALID;
- }
- MG_CHECKRET('V');
- if (depth == ed && ei >= 0) ci++;
- expecting = S_COMMA_OR_EOO;
- break;
+#ifndef MG_MAX_SSI_DEPTH
+#define MG_MAX_SSI_DEPTH 5
+#endif
- case S_KEY:
- if (c == '"') {
- int n = mg_pass_string(&s[i + 1], len - i - 1);
- if (n < 0) return n;
- if (i + 1 + n >= len) return MG_JSON_NOT_FOUND;
- if (depth < ed) return MG_JSON_NOT_FOUND;
- if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND;
- // printf("K %s [%.*s] [%.*s] %d %d %d\n", path, pos, path, n,
- // &s[i + 1], n, depth, ed);
- // NOTE(cpq): in the check sequence below is important.
- // strncmp() must go first: it fails fast if the remaining length of
- // the path is smaller than `n`.
- if (depth == ed && path[pos - 1] == '.' &&
- strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 &&
- (path[pos + n] == '\0' || path[pos + n] == '.' ||
- path[pos + n] == '[')) {
- pos += n;
- }
- i += n + 1;
- expecting = S_COLON;
- } else if (c == '}') { // Empty object
- MG_EOO('}');
- expecting = S_COMMA_OR_EOO;
- } else {
- return MG_JSON_INVALID;
- }
- break;
+#ifndef MG_SSI_BUFSIZ
+#define MG_SSI_BUFSIZ 1024
+#endif
- case S_COLON:
- if (c == ':') {
- expecting = S_VALUE;
+#if MG_ENABLE_SSI
+static char *mg_ssi(const char *path, const char *root, int depth) {
+ struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE};
+ FILE *fp = fopen(path, "rb");
+ if (fp != NULL) {
+ char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)];
+ int ch, intag = 0;
+ size_t len = 0;
+ buf[0] = arg[0] = '\0';
+ while ((ch = fgetc(fp)) != EOF) {
+ if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
+ buf[len++] = (char) (ch & 0xff);
+ buf[len] = '\0';
+ if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
+ char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10],
+ *p = (char *) path + strlen(path), *data;
+ while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
+ 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));
+ free(data);
+ } else {
+ MG_ERROR(("%s: file=%s error or too deep", path, arg));
+ }
+ } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
+ char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
+ mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
+ if (depth < MG_MAX_SSI_DEPTH &&
+ (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
+ mg_iobuf_add(&b, b.len, data, strlen(data));
+ free(data);
+ } else {
+ MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
+ }
} else {
- return MG_JSON_INVALID;
+ // Unknown SSI tag
+ MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
+ mg_iobuf_add(&b, b.len, buf, len);
}
- break;
-
- case S_COMMA_OR_EOO:
- if (depth <= 0) {
- return MG_JSON_INVALID;
- } else if (c == ',') {
- expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
- } else if (c == ']' || c == '}') {
- MG_EOO('O');
- if (depth == ed && ei >= 0) ci++;
- } else {
- return MG_JSON_INVALID;
+ intag = 0;
+ len = 0;
+ } else if (ch == '<') {
+ intag = 1;
+ if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
+ len = 0;
+ buf[len++] = (char) (ch & 0xff);
+ } else if (intag) {
+ if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
+ intag = 0;
+ } else if (len >= sizeof(buf) - 2) {
+ MG_ERROR(("%s: SSI tag is too large", path));
+ len = 0;
}
- break;
+ buf[len++] = (char) (ch & 0xff);
+ } else {
+ buf[len++] = (char) (ch & 0xff);
+ if (len >= sizeof(buf)) {
+ mg_iobuf_add(&b, b.len, buf, len);
+ len = 0;
+ }
+ }
}
+ if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
+ if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
+ fclose(fp);
}
- return MG_JSON_NOT_FOUND;
+ (void) depth;
+ (void) root;
+ return (char *) b.buf;
}
-bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
- int n, toklen, found = 0;
- if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
- (json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) {
- if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL);
- found = 1;
- }
- return found;
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+ const char *fullpath) {
+ const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
+ char *data = mg_ssi(fullpath, root, 0);
+ mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
+ free(data);
+}
+#else
+void mg_http_serve_ssi(struct mg_connection *c, const char *root,
+ const char *fullpath) {
+ mg_http_reply(c, 501, NULL, "SSI not enabled");
+ (void) root, (void) fullpath;
}
+#endif
-bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
- int found = 0, off = mg_json_get(json, path, NULL);
- if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) {
- if (v != NULL) *v = json.ptr[off] == 't';
- found = 1;
- }
- return found;
+#ifdef MG_ENABLE_LINES
+#line 1 "src/str.c"
+#endif
+
+
+struct mg_str mg_str_s(const char *s) {
+ struct mg_str str = {(char *) s, s == NULL ? 0 : strlen(s)};
+ return str;
}
-static bool json_unescape(const char *s, size_t len, char *to, size_t n) {
- size_t i, j;
- for (i = 0, j = 0; i < len && j < n; i++, j++) {
- if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
- // \uXXXX escape. We could process a simple one-byte chars
- // \u00xx from the ASCII range. More complex chars would require
- // dragging in a UTF8 library, which is too much for us
- if (s[i + 2] != '0' || s[i + 3] != '0') return false; // Give up
- ((unsigned char *) to)[j] = (unsigned char) mg_unhexn(s + i + 4, 2);
+struct mg_str mg_str_n(const char *s, size_t n) {
+ struct mg_str str = {(char *) s, n};
+ return str;
+}
- i += 5;
- } else if (s[i] == '\\' && i + 1 < len) {
- char c = json_esc(s[i + 1], 0);
- if (c == 0) return false;
- to[j] = c;
- i++;
- } else {
- to[j] = s[i];
- }
+static int mg_tolc(char c) {
+ return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
+}
+
+int mg_casecmp(const char *s1, const char *s2) {
+ int diff = 0;
+ do {
+ int c = mg_tolc(*s1++), d = mg_tolc(*s2++);
+ diff = c - d;
+ } while (diff == 0 && s1[-1] != '\0');
+ return diff;
+}
+
+int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
+ size_t i = 0;
+ while (i < str1.len && i < str2.len) {
+ int c1 = str1.buf[i];
+ int c2 = str2.buf[i];
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ i++;
}
- if (j >= n) return false;
- if (n > 0) to[j] = '\0';
- return true;
+ if (i < str1.len) return 1;
+ if (i < str2.len) return -1;
+ return 0;
}
-char *mg_json_get_str(struct mg_str json, const char *path) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && len > 1 && json.ptr[off] == '"') {
- if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
- !json_unescape(json.ptr + off + 1, (size_t) (len - 2), result,
- (size_t) len)) {
- free(result);
- result = NULL;
- }
+int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {
+ size_t i = 0;
+ while (i < str1.len && i < str2.len) {
+ int c1 = mg_tolc(str1.buf[i]);
+ int c2 = mg_tolc(str2.buf[i]);
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ i++;
}
- return result;
+ if (i < str1.len) return 1;
+ if (i < str2.len) return -1;
+ return 0;
}
-char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
- (result = (char *) calloc(1, (size_t) len)) != NULL) {
- int k = mg_base64_decode(json.ptr + off + 1, len - 2, result);
- if (slen != NULL) *slen = k;
+bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
+ size_t i = 0, j = 0, ni = 0, nj = 0;
+ if (caps) caps->buf = NULL, caps->len = 0;
+ while (i < p.len || j < s.len) {
+ if (i < p.len && j < s.len && (p.buf[i] == '?' || s.buf[j] == p.buf[i])) {
+ if (caps == NULL) {
+ } else if (p.buf[i] == '?') {
+ caps->buf = &s.buf[j], caps->len = 1; // Finalize `?` cap
+ caps++, caps->buf = NULL, caps->len = 0; // Init next cap
+ } else if (caps->buf != NULL && caps->len == 0) {
+ caps->len = (size_t) (&s.buf[j] - caps->buf); // Finalize current cap
+ caps++, caps->len = 0, caps->buf = NULL; // Init next cap
+ }
+ i++, j++;
+ } else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) {
+ if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap
+ ni = i++, nj = j + 1;
+ } else if (nj > 0 && nj <= s.len && (p.buf[ni] == '#' || s.buf[j] != '/')) {
+ i = ni, j = nj;
+ if (caps && caps->buf == NULL && caps->len == 0) {
+ caps--, caps->len = 0; // Restart previous cap
+ }
+ } else {
+ return false;
+ }
}
- return result;
+ if (caps && caps->buf && caps->len == 0) {
+ caps->len = (size_t) (&s.buf[j] - caps->buf);
+ }
+ return true;
}
-char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
- (result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
- mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result);
- result[len / 2 - 1] = '\0';
- if (slen != NULL) *slen = len / 2 - 1;
+bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
+ if (s.len == 0 || s.buf == NULL) {
+ return false; // Empty string, nothing to span - fail
+ } else {
+ size_t len = 0;
+ while (len < s.len && s.buf[len] != sep) len++; // Find separator
+ if (a) *a = mg_str_n(s.buf, len); // Init a
+ if (b) *b = mg_str_n(s.buf + len, s.len - len); // Init b
+ if (b && len < s.len) b->buf++, b->len--; // Skip separator
+ return true;
}
- return result;
}
-long mg_json_get_long(struct mg_str json, const char *path, long dflt) {
- double dv;
- long result = dflt;
- if (mg_json_get_num(json, path, &dv)) result = (long) dv;
- return result;
+bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len) {
+ size_t i = 0, ndigits = 0;
+ uint64_t max = val_len == sizeof(uint8_t) ? 0xFF
+ : val_len == sizeof(uint16_t) ? 0xFFFF
+ : val_len == sizeof(uint32_t) ? 0xFFFFFFFF
+ : (uint64_t) ~0;
+ uint64_t result = 0;
+ if (max == (uint64_t) ~0 && val_len != sizeof(uint64_t)) return false;
+ if (base == 0 && str.len >= 2) {
+ if (str.buf[i] == '0') {
+ i++;
+ base = str.buf[i] == 'b' ? 2 : str.buf[i] == 'x' ? 16 : 10;
+ if (base != 10) ++i;
+ } else {
+ base = 10;
+ }
+ }
+ switch (base) {
+ case 2:
+ while (i < str.len && (str.buf[i] == '0' || str.buf[i] == '1')) {
+ uint64_t digit = (uint64_t) (str.buf[i] - '0');
+ if (result > max/2) return false; // Overflow
+ result *= 2;
+ if (result > max - digit) return false; // Overflow
+ result += digit;
+ i++, ndigits++;
+ }
+ break;
+ case 10:
+ while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') {
+ uint64_t digit = (uint64_t) (str.buf[i] - '0');
+ if (result > max/10) return false; // Overflow
+ result *= 10;
+ if (result > max - digit) return false; // Overflow
+ result += digit;
+ i++, ndigits++;
+ }
+ break;
+ case 16:
+ while (i < str.len) {
+ char c = str.buf[i];
+ uint64_t digit = (c >= '0' && c <= '9') ? (uint64_t) (c - '0')
+ : (c >= 'A' && c <= 'F') ? (uint64_t) (c - '7')
+ : (c >= 'a' && c <= 'f') ? (uint64_t) (c - 'W')
+ : (uint64_t) ~0;
+ if (digit == (uint64_t) ~0) break;
+ if (result > max/16) return false; // Overflow
+ result *= 16;
+ if (result > max - digit) return false; // Overflow
+ result += digit;
+ i++, ndigits++;
+ }
+ break;
+ default:
+ return false;
+ }
+ if (ndigits == 0) return false;
+ if (i != str.len) return false;
+ if (val_len == 1) {
+ *((uint8_t *) val) = (uint8_t) result;
+ } else if (val_len == 2) {
+ *((uint16_t *) val) = (uint16_t) result;
+ } else if (val_len == 4) {
+ *((uint32_t *) val) = (uint32_t) result;
+ } else {
+ *((uint64_t *) val) = (uint64_t) result;
+ }
+ return true;
}
#ifdef MG_ENABLE_LINES
-#line 1 "src/log.c"
+#line 1 "src/timer.c"
#endif
+#define MG_TIMER_CALLED 4
-
-static void default_logger(char c, void *param) {
- putchar(c);
- (void) c, (void) param;
-}
-
-static int s_level = MG_LL_INFO;
-static mg_pfn_t s_log_func = default_logger;
-static void *s_log_func_param = NULL;
-
-void mg_log_set_fn(mg_pfn_t fn, void *param) {
- s_log_func = fn;
- s_log_func_param = param;
-}
-
-static void logc(unsigned char c) {
- s_log_func((char) c, s_log_func_param);
-}
-
-static void logs(const char *buf, size_t len) {
- size_t i;
- for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]);
-}
-
-void mg_log_set(int log_level) {
- MG_DEBUG(("Setting log level to %d", log_level));
- s_level = log_level;
-}
-
-bool mg_log_prefix(int level, const char *file, int line, const char *fname) {
- if (level <= s_level) {
- const char *p = strrchr(file, '/');
- char buf[41];
- size_t n;
- if (p == NULL) p = strrchr(file, '\\');
- n = mg_snprintf(buf, sizeof(buf), "%llx %d %s:%d:%s", mg_millis(), level,
- p == NULL ? file : p + 1, line, fname);
- if (n > sizeof(buf) - 2) n = sizeof(buf) - 2;
- while (n < sizeof(buf)) buf[n++] = ' ';
- logs(buf, n - 1);
- return true;
- } else {
- return false;
- }
+void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms,
+ unsigned flags, void (*fn)(void *), void *arg) {
+ t->id = 0, t->period_ms = ms, t->expire = 0;
+ t->flags = flags, t->fn = fn, t->arg = arg, t->next = *head;
+ *head = t;
}
-void mg_log(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap);
- va_end(ap);
- logc((unsigned char) '\n');
+void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
+ while (*head && *head != t) head = &(*head)->next;
+ if (*head) *head = t->next;
}
-static unsigned char nibble(unsigned c) {
- return (unsigned char) (c < 10 ? c + '0' : c + 'W');
+// t: expiration time, prd: period, now: current time. Return true if expired
+bool mg_timer_expired(uint64_t *t, uint64_t prd, uint64_t now) {
+ if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
+ if (*t == 0) *t = now + prd; // Firt poll? Set expiration
+ if (*t > now) return false; // Not expired yet, return
+ *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
+ return true; // Expired, return true
}
-#define ISPRINT(x) ((x) >= ' ' && (x) <= '~')
-void mg_hexdump(const void *buf, size_t len) {
- const unsigned char *p = (const unsigned char *) buf;
- unsigned char ascii[16], alen = 0;
- size_t i;
- for (i = 0; i < len; i++) {
- if ((i % 16) == 0) {
- // Print buffered ascii chars
- if (i > 0) logs(" ", 2), logs((char *) ascii, 16), logc('\n'), alen = 0;
- // Print hex address, then \t
- logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)),
- logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3);
- }
- logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5
- logc(' '); // Space after hex number
- ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf
+void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
+ struct mg_timer *t, *tmp;
+ for (t = *head; t != NULL; t = tmp) {
+ bool once = t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) &&
+ !(t->flags & MG_TIMER_CALLED); // Handle MG_TIMER_NOW only once
+ bool expired = mg_timer_expired(&t->expire, t->period_ms, now_ms);
+ tmp = t->next;
+ if (!once && !expired) continue;
+ if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) {
+ t->fn(t->arg);
+ }
+ t->flags |= MG_TIMER_CALLED;
}
- while (alen < 16) logs(" ", 3), ascii[alen++] = ' ';
- logs(" ", 2), logs((char *) ascii, 16), logc('\n');
}
#ifdef MG_ENABLE_LINES
-#line 1 "src/md5.c"
+#line 1 "src/tls_aes128.c"
#endif
+/******************************************************************************
+ *
+ * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
+ *
+ * This is a simple and straightforward implementation of the AES Rijndael
+ * 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
+ * of this work was correctness & accuracy. It is written in 'C' without any
+ * particular focus upon optimization or speed. It should be endian (memory
+ * byte order) neutral since the few places that care are handled explicitly.
+ *
+ * This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
+ *
+ * It is intended for general purpose use, but was written in support of GRC's
+ * reference implementation of the SQRL (Secure Quick Reliable Login) client.
+ *
+ * See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
+ *
+ * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
+ * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
+ *
+ *******************************************************************************/
+
+/******************************************************************************/
+#define AES_DECRYPTION 1 // whether AES decryption is supported
+/******************************************************************************/
+
+#define MG_ENCRYPT 1 // specify whether we're encrypting
+#define MG_DECRYPT 0 // or decrypting
+
+
+
+
+
+#if MG_TLS == MG_TLS_BUILTIN
+/******************************************************************************
+ * AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
+ ******************************************************************************/
+static void aes_init_keygen_tables(void);
+
+/******************************************************************************
+ * AES_SETKEY : called to expand the key for encryption or decryption
+ ******************************************************************************/
+static int aes_setkey(aes_context *ctx, // pointer to context
+ int mode, // 1 or 0 for Encrypt/Decrypt
+ const uchar *key, // AES input key
+ uint keysize); // size in bytes (must be 16, 24, 32 for
+ // 128, 192 or 256-bit keys respectively)
+ // returns 0 for success
+
+/******************************************************************************
+ * AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
+ ******************************************************************************/
+static int aes_cipher(aes_context *ctx, // pointer to context
+ const uchar input[16], // 128-bit block to en/decipher
+ uchar output[16]); // 128-bit output result block
+ // returns 0 for success
+
+/******************************************************************************
+ * GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
+ ******************************************************************************/
+typedef struct {
+ int mode; // cipher direction: encrypt/decrypt
+ uint64_t len; // cipher data length processed so far
+ uint64_t add_len; // total add data length
+ uint64_t HL[16]; // precalculated lo-half HTable
+ uint64_t HH[16]; // precalculated hi-half HTable
+ uchar base_ectr[16]; // first counter-mode cipher output for tag
+ uchar y[16]; // the current cipher-input IV|Counter value
+ uchar buf[16]; // buf working value
+ aes_context aes_ctx; // cipher context used
+} gcm_context;
+
+/******************************************************************************
+ * GCM_SETKEY : sets the GCM (and AES) keying material for use
+ ******************************************************************************/
+static int gcm_setkey(
+ gcm_context *ctx, // caller-provided context ptr
+ const uchar *key, // pointer to cipher key
+ const uint keysize // size in bytes (must be 16, 24, 32 for
+ // 128, 192 or 256-bit keys respectively)
+); // returns 0 for success
+
+/******************************************************************************
+ *
+ * GCM_CRYPT_AND_TAG
+ *
+ * This either encrypts or decrypts the user-provided data and, either
+ * way, generates an authentication tag of the requested length. It must be
+ * called with a GCM context whose key has already been set with GCM_SETKEY.
+ *
+ * The user would typically call this explicitly to ENCRYPT a buffer of data
+ * and optional associated data, and produce its an authentication tag.
+ *
+ * To reverse the process the user would typically call the companion
+ * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
+ * authentication tag. The GCM_AUTH_DECRYPT function calls this function
+ * to perform its decryption and tag generation, which it then compares.
+ *
+ ******************************************************************************/
+static int gcm_crypt_and_tag(
+ gcm_context *ctx, // gcm context with key already setup
+ int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
+ const uchar *iv, // pointer to the 12-byte initialization vector
+ size_t iv_len, // byte length if the IV. should always be 12
+ const uchar *add, // pointer to the non-ciphered additional data
+ size_t add_len, // byte length of the additional AEAD data
+ const uchar *input, // pointer to the cipher data source
+ uchar *output, // pointer to the cipher data destination
+ size_t length, // byte length of the cipher data
+ uchar *tag, // pointer to the tag to be generated
+ size_t tag_len); // byte length of the tag to be generated
+
+/******************************************************************************
+ *
+ * GCM_START
+ *
+ * Given a user-provided GCM context, this initializes it, sets the encryption
+ * mode, and preprocesses the initialization vector and additional AEAD data.
+ *
+ ******************************************************************************/
+static int gcm_start(
+ gcm_context *ctx, // pointer to user-provided GCM context
+ int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
+ const uchar *iv, // pointer to initialization vector
+ size_t iv_len, // IV length in bytes (should == 12)
+ const uchar *add, // pointer to additional AEAD data (NULL if none)
+ size_t add_len); // length of additional AEAD data (bytes)
+
+/******************************************************************************
+ *
+ * GCM_UPDATE
+ *
+ * This is called once or more to process bulk plaintext or ciphertext data.
+ * We give this some number of bytes of input and it returns the same number
+ * of output bytes. If called multiple times (which is fine) all but the final
+ * invocation MUST be called with length mod 16 == 0. (Only the final call can
+ * have a partial block length of < 128 bits.)
+ *
+ ******************************************************************************/
+static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
+ size_t length, // length, in bytes, of data to process
+ const uchar *input, // pointer to source data
+ uchar *output); // pointer to destination data
+
+/******************************************************************************
+ *
+ * GCM_FINISH
+ *
+ * This is called once after all calls to GCM_UPDATE to finalize the GCM.
+ * It performs the final GHASH to produce the resulting authentication TAG.
+ *
+ ******************************************************************************/
+static int gcm_finish(
+ gcm_context *ctx, // pointer to user-provided GCM context
+ uchar *tag, // ptr to tag buffer - NULL if tag_len = 0
+ size_t tag_len); // length, in bytes, of the tag-receiving buf
+
+/******************************************************************************
+ *
+ * GCM_ZERO_CTX
+ *
+ * The GCM context contains both the GCM context and the AES context.
+ * This includes keying and key-related material which is security-
+ * sensitive, so it MUST be zeroed after use. This function does that.
+ *
+ ******************************************************************************/
+static void gcm_zero_ctx(gcm_context *ctx);
+
+/******************************************************************************
+ *
+ * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
+ *
+ * This is a simple and straightforward implementation of the AES Rijndael
+ * 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
+ * of this work was correctness & accuracy. It is written in 'C' without any
+ * particular focus upon optimization or speed. It should be endian (memory
+ * byte order) neutral since the few places that care are handled explicitly.
+ *
+ * This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
+ *
+ * It is intended for general purpose use, but was written in support of GRC's
+ * reference implementation of the SQRL (Secure Quick Reliable Login) client.
+ *
+ * See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
+ *
+ * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
+ * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
+ *
+ *******************************************************************************/
+
+
+
+
+static int aes_tables_inited = 0; // run-once flag for performing key
+ // expasion table generation (see below)
+/*
+ * The following static local tables must be filled-in before the first use of
+ * the GCM or AES ciphers. They are used for the AES key expansion/scheduling
+ * and once built are read-only and thread safe. The "gcm_initialize" function
+ * must be called once during system initialization to populate these arrays
+ * for subsequent use by the AES key scheduler. If they have not been built
+ * before attempted use, an error will be returned to the caller.
+ *
+ * NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
+ * GCM uses AES in counter-mode, where the AES cipher output is XORed with
+ * the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
+ * decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
+ */
+// We always need our forward tables
+static uchar FSb[256]; // Forward substitution box (FSb)
+static uint32_t FT0[256]; // Forward key schedule assembly tables
+static uint32_t FT1[256];
+static uint32_t FT2[256];
+static uint32_t FT3[256];
+
+#if AES_DECRYPTION // We ONLY need reverse for decryption
+static uchar RSb[256]; // Reverse substitution box (RSb)
+static uint32_t RT0[256]; // Reverse key schedule assembly tables
+static uint32_t RT1[256];
+static uint32_t RT2[256];
+static uint32_t RT3[256];
+#endif /* AES_DECRYPTION */
+
+static uint32_t RCON[10]; // AES round constants
+/*
+ * Platform Endianness Neutralizing Load and Store Macro definitions
+ * AES wants platform-neutral Little Endian (LE) byte ordering
+ */
+#define GET_UINT32_LE(n, b, i) \
+ { \
+ (n) = ((uint32_t) (b)[(i)]) | ((uint32_t) (b)[(i) + 1] << 8) | \
+ ((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \
+ }
-
-#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5
-
-static void mg_byte_reverse(unsigned char *buf, unsigned longs) {
- if (MG_BIG_ENDIAN) {
- do {
- uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
- ((unsigned) buf[1] << 8 | buf[0]);
- *(uint32_t *) buf = t;
- buf += 4;
- } while (--longs);
- } else {
- (void) buf, (void) longs; // Little endian. Do nothing
+#define PUT_UINT32_LE(n, b, i) \
+ { \
+ (b)[(i)] = (uchar) ((n)); \
+ (b)[(i) + 1] = (uchar) ((n) >> 8); \
+ (b)[(i) + 2] = (uchar) ((n) >> 16); \
+ (b)[(i) + 3] = (uchar) ((n) >> 24); \
}
-}
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
+/*
+ * AES forward and reverse encryption round processing macros
+ */
+#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
+ { \
+ X0 = *RK++ ^ FT0[(Y0) & 0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ \
+ FT2[(Y2 >> 16) & 0xFF] ^ FT3[(Y3 >> 24) & 0xFF]; \
+ \
+ X1 = *RK++ ^ FT0[(Y1) & 0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ \
+ FT2[(Y3 >> 16) & 0xFF] ^ FT3[(Y0 >> 24) & 0xFF]; \
+ \
+ X2 = *RK++ ^ FT0[(Y2) & 0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ \
+ FT2[(Y0 >> 16) & 0xFF] ^ FT3[(Y1 >> 24) & 0xFF]; \
+ \
+ X3 = *RK++ ^ FT0[(Y3) & 0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ \
+ FT2[(Y1 >> 16) & 0xFF] ^ FT3[(Y2 >> 24) & 0xFF]; \
+ }
-#define MD5STEP(f, w, x, y, z, data, s) \
- (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
+ { \
+ X0 = *RK++ ^ RT0[(Y0) & 0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ \
+ RT2[(Y2 >> 16) & 0xFF] ^ RT3[(Y1 >> 24) & 0xFF]; \
+ \
+ X1 = *RK++ ^ RT0[(Y1) & 0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ \
+ RT2[(Y3 >> 16) & 0xFF] ^ RT3[(Y2 >> 24) & 0xFF]; \
+ \
+ X2 = *RK++ ^ RT0[(Y2) & 0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ \
+ RT2[(Y0 >> 16) & 0xFF] ^ RT3[(Y3 >> 24) & 0xFF]; \
+ \
+ X3 = *RK++ ^ RT0[(Y3) & 0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ \
+ RT2[(Y1 >> 16) & 0xFF] ^ RT3[(Y0 >> 24) & 0xFF]; \
+ }
/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
+ * These macros improve the readability of the key
+ * generation initialization code by collapsing
+ * repetitive common operations into logical pieces.
*/
-void mg_md5_init(mg_md5_ctx *ctx) {
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
+#define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24)
+#define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00))
+#define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0)
+#define MIX(x, y) \
+ { \
+ y = ((y << 1) | (y >> 7)) & 0xFF; \
+ x ^= y; \
+ }
+#define CPY128 \
+ { \
+ *RK++ = *SK++; \
+ *RK++ = *SK++; \
+ *RK++ = *SK++; \
+ *RK++ = *SK++; \
+ }
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
-}
+/******************************************************************************
+ *
+ * AES_INIT_KEYGEN_TABLES
+ *
+ * Fills the AES key expansion tables allocated above with their static
+ * data. This is not "per key" data, but static system-wide read-only
+ * table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
+ * at system initialization to setup the tables for all subsequent use.
+ *
+ ******************************************************************************/
+void aes_init_keygen_tables(void) {
+ int i, x, y, z; // general purpose iteration and computation locals
+ int pow[256];
+ int log[256];
+
+ if (aes_tables_inited) return;
+
+ // fill the 'pow' and 'log' tables over GF(2^8)
+ for (i = 0, x = 1; i < 256; i++) {
+ pow[i] = x;
+ log[x] = i;
+ x = (x ^ XTIME(x)) & 0xFF;
+ }
+ // compute the round constants
+ for (i = 0, x = 1; i < 10; i++) {
+ RCON[i] = (uint32_t) x;
+ x = XTIME(x) & 0xFF;
+ }
+ // fill the forward and reverse substitution boxes
+ FSb[0x00] = 0x63;
+#if AES_DECRYPTION // whether AES decryption is supported
+ RSb[0x63] = 0x00;
+#endif /* AES_DECRYPTION */
+
+ for (i = 1; i < 256; i++) {
+ x = y = pow[255 - log[i]];
+ MIX(x, y);
+ MIX(x, y);
+ MIX(x, y);
+ MIX(x, y);
+ FSb[i] = (uchar) (x ^= 0x63);
+#if AES_DECRYPTION // whether AES decryption is supported
+ RSb[x] = (uchar) i;
+#endif /* AES_DECRYPTION */
+ }
+ // generate the forward and reverse key expansion tables
+ for (i = 0; i < 256; i++) {
+ x = FSb[i];
+ y = XTIME(x) & 0xFF;
+ z = (y ^ x) & 0xFF;
-static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
- uint32_t a, b, c, d;
+ FT0[i] = ((uint32_t) y) ^ ((uint32_t) x << 8) ^ ((uint32_t) x << 16) ^
+ ((uint32_t) z << 24);
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
+ FT1[i] = ROTL8(FT0[i]);
+ FT2[i] = ROTL8(FT1[i]);
+ FT3[i] = ROTL8(FT2[i]);
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+#if AES_DECRYPTION // whether AES decryption is supported
+ x = RSb[i];
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+ RT0[i] = ((uint32_t) MUL(0x0E, x)) ^ ((uint32_t) MUL(0x09, x) << 8) ^
+ ((uint32_t) MUL(0x0D, x) << 16) ^ ((uint32_t) MUL(0x0B, x) << 24);
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+ RT1[i] = ROTL8(RT0[i]);
+ RT2[i] = ROTL8(RT1[i]);
+ RT3[i] = ROTL8(RT2[i]);
+#endif /* AES_DECRYPTION */
+ }
+ aes_tables_inited = 1; // flag that the tables have been generated
+} // to permit subsequent use of the AES cipher
+
+/******************************************************************************
+ *
+ * AES_SET_ENCRYPTION_KEY
+ *
+ * This is called by 'aes_setkey' when we're establishing a key for
+ * subsequent encryption. We give it a pointer to the encryption
+ * context, a pointer to the key, and the key's length in bytes.
+ * Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
+ *
+ ******************************************************************************/
+static int aes_set_encryption_key(aes_context *ctx, const uchar *key,
+ uint keysize) {
+ uint i; // general purpose iteration local
+ uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
+
+ for (i = 0; i < (keysize >> 2); i++) {
+ GET_UINT32_LE(RK[i], key, i << 2);
+ }
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+ switch (ctx->rounds) {
+ case 10:
+ for (i = 0; i < 10; i++, RK += 4) {
+ RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[3] >> 8) & 0xFF]) ^
+ ((uint32_t) FSb[(RK[3] >> 16) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(RK[3] >> 24) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(RK[3]) & 0xFF] << 24);
+
+ RK[5] = RK[1] ^ RK[4];
+ RK[6] = RK[2] ^ RK[5];
+ RK[7] = RK[3] ^ RK[6];
+ }
+ break;
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
+ case 12:
+ for (i = 0; i < 8; i++, RK += 6) {
+ RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[5] >> 8) & 0xFF]) ^
+ ((uint32_t) FSb[(RK[5] >> 16) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(RK[5] >> 24) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(RK[5]) & 0xFF] << 24);
+
+ RK[7] = RK[1] ^ RK[6];
+ RK[8] = RK[2] ^ RK[7];
+ RK[9] = RK[3] ^ RK[8];
+ RK[10] = RK[4] ^ RK[9];
+ RK[11] = RK[5] ^ RK[10];
+ }
+ break;
-void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) {
- uint32_t t;
+ case 14:
+ for (i = 0; i < 7; i++, RK += 8) {
+ RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[7] >> 8) & 0xFF]) ^
+ ((uint32_t) FSb[(RK[7] >> 16) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(RK[7] >> 24) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(RK[7]) & 0xFF] << 24);
+
+ RK[9] = RK[1] ^ RK[8];
+ RK[10] = RK[2] ^ RK[9];
+ RK[11] = RK[3] ^ RK[10];
+
+ RK[12] = RK[4] ^ ((uint32_t) FSb[(RK[11]) & 0xFF]) ^
+ ((uint32_t) FSb[(RK[11] >> 8) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(RK[11] >> 16) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(RK[11] >> 24) & 0xFF] << 24);
+
+ RK[13] = RK[5] ^ RK[12];
+ RK[14] = RK[6] ^ RK[13];
+ RK[15] = RK[7] ^ RK[14];
+ }
+ break;
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
- ctx->bits[1] += (uint32_t) len >> 29;
+ default:
+ return -1;
+ }
+ return (0);
+}
+
+#if AES_DECRYPTION // whether AES decryption is supported
+
+/******************************************************************************
+ *
+ * AES_SET_DECRYPTION_KEY
+ *
+ * This is called by 'aes_setkey' when we're establishing a
+ * key for subsequent decryption. We give it a pointer to
+ * the encryption context, a pointer to the key, and the key's
+ * length in bits. Valid lengths are: 128, 192, or 256 bits.
+ *
+ ******************************************************************************/
+static int aes_set_decryption_key(aes_context *ctx, const uchar *key,
+ uint keysize) {
+ int i, j;
+ aes_context cty; // a calling aes context for set_encryption_key
+ uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
+ uint32_t *SK;
+ int ret;
- t = (t >> 3) & 0x3f;
+ cty.rounds = ctx->rounds; // initialize our local aes context
+ cty.rk = cty.buf; // round count and key buf pointer
- if (t) {
- unsigned char *p = (unsigned char *) ctx->in + t;
+ if ((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) return (ret);
- t = 64 - t;
- if (len < t) {
- memcpy(p, buf, len);
- return;
+ SK = cty.rk + cty.rounds * 4;
+
+ CPY128 // copy a 128-bit block from *SK to *RK
+
+ for (i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
+ for (j = 0; j < 4; j++, SK++) {
+ *RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^
+ RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]];
}
- memcpy(p, buf, t);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += t;
- len -= t;
+ }
+ CPY128 // copy a 128-bit block from *SK to *RK
+ memset(&cty, 0, sizeof(aes_context)); // clear local aes context
+ return (0);
+}
+
+#endif /* AES_DECRYPTION */
+
+/******************************************************************************
+ *
+ * AES_SETKEY
+ *
+ * Invoked to establish the key schedule for subsequent encryption/decryption
+ *
+ ******************************************************************************/
+static int aes_setkey(aes_context *ctx, // AES context provided by our caller
+ int mode, // ENCRYPT or DECRYPT flag
+ const uchar *key, // pointer to the key
+ uint keysize) // key length in bytes
+{
+ // since table initialization is not thread safe, we could either add
+ // system-specific mutexes and init the AES key generation tables on
+ // demand, or ask the developer to simply call "gcm_initialize" once during
+ // application startup before threading begins. That's what we choose.
+ if (!aes_tables_inited) return (-1); // fail the call when not inited.
+
+ ctx->mode = mode; // capture the key type we're creating
+ ctx->rk = ctx->buf; // initialize our round key pointer
+
+ switch (keysize) // set the rounds count based upon the keysize
+ {
+ case 16:
+ ctx->rounds = 10;
+ break; // 16-byte, 128-bit key
+ case 24:
+ ctx->rounds = 12;
+ break; // 24-byte, 192-bit key
+ case 32:
+ ctx->rounds = 14;
+ break; // 32-byte, 256-bit key
+ default:
+ return (-1);
}
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
+#if AES_DECRYPTION
+ if (mode == MG_DECRYPT) // expand our key for encryption or decryption
+ return (aes_set_decryption_key(ctx, key, keysize));
+ else /* MG_ENCRYPT */
+#endif /* AES_DECRYPTION */
+ return (aes_set_encryption_key(ctx, key, keysize));
+}
+
+/******************************************************************************
+ *
+ * AES_CIPHER
+ *
+ * Perform AES encryption and decryption.
+ * The AES context will have been setup with the encryption mode
+ * and all keying information appropriate for the task.
+ *
+ ******************************************************************************/
+static int aes_cipher(aes_context *ctx, const uchar input[16],
+ uchar output[16]) {
+ int i;
+ uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
+
+ RK = ctx->rk;
+
+ GET_UINT32_LE(X0, input, 0);
+ X0 ^= *RK++; // load our 128-bit
+ GET_UINT32_LE(X1, input, 4);
+ X1 ^= *RK++; // input buffer in a storage
+ GET_UINT32_LE(X2, input, 8);
+ X2 ^= *RK++; // memory endian-neutral way
+ GET_UINT32_LE(X3, input, 12);
+ X3 ^= *RK++;
+
+#if AES_DECRYPTION // whether AES decryption is supported
+
+ if (ctx->mode == MG_DECRYPT) {
+ for (i = (ctx->rounds >> 1) - 1; i > 0; i--) {
+ AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
+ AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
+ }
+
+ AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
+
+ X0 = *RK++ ^ ((uint32_t) RSb[(Y0) & 0xFF]) ^
+ ((uint32_t) RSb[(Y3 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) RSb[(Y2 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) RSb[(Y1 >> 24) & 0xFF] << 24);
+
+ X1 = *RK++ ^ ((uint32_t) RSb[(Y1) & 0xFF]) ^
+ ((uint32_t) RSb[(Y0 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) RSb[(Y3 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) RSb[(Y2 >> 24) & 0xFF] << 24);
+
+ X2 = *RK++ ^ ((uint32_t) RSb[(Y2) & 0xFF]) ^
+ ((uint32_t) RSb[(Y1 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) RSb[(Y0 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) RSb[(Y3 >> 24) & 0xFF] << 24);
+
+ X3 = *RK++ ^ ((uint32_t) RSb[(Y3) & 0xFF]) ^
+ ((uint32_t) RSb[(Y2 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) RSb[(Y1 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) RSb[(Y0 >> 24) & 0xFF] << 24);
+ } else /* MG_ENCRYPT */
+ {
+#endif /* AES_DECRYPTION */
+
+ for (i = (ctx->rounds >> 1) - 1; i > 0; i--) {
+ AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
+ AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
+ }
+
+ AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
+
+ X0 = *RK++ ^ ((uint32_t) FSb[(Y0) & 0xFF]) ^
+ ((uint32_t) FSb[(Y1 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(Y2 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(Y3 >> 24) & 0xFF] << 24);
+
+ X1 = *RK++ ^ ((uint32_t) FSb[(Y1) & 0xFF]) ^
+ ((uint32_t) FSb[(Y2 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(Y3 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(Y0 >> 24) & 0xFF] << 24);
+
+ X2 = *RK++ ^ ((uint32_t) FSb[(Y2) & 0xFF]) ^
+ ((uint32_t) FSb[(Y3 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(Y0 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(Y1 >> 24) & 0xFF] << 24);
+
+ X3 = *RK++ ^ ((uint32_t) FSb[(Y3) & 0xFF]) ^
+ ((uint32_t) FSb[(Y0 >> 8) & 0xFF] << 8) ^
+ ((uint32_t) FSb[(Y1 >> 16) & 0xFF] << 16) ^
+ ((uint32_t) FSb[(Y2 >> 24) & 0xFF] << 24);
+
+#if AES_DECRYPTION // whether AES decryption is supported
}
+#endif /* AES_DECRYPTION */
+
+ PUT_UINT32_LE(X0, output, 0);
+ PUT_UINT32_LE(X1, output, 4);
+ PUT_UINT32_LE(X2, output, 8);
+ PUT_UINT32_LE(X3, output, 12);
+
+ return (0);
+}
+/* end of aes.c */
+/******************************************************************************
+ *
+ * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
+ *
+ * This is a simple and straightforward implementation of AES-GCM authenticated
+ * encryption. The focus of this work was correctness & accuracy. It is written
+ * in straight 'C' without any particular focus upon optimization or speed. It
+ * should be endian (memory byte order) neutral since the few places that care
+ * are handled explicitly.
+ *
+ * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
+ *
+ * It is intended for general purpose use, but was written in support of GRC's
+ * reference implementation of the SQRL (Secure Quick Reliable Login) client.
+ *
+ * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
+ * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
+ * gcm/gcm-revised-spec.pdf
+ *
+ * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
+ * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
+ *
+ *******************************************************************************/
+
+/******************************************************************************
+ * ==== IMPLEMENTATION WARNING ====
+ *
+ * This code was developed for use within SQRL's fixed environmnent. Thus, it
+ * is somewhat less "general purpose" than it would be if it were designed as
+ * a general purpose AES-GCM library. Specifically, it bothers with almost NO
+ * error checking on parameter limits, buffer bounds, etc. It assumes that it
+ * is being invoked by its author or by someone who understands the values it
+ * expects to receive. Its behavior will be undefined otherwise.
+ *
+ * All functions that might fail are defined to return 'ints' to indicate a
+ * problem. Most do not do so now. But this allows for error propagation out
+ * of internal functions if robust error checking should ever be desired.
+ *
+ ******************************************************************************/
+
+/* Calculating the "GHASH"
+ *
+ * There are many ways of calculating the so-called GHASH in software, each with
+ * a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
+ * an intriguing construction which takes two 128-bit strings (also the cipher's
+ * block size and the fundamental operation size for the system) and hashes them
+ * into a third 128-bit result.
+ *
+ * Many implementation solutions have been worked out that use large precomputed
+ * table lookups in place of more time consuming bit fiddling, and this approach
+ * can be scaled easily upward or downward as needed to change the time/space
+ * tradeoff. It's been studied extensively and there's a solid body of theory
+ * and practice. For example, without using any lookup tables an implementation
+ * might obtain 119 cycles per byte throughput, whereas using a simple, though
+ * large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
+ * cycles per byte.
+ *
+ * And Intel's processors have, since 2010, included an instruction which does
+ * the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
+ *
+ * Since SQRL is interactive, and only processing a few 128-bit blocks, I've
+ * settled upon a relatively slower but appealing small-table compromise which
+ * folds a bunch of not only time consuming but also bit twiddling into a simple
+ * 16-entry table which is attributed to Victor Shoup's 1996 work while at
+ * Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
+ * Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
+ * See, also section 4.1 of the "gcm-revised-spec" cited above.
+ */
- memcpy(ctx->in, buf, len);
-}
+/*
+ * This 16-entry table of pre-computed constants is used by the
+ * GHASH multiplier to improve over a strictly table-free but
+ * significantly slower 128x128 bit multiple within GF(2^128).
+ */
+static const uint64_t last4[16] = {
+ 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
+ 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0};
-void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
- unsigned count;
- unsigned char *p;
- uint32_t *a;
+/*
+ * Platform Endianness Neutralizing Load and Store Macro definitions
+ * GCM wants platform-neutral Big Endian (BE) byte ordering
+ */
+#define GET_UINT32_BE(n, b, i) \
+ { \
+ (n) = ((uint32_t) (b)[(i)] << 24) | ((uint32_t) (b)[(i) + 1] << 16) | \
+ ((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \
+ }
- count = (ctx->bits[0] >> 3) & 0x3F;
+#define PUT_UINT32_BE(n, b, i) \
+ { \
+ (b)[(i)] = (uchar) ((n) >> 24); \
+ (b)[(i) + 1] = (uchar) ((n) >> 16); \
+ (b)[(i) + 2] = (uchar) ((n) >> 8); \
+ (b)[(i) + 3] = (uchar) ((n)); \
+ }
- p = ctx->in + count;
- *p++ = 0x80;
- count = 64 - 1 - count;
- if (count < 8) {
- memset(p, 0, count);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- memset(ctx->in, 0, 56);
- } else {
- memset(p, 0, count - 8);
+/******************************************************************************
+ *
+ * GCM_INITIALIZE
+ *
+ * Must be called once to initialize the GCM library.
+ *
+ * At present, this only calls the AES keygen table generator, which expands
+ * the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
+ * MUST be called during system initialization before a multi-threading
+ * environment is running.
+ *
+ ******************************************************************************/
+int mg_gcm_initialize(void) {
+ aes_init_keygen_tables();
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM_MULT
+ *
+ * Performs a GHASH operation on the 128-bit input vector 'x', setting
+ * the 128-bit output vector to 'x' times H using our precomputed tables.
+ * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
+ *
+ ******************************************************************************/
+static void gcm_mult(gcm_context *ctx, // pointer to established context
+ const uchar x[16], // pointer to 128-bit input vector
+ uchar output[16]) // pointer to 128-bit output vector
+{
+ int i;
+ uchar lo, hi, rem;
+ uint64_t zh, zl;
+
+ lo = (uchar) (x[15] & 0x0f);
+ hi = (uchar) (x[15] >> 4);
+ zh = ctx->HH[lo];
+ zl = ctx->HL[lo];
+
+ for (i = 15; i >= 0; i--) {
+ lo = (uchar) (x[i] & 0x0f);
+ hi = (uchar) (x[i] >> 4);
+
+ if (i != 15) {
+ rem = (uchar) (zl & 0x0f);
+ zl = (zh << 60) | (zl >> 4);
+ zh = (zh >> 4);
+ zh ^= (uint64_t) last4[rem] << 48;
+ zh ^= ctx->HH[lo];
+ zl ^= ctx->HL[lo];
+ }
+ rem = (uchar) (zl & 0x0f);
+ zl = (zh << 60) | (zl >> 4);
+ zh = (zh >> 4);
+ zh ^= (uint64_t) last4[rem] << 48;
+ zh ^= ctx->HH[hi];
+ zl ^= ctx->HL[hi];
}
- mg_byte_reverse(ctx->in, 14);
+ PUT_UINT32_BE(zh >> 32, output, 0);
+ PUT_UINT32_BE(zh, output, 4);
+ PUT_UINT32_BE(zl >> 32, output, 8);
+ PUT_UINT32_BE(zl, output, 12);
+}
+
+/******************************************************************************
+ *
+ * GCM_SETKEY
+ *
+ * This is called to set the AES-GCM key. It initializes the AES key
+ * and populates the gcm context's pre-calculated HTables.
+ *
+ ******************************************************************************/
+static int gcm_setkey(
+ gcm_context *ctx, // pointer to caller-provided gcm context
+ const uchar *key, // pointer to the AES encryption key
+ const uint keysize) // size in bytes (must be 16, 24, 32 for
+ // 128, 192 or 256-bit keys respectively)
+{
+ int ret, i, j;
+ uint64_t hi, lo;
+ uint64_t vl, vh;
+ unsigned char h[16];
+
+ memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
+ memset(h, 0, 16); // initialize the block to encrypt
+
+ // encrypt the null 128-bit block to generate a key-based value
+ // which is then used to initialize our GHASH lookup tables
+ if ((ret = aes_setkey(&ctx->aes_ctx, MG_ENCRYPT, key, keysize)) != 0)
+ return (ret);
+ if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) return (ret);
+
+ GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
+ GET_UINT32_BE(lo, h, 4);
+ vh = (uint64_t) hi << 32 | lo;
+
+ GET_UINT32_BE(hi, h, 8);
+ GET_UINT32_BE(lo, h, 12);
+ vl = (uint64_t) hi << 32 | lo;
+
+ ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
+ ctx->HH[8] = vh;
+ ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
+ ctx->HL[0] = 0;
+
+ for (i = 4; i > 0; i >>= 1) {
+ uint32_t T = (uint32_t) (vl & 1) * 0xe1000000U;
+ vl = (vh << 63) | (vl >> 1);
+ vh = (vh >> 1) ^ ((uint64_t) T << 32);
+ ctx->HL[i] = vl;
+ ctx->HH[i] = vh;
+ }
+ for (i = 2; i < 16; i <<= 1) {
+ uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
+ vh = *HiH;
+ vl = *HiL;
+ for (j = 1; j < i; j++) {
+ HiH[j] = vh ^ ctx->HH[j];
+ HiL[j] = vl ^ ctx->HL[j];
+ }
+ }
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
+ *
+ * SETKEY:
+ *
+ * START: Sets the Encryption/Decryption mode.
+ * Accepts the initialization vector and additional data.
+ *
+ * UPDATE: Encrypts or decrypts the plaintext or ciphertext.
+ *
+ * FINISH: Performs a final GHASH to generate the authentication tag.
+ *
+ ******************************************************************************
+ *
+ * GCM_START
+ *
+ * Given a user-provided GCM context, this initializes it, sets the encryption
+ * mode, and preprocesses the initialization vector and additional AEAD data.
+ *
+ ******************************************************************************/
+int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
+ int mode, // GCM_ENCRYPT or GCM_DECRYPT
+ const uchar *iv, // pointer to initialization vector
+ size_t iv_len, // IV length in bytes (should == 12)
+ const uchar *add, // ptr to additional AEAD data (NULL if none)
+ size_t add_len) // length of additional AEAD data (bytes)
+{
+ int ret; // our error return if the AES encrypt fails
+ uchar work_buf[16]; // XOR source built from provided IV if len != 16
+ const uchar *p; // general purpose array pointer
+ size_t use_len; // byte count to process, up to 16 bytes
+ size_t i; // local loop iterator
+
+ // since the context might be reused under the same key
+ // we zero the working buffers for this next new process
+ memset(ctx->y, 0x00, sizeof(ctx->y));
+ memset(ctx->buf, 0x00, sizeof(ctx->buf));
+ ctx->len = 0;
+ ctx->add_len = 0;
+
+ ctx->mode = mode; // set the GCM encryption/decryption mode
+ ctx->aes_ctx.mode = MG_ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
+
+ if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
+ memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
+ ctx->y[15] = 1; // start "counting" from 1 (not 0)
+ } else // if we don't have a 12-byte IV, we GHASH whatever we've been given
+ {
+ memset(work_buf, 0x00, 16); // clear the working buffer
+ PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
+
+ p = iv;
+ while (iv_len > 0) {
+ use_len = (iv_len < 16) ? iv_len : 16;
+ for (i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
+ gcm_mult(ctx, ctx->y, ctx->y);
+ iv_len -= use_len;
+ p += use_len;
+ }
+ for (i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
+ gcm_mult(ctx, ctx->y, ctx->y);
+ }
+ if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0)
+ return (ret);
+
+ ctx->add_len = add_len;
+ p = add;
+ while (add_len > 0) {
+ use_len = (add_len < 16) ? add_len : 16;
+ for (i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
+ gcm_mult(ctx, ctx->buf, ctx->buf);
+ add_len -= use_len;
+ p += use_len;
+ }
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM_UPDATE
+ *
+ * This is called once or more to process bulk plaintext or ciphertext data.
+ * We give this some number of bytes of input and it returns the same number
+ * of output bytes. If called multiple times (which is fine) all but the final
+ * invocation MUST be called with length mod 16 == 0. (Only the final call can
+ * have a partial block length of < 128 bits.)
+ *
+ ******************************************************************************/
+int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
+ size_t length, // length, in bytes, of data to process
+ const uchar *input, // pointer to source data
+ uchar *output) // pointer to destination data
+{
+ int ret; // our error return if the AES encrypt fails
+ uchar ectr[16]; // counter-mode cipher output for XORing
+ size_t use_len; // byte count to process, up to 16 bytes
+ size_t i; // local loop iterator
+
+ ctx->len += length; // bump the GCM context's running length count
+
+ while (length > 0) {
+ // clamp the length to process at 16 bytes
+ use_len = (length < 16) ? length : 16;
+
+ // increment the context's 128-bit IV||Counter 'y' vector
+ for (i = 16; i > 12; i--)
+ if (++ctx->y[i - 1] != 0) break;
+
+ // encrypt the context's 'y' vector under the established key
+ if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) return (ret);
+
+ // encrypt or decrypt the input to the output
+ if (ctx->mode == MG_ENCRYPT) {
+ for (i = 0; i < use_len; i++) {
+ // XOR the cipher's ouptut vector (ectr) with our input
+ output[i] = (uchar) (ectr[i] ^ input[i]);
+ // now we mix in our data into the authentication hash.
+ // if we're ENcrypting we XOR in the post-XOR (output)
+ // results, but if we're DEcrypting we XOR in the input
+ // data
+ ctx->buf[i] ^= output[i];
+ }
+ } else {
+ for (i = 0; i < use_len; i++) {
+ // but if we're DEcrypting we XOR in the input data first,
+ // i.e. before saving to ouput data, otherwise if the input
+ // and output buffer are the same (inplace decryption) we
+ // would not get the correct auth tag
- a = (uint32_t *) ctx->in;
- a[14] = ctx->bits[0];
- a[15] = ctx->bits[1];
+ ctx->buf[i] ^= input[i];
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- mg_byte_reverse((unsigned char *) ctx->buf, 4);
- memcpy(digest, ctx->buf, 16);
- memset((char *) ctx, 0, sizeof(*ctx));
-}
-#endif
+ // XOR the cipher's ouptut vector (ectr) with our input
+ output[i] = (uchar) (ectr[i] ^ input[i]);
+ }
+ }
+ gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
-#ifdef MG_ENABLE_LINES
-#line 1 "src/mqtt.c"
-#endif
+ length -= use_len; // drop the remaining byte count to process
+ input += use_len; // bump our input pointer forward
+ output += use_len; // bump our output pointer forward
+ }
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM_FINISH
+ *
+ * This is called once after all calls to GCM_UPDATE to finalize the GCM.
+ * It performs the final GHASH to produce the resulting authentication TAG.
+ *
+ ******************************************************************************/
+int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
+ uchar *tag, // pointer to buffer which receives the tag
+ size_t tag_len) // length, in bytes, of the tag-receiving buf
+{
+ uchar work_buf[16];
+ uint64_t orig_len = ctx->len * 8;
+ uint64_t orig_add_len = ctx->add_len * 8;
+ size_t i;
+ if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
+ if (orig_len || orig_add_len) {
+ memset(work_buf, 0x00, 16);
+ PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
+ PUT_UINT32_BE((orig_add_len), work_buf, 4);
+ PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
+ PUT_UINT32_BE((orig_len), work_buf, 12);
+ for (i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
+ gcm_mult(ctx, ctx->buf, ctx->buf);
+ for (i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
+ }
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM_CRYPT_AND_TAG
+ *
+ * This either encrypts or decrypts the user-provided data and, either
+ * way, generates an authentication tag of the requested length. It must be
+ * called with a GCM context whose key has already been set with GCM_SETKEY.
+ *
+ * The user would typically call this explicitly to ENCRYPT a buffer of data
+ * and optional associated data, and produce its an authentication tag.
+ *
+ * To reverse the process the user would typically call the companion
+ * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
+ * authentication tag. The GCM_AUTH_DECRYPT function calls this function
+ * to perform its decryption and tag generation, which it then compares.
+ *
+ ******************************************************************************/
+int gcm_crypt_and_tag(
+ gcm_context *ctx, // gcm context with key already setup
+ int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
+ const uchar *iv, // pointer to the 12-byte initialization vector
+ size_t iv_len, // byte length if the IV. should always be 12
+ const uchar *add, // pointer to the non-ciphered additional data
+ size_t add_len, // byte length of the additional AEAD data
+ const uchar *input, // pointer to the cipher data source
+ uchar *output, // pointer to the cipher data destination
+ size_t length, // byte length of the cipher data
+ uchar *tag, // pointer to the tag to be generated
+ size_t tag_len) // byte length of the tag to be generated
+{ /*
+ assuming that the caller has already invoked gcm_setkey to
+ prepare the gcm context with the keying material, we simply
+ invoke each of the three GCM sub-functions in turn...
+ */
+ gcm_start(ctx, mode, iv, iv_len, add, add_len);
+ gcm_update(ctx, length, input, output);
+ gcm_finish(ctx, tag, tag_len);
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * GCM_ZERO_CTX
+ *
+ * The GCM context contains both the GCM context and the AES context.
+ * This includes keying and key-related material which is security-
+ * sensitive, so it MUST be zeroed after use. This function does that.
+ *
+ ******************************************************************************/
+void gcm_zero_ctx(gcm_context *ctx) {
+ // zero the context originally provided to us
+ memset(ctx, 0, sizeof(gcm_context));
+}
+//
+// aes-gcm.c
+// Pods
+//
+// Created by Markus Kosmal on 20/11/14.
+//
+//
+int mg_aes_gcm_encrypt(unsigned char *output, //
+ const unsigned char *input, size_t input_length,
+ const unsigned char *key, const size_t key_len,
+ const unsigned char *iv, const size_t iv_len,
+ unsigned char *aead, size_t aead_len, unsigned char *tag,
+ const size_t tag_len) {
+ int ret = 0; // our return value
+ gcm_context ctx; // includes the AES context structure
+ gcm_setkey(&ctx, key, (uint) key_len);
+ ret = gcm_crypt_and_tag(&ctx, MG_ENCRYPT, iv, iv_len, aead, aead_len, input,
+ output, input_length, tag, tag_len);
-#define MQTT_CLEAN_SESSION 0x02
-#define MQTT_HAS_WILL 0x04
-#define MQTT_WILL_RETAIN 0x20
-#define MQTT_HAS_PASSWORD 0x40
-#define MQTT_HAS_USER_NAME 0x80
+ gcm_zero_ctx(&ctx);
-void mg_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 {
- *vlen = len % 0x80;
- len /= 0x80;
- if (len > 0) *vlen |= 0x80;
- vlen++;
- } while (len > 0 && vlen < &buf[sizeof(buf)]);
- mg_send(c, buf, (size_t) (vlen - buf));
+ return (ret);
}
-static void mg_send_u16(struct mg_connection *c, uint16_t value) {
- mg_send(c, &value, sizeof(value));
+int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
+ size_t input_length, const unsigned char *key,
+ const size_t key_len, const unsigned char *iv,
+ const size_t iv_len) {
+ int ret = 0; // our return value
+ gcm_context ctx; // includes the AES context structure
+
+ size_t tag_len = 0;
+ unsigned char *tag_buf = NULL;
+
+ gcm_setkey(&ctx, key, (uint) key_len);
+
+ ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, NULL, 0, input, output,
+ input_length, tag_buf, tag_len);
+
+ gcm_zero_ctx(&ctx);
+
+ return (ret);
}
+#endif
+// End of aes128 PD
-void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- char rnd[10], client_id[21], zero = 0;
- struct mg_str cid = opts->client_id;
- uint32_t total_len = 7 + 1 + 2 + 2;
- uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_builtin.c"
+#endif
- if (cid.len == 0) {
- mg_random(rnd, sizeof(rnd));
- mg_hex(rnd, sizeof(rnd), client_id);
- client_id[sizeof(client_id) - 1] = '\0';
- cid = mg_str(client_id);
- }
- if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1)
- c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag
- hdr[7] = (uint8_t) ((opts->will_qos & 3) << 3); // Connection flags
- if (opts->user.len > 0) {
- total_len += 2 + (uint32_t) opts->user.len;
- hdr[7] |= MQTT_HAS_USER_NAME;
+
+
+#if MG_TLS == MG_TLS_BUILTIN
+
+/* TLS 1.3 Record Content Type (RFC8446 B.1) */
+#define MG_TLS_CHANGE_CIPHER 20
+#define MG_TLS_ALERT 21
+#define MG_TLS_HANDSHAKE 22
+#define MG_TLS_APP_DATA 23
+#define MG_TLS_HEARTBEAT 24
+
+/* TLS 1.3 Handshake Message Type (RFC8446 B.3) */
+#define MG_TLS_CLIENT_HELLO 1
+#define MG_TLS_SERVER_HELLO 2
+#define MG_TLS_ENCRYPTED_EXTENSIONS 8
+#define MG_TLS_CERTIFICATE 11
+#define MG_TLS_CERTIFICATE_VERIFY 15
+#define MG_TLS_FINISHED 20
+
+// handshake is re-entrant, so we need to keep track of its state state names
+// refer to RFC8446#A.1
+enum mg_tls_hs_state {
+ // Client state machine:
+ MG_TLS_STATE_CLIENT_START, // Send ClientHello
+ MG_TLS_STATE_CLIENT_WAIT_SH, // Wait for ServerHello
+ MG_TLS_STATE_CLIENT_WAIT_EE, // Wait for EncryptedExtensions
+ MG_TLS_STATE_CLIENT_WAIT_CERT, // Wait for Certificate
+ MG_TLS_STATE_CLIENT_WAIT_CV, // Wait for CertificateVerify
+ MG_TLS_STATE_CLIENT_WAIT_FINISHED, // Wait for Finished
+ MG_TLS_STATE_CLIENT_CONNECTED, // Done
+
+ // Server state machine:
+ MG_TLS_STATE_SERVER_START, // Wait for ClientHello
+ MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finished
+ MG_TLS_STATE_SERVER_CONNECTED // Done
+};
+
+// per-connection TLS data
+struct tls_data {
+ enum mg_tls_hs_state state; // keep track of connection handshake progress
+
+ struct mg_iobuf send; // For the receive path, we're reusing c->rtls
+ struct mg_iobuf recv; // While c->rtls contains full records, recv reuses
+ // the same underlying buffer but points at individual
+ // decrypted messages
+ uint8_t content_type; // Last received record content type
+
+ mg_sha256_ctx sha256; // incremental SHA-256 hash for TLS handshake
+
+ uint32_t sseq; // server sequence number, used in encryption
+ uint32_t cseq; // client sequence number, used in decryption
+
+ uint8_t random[32]; // client random from ClientHello
+ uint8_t session_id[32]; // client session ID between the handshake states
+ uint8_t x25519_cli[32]; // client X25519 key between the handshake states
+ uint8_t x25519_sec[32]; // x25519 secret between the handshake states
+
+ int skip_verification; // perform checks on server certificate?
+ struct mg_str server_cert_der; // server certificate in DER format
+ uint8_t server_key[32]; // server EC private key
+ char hostname[254]; // server hostname (client extension)
+
+ uint8_t certhash[32]; // certificate message hash
+ uint8_t pubkey[64]; // server EC public key to verify cert
+ uint8_t sighash[32]; // server EC public key to verify cert
+
+ // keys for AES encryption
+ uint8_t handshake_secret[32];
+ uint8_t server_write_key[16];
+ uint8_t server_write_iv[12];
+ uint8_t server_finished_key[32];
+ uint8_t client_write_key[16];
+ uint8_t client_write_iv[12];
+ uint8_t client_finished_key[32];
+};
+
+#define MG_LOAD_BE16(p) ((uint16_t) ((MG_U8P(p)[0] << 8U) | MG_U8P(p)[1]))
+#define MG_LOAD_BE24(p) \
+ ((uint32_t) ((MG_U8P(p)[0] << 16U) | (MG_U8P(p)[1] << 8U) | MG_U8P(p)[2]))
+#define MG_STORE_BE16(p, n) \
+ do { \
+ MG_U8P(p)[0] = ((n) >> 8U) & 255; \
+ MG_U8P(p)[1] = (n) & 255; \
+ } while (0)
+
+#define TLS_RECHDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes length
+#define TLS_MSGHDR_SIZE 4 // 1 byte type, 3 bytes length
+
+#if 1
+static void mg_ssl_key_log(const char *label, uint8_t client_random[32],
+ uint8_t *secret, size_t secretsz) {
+ (void) label;
+ (void) client_random;
+ (void) secret;
+ (void) secretsz;
+}
+#else
+#include <stdio.h>
+static void mg_ssl_key_log(const char *label, uint8_t client_random[32],
+ uint8_t *secret, size_t secretsz) {
+ char *keylogfile = getenv("SSLKEYLOGFILE");
+ if (keylogfile == NULL) {
+ return;
}
- if (opts->pass.len > 0) {
- total_len += 2 + (uint32_t) opts->pass.len;
- hdr[7] |= MQTT_HAS_PASSWORD;
+ FILE *f = fopen(keylogfile, "a");
+ fprintf(f, "%s ", label);
+ for (int i = 0; i < 32; i++) {
+ fprintf(f, "%02x", client_random[i]);
}
- if (opts->will_topic.len > 0 && opts->will_message.len > 0) {
- total_len +=
- 4 + (uint32_t) opts->will_topic.len + (uint32_t) opts->will_message.len;
- hdr[7] |= MQTT_HAS_WILL;
+ fprintf(f, " ");
+ for (unsigned int i = 0; i < secretsz; i++) {
+ fprintf(f, "%02x", secret[i]);
}
- if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION;
- if (opts->will_retain) hdr[7] |= MQTT_WILL_RETAIN;
- total_len += (uint32_t) cid.len;
- if (c->is_mqtt5) total_len += 1U + (hdr[7] & MQTT_HAS_WILL ? 1U : 0);
+ fprintf(f, "\n");
+ fclose(f);
+}
+#endif
- mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, 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));
+// for derived tls keys we need SHA256([0]*32)
+static uint8_t zeros[32] = {0};
+static uint8_t zeros_sha256_digest[32] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
+ 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
+ 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero)); // V5 properties
- mg_send_u16(c, mg_htons((uint16_t) cid.len));
- mg_send(c, cid.ptr, cid.len);
+// helper to hexdump buffers inline
+static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) {
+ MG_VERBOSE(("%s: %M", msg, mg_print_hex, bufsz, buf));
+}
- if (hdr[7] & MQTT_HAS_WILL) {
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero)); // will props
- mg_send_u16(c, mg_htons((uint16_t) opts->will_topic.len));
- mg_send(c, opts->will_topic.ptr, opts->will_topic.len);
- mg_send_u16(c, mg_htons((uint16_t) opts->will_message.len));
- mg_send(c, opts->will_message.ptr, opts->will_message.len);
+// helper utilities to parse ASN.1 DER
+struct mg_der_tlv {
+ uint8_t type;
+ uint32_t len;
+ uint8_t *value;
+};
+
+// parse DER into a TLV record
+static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
+ if (dersz < 2) {
+ return -1;
}
- if (opts->user.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->user.len));
- mg_send(c, opts->user.ptr, opts->user.len);
+ tlv->type = der[0];
+ tlv->len = der[1];
+ tlv->value = der + 2;
+ if (tlv->len > 0x7f) {
+ uint32_t i, n = tlv->len - 0x80;
+ tlv->len = 0;
+ for (i = 0; i < n; i++) {
+ tlv->len = (tlv->len << 8) | (der[2 + i]);
+ }
+ tlv->value = der + 2 + n;
}
- if (opts->pass.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->pass.len));
- mg_send(c, opts->pass.ptr, opts->pass.len);
+ if (der + dersz < tlv->value + tlv->len) {
+ return -1;
}
+ return 0;
}
-void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
- struct mg_str data, int qos, bool retain) {
- uint8_t flags = (uint8_t) (((qos & 3) << 1) | (retain ? 1 : 0)), zero = 0;
- uint32_t len = 2 + (uint32_t) topic.len + (uint32_t) data.len;
- MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) topic.len, (char *) topic.ptr,
- (int) data.len, (char *) data.ptr));
- if (qos > 0) len += 2;
- if (c->is_mqtt5) len++;
- mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, len);
- mg_send_u16(c, mg_htons((uint16_t) topic.len));
- mg_send(c, topic.ptr, topic.len);
- if (qos > 0) {
- if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
+static int mg_der_find(uint8_t *der, size_t dersz, uint8_t *oid, size_t oidsz,
+ struct mg_der_tlv *tlv) {
+ uint8_t *p, *end;
+ struct mg_der_tlv child = {0, 0, NULL};
+ if (mg_der_to_tlv(der, dersz, tlv) < 0) {
+ return -1; // invalid DER
+ } else if (tlv->type == 6) { // found OID, check value
+ return (tlv->len == oidsz && memcmp(tlv->value, oid, oidsz) == 0);
+ } else if ((tlv->type & 0x20) == 0) {
+ return 0; // Primitive, but not OID: not found
+ }
+ // Constructed object: scan children
+ p = tlv->value;
+ end = tlv->value + tlv->len;
+ while (end > p) {
+ int r;
+ mg_der_to_tlv(p, (size_t) (end - p), &child);
+ r = mg_der_find(p, (size_t) (end - p), oid, oidsz, tlv);
+ if (r < 0) return -1; // error
+ if (r > 0) return 1; // found OID!
+ p = child.value + child.len;
}
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero));
- mg_send(c, data.ptr, data.len);
+ return 0; // not found
}
-void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos) {
- uint8_t qos_ = qos & 3, zero = 0;
- uint32_t len = 2 + (uint32_t) topic.len + 2 + 1 + (c->is_mqtt5 ? 1 : 0);
- mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, len);
- 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(c, &zero, sizeof(zero));
- mg_send_u16(c, mg_htons((uint16_t) topic.len));
- mg_send(c, topic.ptr, topic.len);
- mg_send(c, &qos_, sizeof(qos_));
+// Did we receive a full TLS record in the c->rtls buffer?
+static bool mg_tls_got_record(struct mg_connection *c) {
+ return c->rtls.len >= (size_t) TLS_RECHDR_SIZE &&
+ c->rtls.len >=
+ (size_t) (TLS_RECHDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3));
}
-int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
- struct mg_mqtt_message *m) {
- uint8_t lc = 0, *p, *end;
- uint32_t n = 0, len_len = 0;
+// Remove a single TLS record from the recv buffer
+static void mg_tls_drop_record(struct mg_connection *c) {
+ struct mg_iobuf *rio = &c->rtls;
+ uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_RECHDR_SIZE;
+ mg_iobuf_del(rio, 0, n);
+}
- memset(m, 0, sizeof(*m));
- m->dgram.ptr = (char *) buf;
- if (len < 2) return MQTT_INCOMPLETE;
- m->cmd = (uint8_t) (buf[0] >> 4);
- m->qos = (buf[0] >> 1) & 3;
+// Remove a single TLS message from decrypted buffer, remove the wrapping
+// record if it was the last message within a record
+static void mg_tls_drop_message(struct mg_connection *c) {
+ uint32_t len;
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (tls->recv.len == 0) {
+ return;
+ }
+ len = MG_LOAD_BE24(tls->recv.buf + 1);
+ mg_sha256_update(&tls->sha256, tls->recv.buf, len + TLS_MSGHDR_SIZE);
+ tls->recv.buf += len + TLS_MSGHDR_SIZE;
+ tls->recv.len -= len + TLS_MSGHDR_SIZE;
+ if (tls->recv.len == 0) {
+ mg_tls_drop_record(c);
+ }
+}
- n = len_len = 0;
- p = (uint8_t *) buf + 1;
- while ((size_t) (p - buf) < len) {
- lc = *((uint8_t *) p++);
- n += (uint32_t) ((lc & 0x7f) << 7 * len_len);
- len_len++;
- if (!(lc & 0x80)) break;
- if (len_len >= 4) return MQTT_MALFORMED;
+// TLS1.3 secret derivation based on the key label
+static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz,
+ uint8_t *data, size_t datasz, uint8_t *hash,
+ size_t hashsz) {
+ size_t labelsz = strlen(label);
+ uint8_t secret[32];
+ uint8_t packed[256] = {0, (uint8_t) hashsz, (uint8_t) labelsz};
+ // TODO: assert lengths of label, key, data and hash
+ if (labelsz > 0) memmove(packed + 3, label, labelsz);
+ packed[3 + labelsz] = (uint8_t) datasz;
+ if (datasz > 0) memmove(packed + labelsz + 4, data, datasz);
+ packed[4 + labelsz + datasz] = 1;
+
+ mg_hmac_sha256(secret, key, keysz, packed, 5 + labelsz + datasz);
+ memmove(hash, secret, hashsz);
+}
+
+// at this point we have x25519 shared secret, we can generate a set of derived
+// handshake encryption keys
+static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+
+ mg_sha256_ctx sha256;
+ uint8_t early_secret[32];
+ uint8_t pre_extract_secret[32];
+ uint8_t hello_hash[32];
+ uint8_t server_hs_secret[32];
+ uint8_t client_hs_secret[32];
+
+ mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros));
+ mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest,
+ 32, pre_extract_secret, 32);
+ mg_hmac_sha256(tls->handshake_secret, pre_extract_secret,
+ sizeof(pre_extract_secret), tls->x25519_sec,
+ sizeof(tls->x25519_sec));
+ mg_tls_hexdump("hs secret", tls->handshake_secret, 32);
+
+ // mg_sha256_final is not idempotent, need to copy sha256 context to calculate
+ // the digest
+ memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
+ mg_sha256_final(hello_hash, &sha256);
+
+ mg_tls_hexdump("hello hash", hello_hash, 32);
+ // derive keys needed for the rest of the handshake
+ mg_tls_derive_secret("tls13 s hs traffic", tls->handshake_secret, 32,
+ hello_hash, 32, server_hs_secret, 32);
+ mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0,
+ tls->server_write_key, 16);
+ mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0,
+ tls->server_write_iv, 12);
+ mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0,
+ tls->server_finished_key, 32);
+
+ mg_tls_derive_secret("tls13 c hs traffic", tls->handshake_secret, 32,
+ hello_hash, 32, client_hs_secret, 32);
+ mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0,
+ tls->client_write_key, 16);
+ mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0,
+ tls->client_write_iv, 12);
+ mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0,
+ tls->client_finished_key, 32);
+
+ mg_tls_hexdump("s hs traffic", server_hs_secret, 32);
+ mg_tls_hexdump("s key", tls->server_write_key, 16);
+ mg_tls_hexdump("s iv", tls->server_write_iv, 12);
+ mg_tls_hexdump("s finished", tls->server_finished_key, 32);
+ mg_tls_hexdump("c hs traffic", client_hs_secret, 32);
+ mg_tls_hexdump("c key", tls->client_write_key, 16);
+ mg_tls_hexdump("c iv", tls->client_write_iv, 16);
+ mg_tls_hexdump("c finished", tls->client_finished_key, 32);
+
+ mg_ssl_key_log("SERVER_HANDSHAKE_TRAFFIC_SECRET", tls->random,
+ server_hs_secret, 32);
+ mg_ssl_key_log("CLIENT_HANDSHAKE_TRAFFIC_SECRET", tls->random,
+ client_hs_secret, 32);
+}
+
+static void mg_tls_generate_application_keys(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ uint8_t hash[32];
+ uint8_t premaster_secret[32];
+ uint8_t master_secret[32];
+ uint8_t server_secret[32];
+ uint8_t client_secret[32];
+
+ mg_sha256_ctx sha256;
+ memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
+ mg_sha256_final(hash, &sha256);
+
+ mg_tls_derive_secret("tls13 derived", tls->handshake_secret, 32,
+ zeros_sha256_digest, 32, premaster_secret, 32);
+ mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32);
+
+ mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32,
+ server_secret, 32);
+ mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0,
+ tls->server_write_key, 16);
+ mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0,
+ tls->server_write_iv, 12);
+ mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32,
+ client_secret, 32);
+ mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0,
+ tls->client_write_key, 16);
+ mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0,
+ tls->client_write_iv, 12);
+
+ mg_tls_hexdump("s ap traffic", server_secret, 32);
+ mg_tls_hexdump("s key", tls->server_write_key, 16);
+ mg_tls_hexdump("s iv", tls->server_write_iv, 12);
+ mg_tls_hexdump("s finished", tls->server_finished_key, 32);
+ mg_tls_hexdump("c ap traffic", client_secret, 32);
+ mg_tls_hexdump("c key", tls->client_write_key, 16);
+ mg_tls_hexdump("c iv", tls->client_write_iv, 16);
+ mg_tls_hexdump("c finished", tls->client_finished_key, 32);
+ tls->sseq = tls->cseq = 0;
+
+ mg_ssl_key_log("SERVER_TRAFFIC_SECRET_0", tls->random, server_secret, 32);
+ mg_ssl_key_log("CLIENT_TRAFFIC_SECRET_0", tls->random, client_secret, 32);
+}
+
+// AES GCM encryption of the message + put encoded data into the write buffer
+static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
+ size_t msgsz, uint8_t msgtype) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *wio = &tls->send;
+ uint8_t *outmsg;
+ uint8_t *tag;
+ size_t encsz = msgsz + 16 + 1;
+ uint8_t hdr[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
+ (uint8_t) ((encsz >> 8) & 0xff), (uint8_t) (encsz & 0xff)};
+ uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
+ (uint8_t) ((encsz >> 8) & 0xff),
+ (uint8_t) (encsz & 0xff)};
+ uint8_t nonce[12];
+
+ mg_gcm_initialize();
+
+ if (c->is_client) {
+ memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv));
+ nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U);
+ nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U);
+ nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U);
+ nonce[11] ^= (uint8_t) ((tls->cseq) & 255U);
+ } else {
+ memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv));
+ nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U);
+ nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U);
+ nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U);
+ nonce[11] ^= (uint8_t) ((tls->sseq) & 255U);
}
- end = p + n;
- if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE;
- m->dgram.len = (size_t) (end - buf);
- switch (m->cmd) {
- case MQTT_CMD_CONNACK:
- if (end - p < 2) return MQTT_MALFORMED;
- m->ack = p[1];
- break;
- case MQTT_CMD_PUBACK:
- case MQTT_CMD_PUBREC:
- case MQTT_CMD_PUBREL:
- case MQTT_CMD_PUBCOMP:
- case MQTT_CMD_SUBSCRIBE:
- case MQTT_CMD_SUBACK:
- case MQTT_CMD_UNSUBSCRIBE:
- case MQTT_CMD_UNSUBACK:
- if (p + 2 > end) return MQTT_MALFORMED;
- m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- p += 2;
- break;
- case MQTT_CMD_PUBLISH: {
- if (p + 2 > end) return MQTT_MALFORMED;
- m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- m->topic.ptr = (char *) p + 2;
- p += 2 + m->topic.len;
- if (p > end) return MQTT_MALFORMED;
- if (m->qos > 0) {
- if (p + 2 > end) return MQTT_MALFORMED;
- m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- p += 2;
- }
- if (p > end) return MQTT_MALFORMED;
- if (version == 5 && p + 2 < end) p += 1 + p[0]; // Skip options
- if (p > end) return MQTT_MALFORMED;
- m->data.ptr = (char *) p;
- m->data.len = (size_t) (end - p);
- break;
+ mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr));
+ mg_iobuf_resize(wio, wio->len + encsz);
+ outmsg = wio->buf + wio->len;
+ tag = wio->buf + wio->len + msgsz + 1;
+ memmove(outmsg, msg, msgsz);
+ outmsg[msgsz] = msgtype;
+ if (c->is_client) {
+ mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->client_write_key,
+ sizeof(tls->client_write_key), nonce, sizeof(nonce),
+ associated_data, sizeof(associated_data), tag, 16);
+ tls->cseq++;
+ } else {
+ mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->server_write_key,
+ sizeof(tls->server_write_key), nonce, sizeof(nonce),
+ associated_data, sizeof(associated_data), tag, 16);
+ tls->sseq++;
+ }
+ wio->len += encsz;
+}
+
+// read an encrypted record, decrypt it in place
+static int mg_tls_recv_record(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *rio = &c->rtls;
+ uint16_t msgsz;
+ uint8_t *msg;
+ uint8_t nonce[12];
+ int r;
+ if (tls->recv.len > 0) {
+ return 0; /* some data from previous record is still present */
+ }
+ for (;;) {
+ if (!mg_tls_got_record(c)) {
+ return MG_IO_WAIT;
}
- default:
+ if (rio->buf[0] == MG_TLS_APP_DATA) {
break;
+ } else if (rio->buf[0] ==
+ MG_TLS_CHANGE_CIPHER) { // Skip ChangeCipher messages
+ mg_tls_drop_record(c);
+ } else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts
+ MG_INFO(("TLS ALERT packet received"));
+ mg_tls_drop_record(c);
+ } else {
+ mg_error(c, "unexpected packet");
+ return -1;
+ }
}
- return MQTT_OK;
+
+ mg_gcm_initialize();
+ msgsz = MG_LOAD_BE16(rio->buf + 3);
+ msg = rio->buf + 5;
+ if (c->is_client) {
+ memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv));
+ nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U);
+ nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U);
+ nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U);
+ nonce[11] ^= (uint8_t) ((tls->sseq) & 255U);
+ mg_aes_gcm_decrypt(msg, msg, msgsz - 16, tls->server_write_key,
+ sizeof(tls->server_write_key), nonce, sizeof(nonce));
+ tls->sseq++;
+ } else {
+ memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv));
+ nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U);
+ nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U);
+ nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U);
+ nonce[11] ^= (uint8_t) ((tls->cseq) & 255U);
+ mg_aes_gcm_decrypt(msg, msg, msgsz - 16, tls->client_write_key,
+ sizeof(tls->client_write_key), nonce, sizeof(nonce));
+ tls->cseq++;
+ }
+ r = msgsz - 16 - 1;
+ tls->content_type = msg[msgsz - 16 - 1];
+ tls->recv.buf = msg;
+ tls->recv.size = tls->recv.len = msgsz - 16 - 1;
+ return r;
}
-static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- if (ev == MG_EV_READ) {
- for (;;) {
- uint8_t version = c->is_mqtt5 ? 5 : 4;
- struct mg_mqtt_message mm;
- int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm);
- if (rc == MQTT_MALFORMED) {
- MG_ERROR(("%lu MQTT malformed message", c->id));
- c->is_closing = 1;
- break;
- } else if (rc == MQTT_OK) {
- MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
- (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr));
- switch (mm.cmd) {
- case MQTT_CMD_CONNACK:
- mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
- if (mm.ack == 0) {
- MG_DEBUG(("%lu Connected", c->id));
- } else {
- MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack));
- c->is_closing = 1;
- }
- break;
- case MQTT_CMD_PUBLISH: {
- MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) mm.topic.len,
- mm.topic.ptr, (int) mm.data.len, mm.data.ptr));
- if (mm.qos > 0) {
- uint16_t id = mg_htons(mm.id);
- mg_mqtt_send_header(c, MQTT_CMD_PUBACK, 0, sizeof(id));
- mg_send(c, &id, sizeof(id));
- }
- mg_call(c, MG_EV_MQTT_MSG, &mm);
- break;
- }
- }
- mg_call(c, MG_EV_MQTT_CMD, &mm);
- mg_iobuf_del(&c->recv, 0, mm.dgram.len);
- } else {
- break;
+static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
+ uint8_t hash[32]) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ uint8_t sig_content[130] = {
+ " "
+ " "
+ "TLS 1.3, server CertificateVerify\0"};
+ mg_sha256_ctx sha256;
+ memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
+ mg_sha256_final(sig_content + 98, &sha256);
+
+ mg_sha256_init(&sha256);
+ mg_sha256_update(&sha256, sig_content, sizeof(sig_content));
+ mg_sha256_final(hash, &sha256);
+}
+
+// read and parse ClientHello record
+static int mg_tls_server_recv_hello(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *rio = &c->rtls;
+ uint8_t session_id_len;
+ uint16_t j;
+ uint16_t cipher_suites_len;
+ uint16_t ext_len;
+ uint8_t *ext;
+ uint16_t msgsz;
+
+ if (!mg_tls_got_record(c)) {
+ return MG_IO_WAIT;
+ }
+ if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
+ mg_error(c, "not a client hello packet");
+ return -1;
+ }
+ msgsz = MG_LOAD_BE16(rio->buf + 3);
+ mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
+ // store client random
+ memmove(tls->random, rio->buf + 11, sizeof(tls->random));
+ // store session_id
+ session_id_len = rio->buf[43];
+ if (session_id_len == sizeof(tls->session_id)) {
+ memmove(tls->session_id, rio->buf + 44, session_id_len);
+ } else if (session_id_len != 0) {
+ MG_INFO(("bad session id len"));
+ }
+ cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len);
+ ext_len = MG_LOAD_BE16(rio->buf + 48 + session_id_len + cipher_suites_len);
+ ext = rio->buf + 50 + session_id_len + cipher_suites_len;
+ for (j = 0; j < ext_len;) {
+ uint16_t k;
+ uint16_t key_exchange_len;
+ uint8_t *key_exchange;
+ uint16_t n = MG_LOAD_BE16(ext + j + 2);
+ if (ext[j] != 0x00 ||
+ ext[j + 1] != 0x33) { // not a key share extension, ignore
+ j += (uint16_t) (n + 4);
+ continue;
+ }
+ key_exchange_len = MG_LOAD_BE16(ext + j + 5);
+ key_exchange = ext + j + 6;
+ for (k = 0; k < key_exchange_len;) {
+ uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
+ if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) {
+ memmove(tls->x25519_cli, key_exchange + k + 4, m);
+ mg_tls_drop_record(c);
+ return 0;
}
+ k += (uint16_t) (m + 4);
}
+ j += (uint16_t) (n + 4);
}
- (void) ev_data;
- (void) fn_data;
-}
-
-void mg_mqtt_ping(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0);
-}
-
-void mg_mqtt_pong(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0);
+ mg_error(c, "bad client hello");
+ return -1;
}
-void mg_mqtt_disconnect(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_DISCONNECT, 0, 0);
-}
+#define PLACEHOLDER_8B 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
+#define PLACEHOLDER_16B PLACEHOLDER_8B, PLACEHOLDER_8B
+#define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B
+
+// put ServerHello record into wio buffer
+static void mg_tls_server_send_hello(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *wio = &tls->send;
+
+ uint8_t msg_server_hello[122] = {
+ // server hello, tls 1.2
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x76,
+ 0x03,
+ 0x03,
+ // random (32 bytes)
+ PLACEHOLDER_32B,
+ // session ID length + session ID (32 bytes)
+ 0x20,
+ PLACEHOLDER_32B,
+#if defined(CHACHA20) && CHACHA20
+ // TLS_CHACHA20_POLY1305_SHA256 + no compression
+ 0x13,
+ 0x03,
+ 0x00,
+#else
+ // TLS_AES_128_GCM_SHA256 + no compression
+ 0x13,
+ 0x01,
+ 0x00,
+#endif
+ // extensions + keyshare
+ 0x00,
+ 0x2e,
+ 0x00,
+ 0x33,
+ 0x00,
+ 0x24,
+ 0x00,
+ 0x1d,
+ 0x00,
+ 0x20,
+ // x25519 keyshare
+ PLACEHOLDER_32B,
+ // supported versions (tls1.3 == 0x304)
+ 0x00,
+ 0x2b,
+ 0x00,
+ 0x02,
+ 0x03,
+ 0x04
+ };
-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(mgr, url, fn, fn_data);
- if (c != NULL) {
- struct mg_mqtt_opts empty;
- memset(&empty, 0, sizeof(empty));
- mg_mqtt_login(c, opts == NULL ? &empty : opts);
- c->pfn = mqtt_cb;
+ // calculate keyshare
+ uint8_t x25519_pub[X25519_BYTES];
+ uint8_t x25519_prv[X25519_BYTES];
+ mg_random(x25519_prv, sizeof(x25519_prv));
+ mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1);
+ mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1);
+ mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec));
+
+ // fill in the gaps: random + session ID + keyshare
+ memmove(msg_server_hello + 6, tls->random, sizeof(tls->random));
+ memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id));
+ memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
+
+ // server hello message
+ mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5);
+ mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello));
+ mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
+
+ // change cipher message
+ mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6);
+}
+
+static void mg_tls_server_send_ext(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ // server extensions
+ uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0};
+ mg_sha256_update(&tls->sha256, ext, sizeof(ext));
+ mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
+}
+
+static void mg_tls_server_send_cert(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ // server DER certificate (empty)
+ size_t n = tls->server_cert_der.len;
+ uint8_t *cert = (uint8_t *) calloc(1, 13 + n);
+ if (cert == NULL) {
+ mg_error(c, "tls cert oom");
+ return;
}
- return c;
-}
+ cert[0] = 0x0b; // handshake header
+ cert[1] = (uint8_t) (((n + 9) >> 16) & 255U); // 3 bytes: payload length
+ cert[2] = (uint8_t) (((n + 9) >> 8) & 255U);
+ cert[3] = (uint8_t) ((n + 9) & 255U);
+ cert[4] = 0; // request context
+ cert[5] = (uint8_t) (((n + 5) >> 16) & 255U); // 3 bytes: cert (s) length
+ cert[6] = (uint8_t) (((n + 5) >> 8) & 255U);
+ cert[7] = (uint8_t) ((n + 5) & 255U);
+ cert[8] =
+ (uint8_t) (((n) >> 16) & 255U); // 3 bytes: first (and only) cert len
+ cert[9] = (uint8_t) (((n) >> 8) & 255U);
+ cert[10] = (uint8_t) (n & 255U);
+ // bytes 11+ are certificate in DER format
+ memmove(cert + 11, tls->server_cert_der.buf, n);
+ cert[11 + n] = cert[12 + n] = 0; // certificate extensions (none)
+ mg_sha256_update(&tls->sha256, cert, 13 + n);
+ mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
+ free(cert);
+}
+
+// type adapter between uECC hash context and our sha256 implementation
+typedef struct SHA256_HashContext {
+ MG_UECC_HashContext uECC;
+ mg_sha256_ctx ctx;
+} SHA256_HashContext;
+
+static void init_SHA256(const MG_UECC_HashContext *base) {
+ SHA256_HashContext *c = (SHA256_HashContext *) base;
+ mg_sha256_init(&c->ctx);
+}
+
+static void update_SHA256(const MG_UECC_HashContext *base,
+ const uint8_t *message, unsigned message_size) {
+ SHA256_HashContext *c = (SHA256_HashContext *) base;
+ mg_sha256_update(&c->ctx, message, message_size);
+}
+static void finish_SHA256(const MG_UECC_HashContext *base,
+ uint8_t *hash_result) {
+ SHA256_HashContext *c = (SHA256_HashContext *) base;
+ mg_sha256_final(hash_result, &c->ctx);
+}
+
+static void mg_tls_server_send_cert_verify(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ // server certificate verify packet
+ uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
+ size_t sigsz, verifysz = 0;
+ uint8_t hash[32] = {0}, tmp[2 * 32 + 64] = {0};
+ struct SHA256_HashContext ctx = {
+ {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp},
+ {{0}, 0, 0, {0}}};
+ int neg1, neg2;
+ uint8_t sig[64] = {0};
+
+ mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash);
+
+ mg_uecc_sign_deterministic(tls->server_key, hash, sizeof(hash), &ctx.uECC,
+ sig, mg_uecc_secp256r1());
+
+ neg1 = !!(sig[0] & 0x80);
+ neg2 = !!(sig[32] & 0x80);
+ verify[8] = 0x30; // ASN.1 SEQUENCE
+ verify[9] = (uint8_t) (68 + neg1 + neg2);
+ verify[10] = 0x02; // ASN.1 INTEGER
+ verify[11] = (uint8_t) (32 + neg1);
+ memmove(verify + 12 + neg1, sig, 32);
+ verify[12 + 32 + neg1] = 0x02; // ASN.1 INTEGER
+ verify[13 + 32 + neg1] = (uint8_t) (32 + neg2);
+ memmove(verify + 14 + 32 + neg1 + neg2, sig + 32, 32);
+
+ sigsz = (size_t) (70 + neg1 + neg2);
+ verifysz = 8U + sigsz;
+ verify[3] = (uint8_t) (sigsz + 4);
+ verify[7] = (uint8_t) sigsz;
+
+ mg_sha256_update(&tls->sha256, verify, verifysz);
+ mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
+}
+
+static void mg_tls_server_send_finish(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *wio = &tls->send;
+ mg_sha256_ctx sha256;
+ uint8_t hash[32];
+ uint8_t finish[36] = {0x14, 0, 0, 32};
+ memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
+ mg_sha256_final(hash, &sha256);
+ mg_hmac_sha256(finish + 4, tls->server_finished_key, 32, hash, 32);
+ mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
+ mg_io_send(c, wio->buf, wio->len);
+ wio->len = 0;
+
+ mg_sha256_update(&tls->sha256, finish, sizeof(finish));
+}
+
+static int mg_tls_server_recv_finish(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ // we have to backup sha256 value to restore it later, since Finished record
+ // is exceptional and is not supposed to be added to the rolling hash
+ // calculation.
+ mg_sha256_ctx sha256 = tls->sha256;
+ if (mg_tls_recv_record(c) < 0) {
+ return -1;
+ }
+ if (tls->recv.buf[0] != MG_TLS_FINISHED) {
+ mg_error(c, "expected Finish but got msg 0x%02x", tls->recv.buf[0]);
+ return -1;
+ }
+ mg_tls_drop_message(c);
-struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr;
- return c;
+ // restore hash
+ tls->sha256 = sha256;
+ return 0;
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/net.c"
+static void mg_tls_client_send_hello(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *wio = &tls->send;
+
+ const char *hostname = tls->hostname;
+ size_t hostnamesz = strlen(tls->hostname);
+ uint8_t x25519_pub[X25519_BYTES];
+
+ uint8_t msg_client_hello[162 + 32] = {
+ // TLS Client Hello header reported as TLS1.2 (5)
+ 0x16,
+ 0x03,
+ 0x01,
+ 0x00,
+ 0xfe,
+ // server hello, tls 1.2 (6)
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x8c,
+ 0x03,
+ 0x03,
+ // random (32 bytes)
+ PLACEHOLDER_32B,
+ // session ID length + session ID (32 bytes)
+ 0x20,
+ PLACEHOLDER_32B,
+#if defined(CHACHA20) && CHACHA20
+ // TLS_CHACHA20_POLY1305_SHA256 + no compression
+ 0x13,
+ 0x03,
+ 0x00,
+#else
+ 0x00,
+ 0x02, // size = 2 bytes
+ 0x13,
+ 0x01, // TLS_AES_128_GCM_SHA256
+ 0x01,
+ 0x00, // no compression
#endif
+ // extensions + keyshare
+ 0x00,
+ 0xfe,
+ // x25519 keyshare
+ 0x00,
+ 0x33,
+ 0x00,
+ 0x26,
+ 0x00,
+ 0x24,
+ 0x00,
+ 0x1d,
+ 0x00,
+ 0x20,
+ PLACEHOLDER_32B,
+ // supported groups (x25519)
+ 0x00,
+ 0x0a,
+ 0x00,
+ 0x04,
+ 0x00,
+ 0x02,
+ 0x00,
+ 0x1d,
+ // supported versions (tls1.3 == 0x304)
+ 0x00,
+ 0x2b,
+ 0x00,
+ 0x03,
+ 0x02,
+ 0x03,
+ 0x04,
+ // session ticket (none)
+ 0x00,
+ 0x23,
+ 0x00,
+ 0x00,
+ // signature algorithms (we don't care, so list all the common ones)
+ 0x00,
+ 0x0d,
+ 0x00,
+ 0x24,
+ 0x00,
+ 0x22,
+ 0x04,
+ 0x03,
+ 0x05,
+ 0x03,
+ 0x06,
+ 0x03,
+ 0x08,
+ 0x07,
+ 0x08,
+ 0x08,
+ 0x08,
+ 0x1a,
+ 0x08,
+ 0x1b,
+ 0x08,
+ 0x1c,
+ 0x08,
+ 0x09,
+ 0x08,
+ 0x0a,
+ 0x08,
+ 0x0b,
+ 0x08,
+ 0x04,
+ 0x08,
+ 0x05,
+ 0x08,
+ 0x06,
+ 0x04,
+ 0x01,
+ 0x05,
+ 0x01,
+ 0x06,
+ 0x01,
+ // server name
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xfe,
+ 0x00,
+ 0xfe,
+ 0x00,
+ 0x00,
+ 0xfe
+ };
+ // patch ClientHello with correct hostname length + offset:
+ MG_STORE_BE16(msg_client_hello + 3, hostnamesz + 189);
+ MG_STORE_BE16(msg_client_hello + 7, hostnamesz + 185);
+ MG_STORE_BE16(msg_client_hello + 82, hostnamesz + 110);
+ MG_STORE_BE16(msg_client_hello + 187, hostnamesz + 5);
+ MG_STORE_BE16(msg_client_hello + 189, hostnamesz + 3);
+ MG_STORE_BE16(msg_client_hello + 192, hostnamesz);
+
+ // calculate keyshare
+ mg_random(tls->x25519_cli, sizeof(tls->x25519_cli));
+ mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1);
+
+ // fill in the gaps: random + session ID + keyshare
+ mg_random(tls->session_id, sizeof(tls->session_id));
+ mg_random(tls->random, sizeof(tls->random));
+ memmove(msg_client_hello + 11, tls->random, sizeof(tls->random));
+ memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id));
+ memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
+
+ // server hello message
+ mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello));
+ mg_iobuf_add(wio, wio->len, hostname, strlen(hostname));
+ mg_sha256_update(&tls->sha256, msg_client_hello + 5,
+ sizeof(msg_client_hello) - 5);
+ mg_sha256_update(&tls->sha256, (uint8_t *) hostname, strlen(hostname));
+
+ // change cipher message
+ mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6);
+ mg_io_send(c, wio->buf, wio->len);
+ wio->len = 0;
+}
+
+static int mg_tls_client_recv_hello(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *rio = &c->rtls;
+ uint16_t msgsz;
+ uint8_t *ext;
+ uint16_t ext_len;
+ int j;
+
+ if (!mg_tls_got_record(c)) {
+ return MG_IO_WAIT;
+ }
+ if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_SERVER_HELLO) {
+ if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) {
+ mg_error(c, "tls alert %d", rio->buf[6]);
+ return -1;
+ }
+ MG_INFO(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5]));
+ mg_error(c, "not a server hello packet");
+ return -1;
+ }
-
-
-
-
-size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) {
- size_t old = c->send.len;
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- return c->send.len - old;
-}
-
-size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
- size_t len = 0;
- va_list ap;
- va_start(ap, fmt);
- len = mg_vprintf(c, fmt, &ap);
- va_end(ap);
- return len;
-}
-
-static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
- if (mg_vcasecmp(&str, "localhost") != 0) return false;
- addr->ip = mg_htonl(0x7f000001);
- addr->is_ip6 = false;
- return true;
+ msgsz = MG_LOAD_BE16(rio->buf + 3);
+ mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
+
+ ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3);
+ ext = rio->buf + 5 + 39 + 32 + 3 + 2;
+
+ for (j = 0; j < ext_len;) {
+ uint16_t ext_type = MG_LOAD_BE16(ext + j);
+ uint16_t ext_len2 = MG_LOAD_BE16(ext + j + 2);
+ uint16_t group;
+ uint8_t *key_exchange;
+ uint16_t key_exchange_len;
+ if (ext_type != 0x0033) { // not a key share extension, ignore
+ j += (uint16_t) (ext_len2 + 4);
+ continue;
+ }
+ group = MG_LOAD_BE16(ext + j + 4);
+ if (group != 0x001d) {
+ mg_error(c, "bad key exchange group");
+ return -1;
+ }
+ key_exchange_len = MG_LOAD_BE16(ext + j + 6);
+ key_exchange = ext + j + 8;
+ if (key_exchange_len != 32) {
+ mg_error(c, "bad key exchange length");
+ return -1;
+ }
+ mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1);
+ mg_tls_hexdump("c x25519 sec", tls->x25519_sec, 32);
+ mg_tls_drop_record(c);
+ /* generate handshake keys */
+ mg_tls_generate_handshake_keys(c);
+ return 0;
+ }
+ mg_error(c, "bad client hello");
+ return -1;
}
-static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
- if (str.len > 0) return false;
- addr->ip = 0;
- addr->is_ip6 = false;
- return true;
+static int mg_tls_client_recv_ext(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (mg_tls_recv_record(c) < 0) {
+ return -1;
+ }
+ if (tls->recv.buf[0] != MG_TLS_ENCRYPTED_EXTENSIONS) {
+ mg_error(c, "expected server extensions but got msg 0x%02x",
+ tls->recv.buf[0]);
+ return -1;
+ }
+ mg_tls_drop_message(c);
+ return 0;
}
-static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
- uint8_t data[4] = {0, 0, 0, 0};
- size_t i, num_dots = 0;
- for (i = 0; i < str.len; i++) {
- if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
- if (octet > 255) return false;
- data[num_dots] = (uint8_t) octet;
- } else if (str.ptr[i] == '.') {
- if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
- num_dots++;
- } else {
- return false;
- }
+static int mg_tls_client_recv_cert(struct mg_connection *c) {
+ uint8_t *cert;
+ uint32_t certsz;
+ struct mg_der_tlv oid, pubkey, seq, subj;
+ int subj_match = 0;
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (mg_tls_recv_record(c) < 0) {
+ return -1;
+ }
+ if (tls->recv.buf[0] != MG_TLS_CERTIFICATE) {
+ mg_error(c, "expected server certificate but got msg 0x%02x",
+ tls->recv.buf[0]);
+ return -1;
+ }
+ if (tls->skip_verification) {
+ mg_tls_drop_message(c);
+ return 0;
}
- if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
- memcpy(&addr->ip, data, sizeof(data));
- addr->is_ip6 = false;
- return true;
-}
-static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
- int i;
- if (str.len < 14) return false;
- if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false;
- for (i = 2; i < 6; i++) {
- if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false;
+ if (tls->recv.len < 11) {
+ mg_error(c, "certificate list too short");
+ return -1;
}
- if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false;
- memset(addr->ip6, 0, sizeof(addr->ip6));
- addr->ip6[10] = addr->ip6[11] = 255;
- memcpy(&addr->ip6[12], &addr->ip, 4);
- addr->is_ip6 = true;
- return true;
-}
-static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
- size_t i, j = 0, n = 0, dc = 42;
- if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2;
- if (mg_v4mapped(str, addr)) return true;
- for (i = 0; i < str.len; i++) {
- if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
- (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
- (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
- unsigned long val;
- if (i > j + 3) return false;
- // MG_DEBUG(("%zu %zu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j]));
- val = mg_unhexn(&str.ptr[j], i - j + 1);
- addr->ip6[n] = (uint8_t) ((val >> 8) & 255);
- addr->ip6[n + 1] = (uint8_t) (val & 255);
- } else if (str.ptr[i] == ':') {
- j = i + 1;
- if (i > 0 && str.ptr[i - 1] == ':') {
- dc = n; // Double colon
- if (i > 1 && str.ptr[i - 2] == ':') return false;
- } else if (i > 0) {
- n += 2;
+ cert = tls->recv.buf + 11;
+ certsz = MG_LOAD_BE24(tls->recv.buf + 8);
+ if (certsz > tls->recv.len - 11) {
+ mg_error(c, "certificate too long: %d vs %d", certsz, tls->recv.len - 11);
+ return -1;
+ }
+
+ do {
+ // secp256r1 public key
+ if (mg_der_find(cert, certsz,
+ (uint8_t *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8,
+ &oid) < 0) {
+ mg_error(c, "certificate secp256r1 public key OID not found");
+ return -1;
+ }
+ if (mg_der_to_tlv(oid.value + oid.len,
+ (size_t) (cert + certsz - (oid.value + oid.len)),
+ &pubkey) < 0) {
+ mg_error(c, "certificate secp256r1 public key not found");
+ return -1;
+ }
+
+ // expect BIT STRING, unpadded, uncompressed: [0]+[4]+32+32 content bytes
+ if (pubkey.type != 3 || pubkey.len != 66 || pubkey.value[0] != 0 ||
+ pubkey.value[1] != 4) {
+ mg_error(c, "unsupported public key bitstring encoding");
+ return -1;
+ }
+ memmove(tls->pubkey, pubkey.value + 2, pubkey.len - 2);
+ } while (0);
+
+ // Subject Alternative Names
+ do {
+ if (mg_der_find(cert, certsz, (uint8_t *) "\x55\x1d\x11", 3, &oid) < 0) {
+ mg_error(c, "certificate does not contain subject alternative names");
+ return -1;
+ }
+ if (mg_der_to_tlv(oid.value + oid.len,
+ (size_t) (cert + certsz - (oid.value + oid.len)),
+ &seq) < 0) {
+ mg_error(c, "certificate subject alternative names not found");
+ return -1;
+ }
+ if (mg_der_to_tlv(seq.value, seq.len, &seq) < 0) {
+ mg_error(
+ c,
+ "certificate subject alternative names is not a constructed object");
+ return -1;
+ }
+ MG_VERBOSE(("verify hostname %s", tls->hostname));
+ while (seq.len > 0) {
+ if (mg_der_to_tlv(seq.value, seq.len, &subj) < 0) {
+ mg_error(c, "bad subject alternative name");
+ return -1;
}
- if (n > 14) return false;
- addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
- } else {
- return false;
+ MG_VERBOSE(("subj=%.*s", subj.len, subj.value));
+ if (mg_match(mg_str((const char *) tls->hostname),
+ mg_str_n((const char *) subj.value, subj.len), NULL)) {
+ subj_match = 1;
+ break;
+ }
+ seq.len = (uint32_t) (seq.value + seq.len - (subj.value + subj.len));
+ seq.value = subj.value + subj.len;
+ }
+ if (!subj_match) {
+ mg_error(c, "certificate did not match the hostname");
+ return -1;
}
+ } while (0);
+
+ mg_tls_drop_message(c);
+ mg_tls_calc_cert_verify_hash(c, tls->sighash);
+ return 0;
+}
+
+static int mg_tls_client_recv_cert_verify(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (mg_tls_recv_record(c) < 0) {
+ return -1;
}
- if (n < 14 && dc == 42) return false;
- if (n < 14) {
- memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
- memset(&addr->ip6[dc], 0, 14 - n);
+ if (tls->recv.buf[0] != MG_TLS_CERTIFICATE_VERIFY) {
+ mg_error(c, "expected server certificate verify but got msg 0x%02x",
+ tls->recv.buf[0]);
+ return -1;
+ }
+ // Ignore CertificateVerify is strict checks are not required
+ if (tls->skip_verification) {
+ mg_tls_drop_message(c);
+ return 0;
}
- addr->is_ip6 = true;
- return true;
-}
-bool mg_aton(struct mg_str str, struct mg_addr *addr) {
- // MG_INFO(("[%.*s]", (int) str.len, str.ptr));
- return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
- mg_aton6(str, addr);
+ // Extract certificate signature and verify it using pubkey and sighash
+ do {
+ uint8_t sig[64];
+ struct mg_der_tlv seq, a, b;
+ if (mg_der_to_tlv(tls->recv.buf + 8, tls->recv.len - 8, &seq) < 0) {
+ mg_error(c, "verification message is not an ASN.1 DER sequence");
+ return -1;
+ }
+ if (mg_der_to_tlv(seq.value, seq.len, &a) < 0) {
+ mg_error(c, "missing first part of the signature");
+ return -1;
+ }
+ if (mg_der_to_tlv(a.value + a.len, seq.len - a.len, &b) < 0) {
+ mg_error(c, "missing second part of the signature");
+ return -1;
+ }
+ // Integers may be padded with zeroes
+ if (a.len > 32) {
+ a.value = a.value + (a.len - 32);
+ a.len = 32;
+ }
+ if (b.len > 32) {
+ b.value = b.value + (b.len - 32);
+ b.len = 32;
+ }
+
+ memmove(sig, a.value, a.len);
+ memmove(sig + 32, b.value, b.len);
+
+ if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig,
+ mg_uecc_secp256r1()) != 1) {
+ mg_error(c, "failed to verify certificate");
+ return -1;
+ }
+ } while (0);
+
+ mg_tls_drop_message(c);
+ return 0;
}
-struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
- struct mg_connection *c =
- (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize);
- if (c != NULL) {
- c->mgr = mgr;
- c->send.align = c->recv.align = MG_IO_SIZE;
- c->id = ++mgr->nextid;
+static int mg_tls_client_recv_finish(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (mg_tls_recv_record(c) < 0) {
+ return -1;
}
- return c;
+ if (tls->recv.buf[0] != MG_TLS_FINISHED) {
+ mg_error(c, "expected server finished but got msg 0x%02x",
+ tls->recv.buf[0]);
+ return -1;
+ }
+ mg_tls_drop_message(c);
+ return 0;
}
-void mg_close_conn(struct mg_connection *c) {
- mg_resolve_cancel(c); // Close any pending DNS query
- LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
- if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
- if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
- // Order of operations is important. `MG_EV_CLOSE` event must be fired
- // before we deallocate received data, see #1331
- mg_call(c, MG_EV_CLOSE, NULL);
- MG_DEBUG(("%lu %p closed", c->id, c->fd));
-
- mg_tls_free(c);
- mg_iobuf_free(&c->recv);
- mg_iobuf_free(&c->send);
- memset(c, 0, sizeof(*c));
- free(c);
+static void mg_tls_client_send_finish(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ struct mg_iobuf *wio = &tls->send;
+ mg_sha256_ctx sha256;
+ uint8_t hash[32];
+ uint8_t finish[36] = {0x14, 0, 0, 32};
+ memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
+ mg_sha256_final(hash, &sha256);
+ mg_hmac_sha256(finish + 4, tls->client_finished_key, 32, hash, 32);
+ mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
+ mg_io_send(c, wio->buf, wio->len);
+ wio->len = 0;
+}
+
+static void mg_tls_client_handshake(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ switch (tls->state) {
+ case MG_TLS_STATE_CLIENT_START:
+ mg_tls_client_send_hello(c);
+ tls->state = MG_TLS_STATE_CLIENT_WAIT_SH;
+ // Fallthrough
+ case MG_TLS_STATE_CLIENT_WAIT_SH:
+ if (mg_tls_client_recv_hello(c) < 0) {
+ break;
+ }
+ tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
+ // Fallthrough
+ case MG_TLS_STATE_CLIENT_WAIT_EE:
+ if (mg_tls_client_recv_ext(c) < 0) {
+ break;
+ }
+ tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
+ // Fallthrough
+ case MG_TLS_STATE_CLIENT_WAIT_CERT:
+ if (mg_tls_client_recv_cert(c) < 0) {
+ break;
+ }
+ tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
+ // Fallthrough
+ case MG_TLS_STATE_CLIENT_WAIT_CV:
+ if (mg_tls_client_recv_cert_verify(c) < 0) {
+ break;
+ }
+ tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISHED;
+ // Fallthrough
+ case MG_TLS_STATE_CLIENT_WAIT_FINISHED:
+ if (mg_tls_client_recv_finish(c) < 0) {
+ break;
+ }
+ mg_tls_client_send_finish(c);
+ mg_tls_generate_application_keys(c);
+ tls->state = MG_TLS_STATE_CLIENT_CONNECTED;
+ c->is_tls_hs = 0;
+ break;
+ default: mg_error(c, "unexpected client state: %d", tls->state); break;
+ }
}
-struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = NULL;
- if (url == NULL || url[0] == '\0') {
- MG_ERROR(("null url"));
- } else if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("OOM"));
- } else {
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->is_udp = (strncmp(url, "udp:", 4) == 0);
- c->fd = (void *) (size_t) MG_INVALID_SOCKET;
- c->fn = fn;
- c->is_client = true;
- c->fn_data = fn_data;
- MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
- mg_call(c, MG_EV_OPEN, NULL);
- mg_resolve(c, url);
+static void mg_tls_server_handshake(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ switch (tls->state) {
+ case MG_TLS_STATE_SERVER_START:
+ if (mg_tls_server_recv_hello(c) < 0) {
+ return;
+ }
+ mg_tls_server_send_hello(c);
+ mg_tls_generate_handshake_keys(c);
+ mg_tls_server_send_ext(c);
+ mg_tls_server_send_cert(c);
+ mg_tls_server_send_cert_verify(c);
+ mg_tls_server_send_finish(c);
+ tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
+ // fallthrough
+ case MG_TLS_STATE_SERVER_NEGOTIATED:
+ if (mg_tls_server_recv_finish(c) < 0) {
+ return;
+ }
+ mg_tls_generate_application_keys(c);
+ tls->state = MG_TLS_STATE_SERVER_CONNECTED;
+ c->is_tls_hs = 0;
+ return;
+ default: mg_error(c, "unexpected server state: %d", tls->state); break;
}
- return c;
}
-struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = NULL;
- if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("OOM %s", url));
- } else if (!mg_open_listener(c, url)) {
- MG_ERROR(("Failed: %s, errno %d", url, errno));
- free(c);
- c = NULL;
+void mg_tls_handshake(struct mg_connection *c) {
+ if (c->is_client) {
+ mg_tls_client_handshake(c);
} else {
- c->is_listening = 1;
- c->is_udp = strncmp(url, "udp:", 4) == 0;
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->fn = fn;
- c->fn_data = fn_data;
- mg_call(c, MG_EV_OPEN, NULL);
- MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
+ mg_tls_server_handshake(c);
}
- return c;
}
-struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_alloc_conn(mgr);
- if (c != NULL) {
- c->fd = (void *) (size_t) fd;
- c->fn = fn;
- c->fn_data = fn_data;
- MG_EPOLL_ADD(c);
- mg_call(c, MG_EV_OPEN, NULL);
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
+static int mg_parse_pem(const struct mg_str pem, const struct mg_str label,
+ struct mg_str *der) {
+ size_t n = 0, m = 0;
+ char *s;
+ const char *c;
+ struct mg_str caps[5];
+ if (!mg_match(pem, mg_str("#-----BEGIN #-----#-----END #-----#"), caps)) {
+ der->buf = mg_mprintf("%.*s", pem.len, pem.buf);
+ der->len = pem.len;
+ return 0;
+ }
+ if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {
+ return -1; // bad label
+ }
+ if ((s = (char *) calloc(1, caps[2].len)) == NULL) {
+ return -1;
}
- return c;
-}
-struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
- unsigned flags, void (*fn)(void *), void *arg) {
- struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
- if (t != NULL) {
- mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
- t->id = mgr->timerid++;
+ for (c = caps[2].buf; c < caps[2].buf + caps[2].len; c++) {
+ if (*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t') {
+ continue;
+ }
+ s[n++] = *c;
}
- return t;
+ m = mg_base64_decode(s, n, s, n);
+ if (m == 0) {
+ free(s);
+ return -1;
+ }
+ der->buf = s;
+ der->len = m;
+ return 0;
}
-void mg_mgr_free(struct mg_mgr *mgr) {
- struct mg_connection *c;
- struct mg_timer *tmp, *t = mgr->timers;
- while (t != NULL) tmp = t->next, free(t), t = tmp;
- mgr->timers = NULL; // Important. Next call to poll won't touch timers
- for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
- mg_mgr_poll(mgr, 0);
-#if MG_ENABLE_FREERTOS_TCP
- FreeRTOS_DeleteSocketSet(mgr->ss);
-#endif
- MG_DEBUG(("All connections closed"));
-#if MG_ENABLE_EPOLL
- if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1;
-#endif
-}
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ struct mg_str key;
+ struct tls_data *tls = (struct tls_data *) calloc(1, sizeof(struct tls_data));
+ if (tls == NULL) {
+ mg_error(c, "tls oom");
+ return;
+ }
-void mg_mgr_init(struct mg_mgr *mgr) {
- memset(mgr, 0, sizeof(*mgr));
-#if MG_ENABLE_EPOLL
- if ((mgr->epoll_fd = epoll_create1(0)) < 0) MG_ERROR(("epoll: %d", errno));
-#else
- mgr->epoll_fd = -1;
-#endif
-#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- // clang-format off
- { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
- // clang-format on
-#elif MG_ENABLE_FREERTOS_TCP
- mgr->ss = FreeRTOS_CreateSocketSet();
-#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
- // Ignore SIGPIPE signal, so if client cancels the request, it
- // won't kill the whole process.
- signal(SIGPIPE, SIG_IGN);
-#endif
- mgr->dnstimeout = 3000;
- mgr->dns4.url = "udp://8.8.8.8:53";
- mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
-}
+ tls->state =
+ c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
-#ifdef MG_ENABLE_LINES
-#line 1 "src/rpc.c"
-#endif
+ tls->skip_verification = opts->skip_verification;
+ tls->send.align = MG_IO_SIZE;
+ c->tls = tls;
+ c->is_tls = c->is_tls_hs = 1;
+ mg_sha256_init(&tls->sha256);
-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 = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data;
- rpc->next = *head, *head = rpc;
+ // save hostname (client extension)
+ if (opts->name.len > 0) {
+ if (opts->name.len >= sizeof(tls->hostname) - 1) {
+ mg_error(c, "hostname too long");
+ }
+ strncpy((char *) tls->hostname, opts->name.buf, sizeof(tls->hostname) - 1);
+ tls->hostname[opts->name.len] = 0;
}
-}
-void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) {
- struct mg_rpc *r;
- while ((r = *head) != NULL) {
- if (r->fn == fn || fn == NULL) {
- *head = r->next;
- free((void *) r->method.ptr);
- free(r);
- } else {
- head = &(*head)->next;
- }
+ if (c->is_client) {
+ tls->server_cert_der.buf = NULL;
+ return;
}
-}
-static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) {
- struct mg_rpc *h = r->head == NULL ? NULL : *r->head;
- while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next;
- if (h != NULL) {
- r->rpc = h;
- h->fn(r);
+ // parse PEM or DER certificate
+ if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->server_cert_der) <
+ 0) {
+ MG_ERROR(("Failed to load certificate"));
+ return;
+ }
+
+ // parse PEM or DER EC key
+ if (opts->key.buf == NULL) {
+ mg_error(c, "certificate provided without a private key");
+ return;
+ }
+
+ if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) {
+ if (key.len < 39) {
+ MG_ERROR(("EC private key too short"));
+ return;
+ }
+ // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...]
+ // 30 nn 02 01 01 04 20 [key] ...
+ if (key.buf[0] != 0x30 || (key.buf[1] & 0x80) != 0) {
+ MG_ERROR(("EC private key: ASN.1 bad sequence"));
+ return;
+ }
+ if (memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) {
+ MG_ERROR(("EC private key: ASN.1 bad data"));
+ }
+ memmove(tls->server_key, key.buf + 7, 32);
+ free((void *) key.buf);
+ } else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) {
+ mg_error(c, "PKCS8 private key format is not supported");
} else {
- mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.ptr);
+ mg_error(c, "expected EC PRIVATE KEY or PRIVATE KEY");
}
}
-void mg_rpc_process(struct mg_rpc_req *r) {
- int len, off = mg_json_get(r->frame, "$.method", &len);
- if (off > 0 && r->frame.ptr[off] == '"') {
- struct mg_str method = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2);
- mg_rpc_call(r, method);
- } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 ||
- (off = mg_json_get(r->frame, "$.error", &len)) > 0) {
- mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler
- } else {
- mg_rpc_err(r, -32700, "%.*Q", (int) r->frame.len, r->frame.ptr); // Invalid
+void mg_tls_free(struct mg_connection *c) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ if (tls != NULL) {
+ mg_iobuf_free(&tls->send);
+ free((void *) tls->server_cert_der.buf);
}
+ free(c->tls);
+ c->tls = NULL;
}
-void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
- int len, off = mg_json_get(r->frame, "$.id", &len);
- if (off > 0) {
- mg_xprintf(r->pfn, r->pfn_data, "{%Q:%.*s,%Q:", "id", len,
- &r->frame.ptr[off], "result");
- mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
- mg_xprintf(r->pfn, r->pfn_data, "}");
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ long n = MG_IO_WAIT;
+ if (len > MG_IO_SIZE) len = MG_IO_SIZE;
+ mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA);
+ while (tls->send.len > 0 &&
+ (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
+ mg_iobuf_del(&tls->send, 0, (size_t) n);
}
+ if (n == MG_IO_ERR || n == MG_IO_WAIT) return n;
+ return (long) len;
}
-void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_rpc_vok(r, fmt, &ap);
- va_end(ap);
-}
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ int r = 0;
+ struct tls_data *tls = (struct tls_data *) c->tls;
+ size_t minlen;
-void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
- int len, off = mg_json_get(r->frame, "$.id", &len);
- mg_xprintf(r->pfn, r->pfn_data, "{");
- if (off > 0) {
- mg_xprintf(r->pfn, r->pfn_data, "%Q:%.*s,", "id", len, &r->frame.ptr[off]);
+ r = mg_tls_recv_record(c);
+ if (r < 0) {
+ return r;
}
- mg_xprintf(r->pfn, r->pfn_data, "%Q:{%Q:%d,%Q:", "error", "code", code,
- "message");
- mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
- mg_xprintf(r->pfn, r->pfn_data, "}}");
+ if (tls->content_type != MG_TLS_APP_DATA) {
+ tls->recv.len = 0;
+ mg_tls_drop_record(c);
+ return MG_IO_WAIT;
+ }
+ minlen = len < tls->recv.len ? len : tls->recv.len;
+ memmove(buf, tls->recv.buf, minlen);
+ tls->recv.buf += minlen;
+ tls->recv.len -= minlen;
+ if (tls->recv.len == 0) {
+ mg_tls_drop_record(c);
+ }
+ return (long) minlen;
}
-void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_rpc_verr(r, code, fmt, &ap);
- va_end(ap);
+size_t mg_tls_pending(struct mg_connection *c) {
+ return mg_tls_got_record(c) ? 1 : 0;
}
-static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
- struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **);
- size_t len = 0;
- for (h = *head; h != NULL; h = h->next) {
- if (h->method.len == 0) continue; // Ignore response handler
- len += mg_xprintf(pfn, pfn_data, "%s%.*Q", h == *head ? "" : ",",
- (int) h->method.len, h->method.ptr);
- }
- return len;
+void mg_tls_ctx_init(struct mg_mgr *mgr) {
+ (void) mgr;
}
-void mg_rpc_list(struct mg_rpc_req *r) {
- mg_rpc_ok(r, "[%M]", print_methods, r->head);
+void mg_tls_ctx_free(struct mg_mgr *mgr) {
+ (void) mgr;
}
+#endif
#ifdef MG_ENABLE_LINES
-#line 1 "src/sha1.c"
+#line 1 "src/tls_dummy.c"
+#endif
+
+
+#if MG_TLS == MG_TLS_NONE
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ (void) opts;
+ mg_error(c, "TLS is not enabled");
+}
+void mg_tls_handshake(struct mg_connection *c) {
+ (void) c;
+}
+void mg_tls_free(struct mg_connection *c) {
+ (void) c;
+}
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ return c == NULL || buf == NULL || len == 0 ? 0 : -1;
+}
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ return c == NULL || buf == NULL || len == 0 ? 0 : -1;
+}
+size_t mg_tls_pending(struct mg_connection *c) {
+ (void) c;
+ return 0;
+}
+void mg_tls_ctx_init(struct mg_mgr *mgr) {
+ (void) mgr;
+}
+void mg_tls_ctx_free(struct mg_mgr *mgr) {
+ (void) mgr;
+}
#endif
-/* Copyright(c) By Steve Reid <steve@edmweb.com> */
-/* 100% Public Domain */
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_mbed.c"
+#endif
-union char64long16 {
- unsigned char c[64];
- uint32_t l[16];
-};
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#if MG_TLS == MG_TLS_MBED
-static uint32_t blk0(union char64long16 *block, int i) {
- if (MG_BIG_ENDIAN) {
- } else {
- block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
- (rol(block->l[i], 8) & 0x00FF00FF);
+#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
+#define MG_MBEDTLS_RNG_GET , mg_mbed_rng, NULL
+#else
+#define MG_MBEDTLS_RNG_GET
+#endif
+
+static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) {
+ mg_random(buf, len);
+ (void) ctx;
+ return 0;
+}
+
+static bool mg_load_cert(struct mg_str str, mbedtls_x509_crt *p) {
+ int rc;
+ if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
+ if (str.buf[0] == '-') str.len++; // PEM, include trailing NUL
+ if ((rc = mbedtls_x509_crt_parse(p, (uint8_t *) str.buf, str.len)) != 0) {
+ MG_ERROR(("cert err %#x", -rc));
+ return false;
}
- return block->l[i];
+ return true;
}
-/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
-#undef blk
-#undef R0
-#undef R1
-#undef R2
-#undef R3
-#undef R4
+static bool mg_load_key(struct mg_str str, mbedtls_pk_context *p) {
+ int rc;
+ if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
+ if (str.buf[0] == '-') str.len++; // PEM, include trailing NUL
+ if ((rc = mbedtls_pk_parse_key(p, (uint8_t *) str.buf, str.len, NULL,
+ 0 MG_MBEDTLS_RNG_GET)) != 0) {
+ MG_ERROR(("key err %#x", -rc));
+ return false;
+ }
+ return true;
+}
-#define blk(i) \
- (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
- block->l[(i + 2) & 15] ^ block->l[i & 15], \
- 1))
-#define R0(v, w, x, y, z, i) \
- z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
- w = rol(w, 30);
-#define R1(v, w, x, y, z, i) \
- z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
- w = rol(w, 30);
-#define R2(v, w, x, y, z, i) \
- z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
- w = rol(w, 30);
-#define R3(v, w, x, y, z, i) \
- z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
- w = rol(w, 30);
-#define R4(v, w, x, y, z, i) \
- z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
- w = rol(w, 30);
+void mg_tls_free(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ if (tls != NULL) {
+ mbedtls_ssl_free(&tls->ssl);
+ mbedtls_pk_free(&tls->pk);
+ mbedtls_x509_crt_free(&tls->ca);
+ mbedtls_x509_crt_free(&tls->cert);
+ mbedtls_ssl_config_free(&tls->conf);
+#ifdef MBEDTLS_SSL_SESSION_TICKETS
+ mbedtls_ssl_ticket_free(&tls->ticket);
+#endif
+ free(tls);
+ c->tls = NULL;
+ }
+}
-static void mg_sha1_transform(uint32_t state[5],
- const unsigned char buffer[64]) {
- uint32_t a, b, c, d, e;
- union char64long16 block[1];
+static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
+ long n = mg_io_send((struct mg_connection *) ctx, buf, len);
+ MG_VERBOSE(("%lu n=%ld e=%d", ((struct mg_connection *) ctx)->id, n, errno));
+ if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
+ if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
+ if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
+ return (int) n;
+}
- memcpy(block, buffer, 64);
- a = state[0];
- b = state[1];
- c = state[2];
- d = state[3];
- e = state[4];
- R0(a, b, c, d, e, 0);
- R0(e, a, b, c, d, 1);
- R0(d, e, a, b, c, 2);
- R0(c, d, e, a, b, 3);
- R0(b, c, d, e, a, 4);
- R0(a, b, c, d, e, 5);
- R0(e, a, b, c, d, 6);
- R0(d, e, a, b, c, 7);
- R0(c, d, e, a, b, 8);
- R0(b, c, d, e, a, 9);
- R0(a, b, c, d, e, 10);
- R0(e, a, b, c, d, 11);
- R0(d, e, a, b, c, 12);
- R0(c, d, e, a, b, 13);
- R0(b, c, d, e, a, 14);
- R0(a, b, c, d, e, 15);
- R1(e, a, b, c, d, 16);
- R1(d, e, a, b, c, 17);
- R1(c, d, e, a, b, 18);
- R1(b, c, d, e, a, 19);
- R2(a, b, c, d, e, 20);
- R2(e, a, b, c, d, 21);
- R2(d, e, a, b, c, 22);
- R2(c, d, e, a, b, 23);
- R2(b, c, d, e, a, 24);
- R2(a, b, c, d, e, 25);
- R2(e, a, b, c, d, 26);
- R2(d, e, a, b, c, 27);
- R2(c, d, e, a, b, 28);
- R2(b, c, d, e, a, 29);
- R2(a, b, c, d, e, 30);
- R2(e, a, b, c, d, 31);
- R2(d, e, a, b, c, 32);
- R2(c, d, e, a, b, 33);
- R2(b, c, d, e, a, 34);
- R2(a, b, c, d, e, 35);
- R2(e, a, b, c, d, 36);
- R2(d, e, a, b, c, 37);
- R2(c, d, e, a, b, 38);
- R2(b, c, d, e, a, 39);
- R3(a, b, c, d, e, 40);
- R3(e, a, b, c, d, 41);
- R3(d, e, a, b, c, 42);
- R3(c, d, e, a, b, 43);
- R3(b, c, d, e, a, 44);
- R3(a, b, c, d, e, 45);
- R3(e, a, b, c, d, 46);
- R3(d, e, a, b, c, 47);
- R3(c, d, e, a, b, 48);
- R3(b, c, d, e, a, 49);
- R3(a, b, c, d, e, 50);
- R3(e, a, b, c, d, 51);
- R3(d, e, a, b, c, 52);
- R3(c, d, e, a, b, 53);
- R3(b, c, d, e, a, 54);
- R3(a, b, c, d, e, 55);
- R3(e, a, b, c, d, 56);
- R3(d, e, a, b, c, 57);
- R3(c, d, e, a, b, 58);
- R3(b, c, d, e, a, 59);
- R4(a, b, c, d, e, 60);
- R4(e, a, b, c, d, 61);
- R4(d, e, a, b, c, 62);
- R4(c, d, e, a, b, 63);
- R4(b, c, d, e, a, 64);
- R4(a, b, c, d, e, 65);
- R4(e, a, b, c, d, 66);
- R4(d, e, a, b, c, 67);
- R4(c, d, e, a, b, 68);
- R4(b, c, d, e, a, 69);
- R4(a, b, c, d, e, 70);
- R4(e, a, b, c, d, 71);
- R4(d, e, a, b, c, 72);
- R4(c, d, e, a, b, 73);
- R4(b, c, d, e, a, 74);
- R4(a, b, c, d, e, 75);
- R4(e, a, b, c, d, 76);
- R4(d, e, a, b, c, 77);
- R4(c, d, e, a, b, 78);
- R4(b, c, d, e, a, 79);
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- state[4] += e;
- /* Erase working structures. The order of operations is important,
- * used to ensure that compiler doesn't optimize those out. */
- memset(block, 0, sizeof(block));
- a = b = c = d = e = 0;
- (void) a;
- (void) b;
- (void) c;
- (void) d;
- (void) e;
+static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
+ long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
+ MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
+ if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
+ if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
+ if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
+ return (int) n;
+}
+
+void mg_tls_handshake(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int rc = mbedtls_ssl_handshake(&tls->ssl);
+ if (rc == 0) { // Success
+ MG_DEBUG(("%lu success", c->id));
+ c->is_tls_hs = 0;
+ mg_call(c, MG_EV_TLS_HS, NULL);
+ } else if (rc == MBEDTLS_ERR_SSL_WANT_READ ||
+ rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending
+ MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting,
+ c->is_tls_hs, rc, -rc));
+ } else {
+ mg_error(c, "TLS handshake: -%#x", -rc); // Error
+ }
}
-void mg_sha1_init(mg_sha1_ctx *context) {
- context->state[0] = 0x67452301;
- context->state[1] = 0xEFCDAB89;
- context->state[2] = 0x98BADCFE;
- context->state[3] = 0x10325476;
- context->state[4] = 0xC3D2E1F0;
- context->count[0] = context->count[1] = 0;
+static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
+ n = (int) strlen(s2) - 1;
+ MG_INFO(("%lu %d %.*s", ((struct mg_connection *) c)->id, lev, n, s2));
+ (void) s;
}
-void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data,
- size_t len) {
- size_t i, j;
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
+ int rc = 0;
+ c->tls = tls;
+ if (c->tls == NULL) {
+ mg_error(c, "TLS OOM");
+ goto fail;
+ }
+ if (c->is_listening) goto fail;
+ MG_DEBUG(("%lu Setting TLS", c->id));
+ MG_PROF_ADD(c, "mbedtls_init_start");
+#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
+ defined(MBEDTLS_PSA_CRYPTO_C)
+ psa_crypto_init(); // https://github.com/Mbed-TLS/mbedtls/issues/9072#issuecomment-2084845711
+#endif
+ mbedtls_ssl_init(&tls->ssl);
+ mbedtls_ssl_config_init(&tls->conf);
+ mbedtls_x509_crt_init(&tls->ca);
+ mbedtls_x509_crt_init(&tls->cert);
+ mbedtls_pk_init(&tls->pk);
+ mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
+#if defined(MG_MBEDTLS_DEBUG_LEVEL)
+ mbedtls_debug_set_threshold(MG_MBEDTLS_DEBUG_LEVEL);
+#endif
+ if ((rc = mbedtls_ssl_config_defaults(
+ &tls->conf,
+ c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
+ mg_error(c, "tls defaults %#x", -rc);
+ goto fail;
+ }
+ mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
- j = context->count[0];
- if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++;
- context->count[1] += (uint32_t) (len >> 29);
- j = (j >> 3) & 63;
- if ((j + len) > 63) {
- memcpy(&context->buffer[j], data, (i = 64 - j));
- mg_sha1_transform(context->state, context->buffer);
- for (; i + 63 < len; i += 64) {
- mg_sha1_transform(context->state, &data[i]);
+ if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
+ // NOTE: MBEDTLS_SSL_VERIFY_NONE is not supported for TLS1.3 on client side
+ // See https://github.com/Mbed-TLS/mbedtls/issues/7075
+ mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
+ } else {
+ if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
+ mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
+ if (c->is_client && opts->name.buf != NULL && opts->name.buf[0] != '\0') {
+ char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf);
+ mbedtls_ssl_set_hostname(&tls->ssl, host);
+ MG_DEBUG(("%lu hostname verification: %s", c->id, host));
+ free(host);
}
- j = 0;
- } else
- i = 0;
- memcpy(&context->buffer[j], &data[i], len - i);
+ mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
+ }
+ if (!mg_load_cert(opts->cert, &tls->cert)) goto fail;
+ if (!mg_load_key(opts->key, &tls->pk)) goto fail;
+ if (tls->cert.version &&
+ (rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk)) != 0) {
+ mg_error(c, "own cert %#x", -rc);
+ goto fail;
+ }
+
+#ifdef MBEDTLS_SSL_SESSION_TICKETS
+ mbedtls_ssl_conf_session_tickets_cb(
+ &tls->conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse,
+ &((struct mg_tls_ctx *) c->mgr->tls_ctx)->tickets);
+#endif
+
+ if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
+ mg_error(c, "setup err %#x", -rc);
+ goto fail;
+ }
+ c->is_tls = 1;
+ c->is_tls_hs = 1;
+ mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
+ MG_PROF_ADD(c, "mbedtls_init_end");
+ if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
+ mg_tls_handshake(c);
+ }
+ return;
+fail:
+ mg_tls_free(c);
}
-void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) {
- unsigned i;
- unsigned char finalcount[8], c;
+size_t mg_tls_pending(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl);
+}
- for (i = 0; i < 8; i++) {
- finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >>
- ((3 - (i & 3)) * 8)) &
- 255);
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
+ if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
+ return MG_IO_WAIT;
+#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
+ if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) {
+ return MG_IO_WAIT;
}
- c = 0200;
- mg_sha1_update(context, &c, 1);
- while ((context->count[0] & 504) != 448) {
- c = 0000;
- mg_sha1_update(context, &c, 1);
+#endif
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
+ if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
+ return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+
+void mg_tls_ctx_init(struct mg_mgr *mgr) {
+ struct mg_tls_ctx *ctx = (struct mg_tls_ctx *) calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ MG_ERROR(("TLS context init OOM"));
+ } else {
+#ifdef MBEDTLS_SSL_SESSION_TICKETS
+ int rc;
+ mbedtls_ssl_ticket_init(&ctx->tickets);
+ if ((rc = mbedtls_ssl_ticket_setup(&ctx->tickets, mg_mbed_rng, NULL,
+ MBEDTLS_CIPHER_AES_128_GCM, 86400)) !=
+ 0) {
+ MG_ERROR((" mbedtls_ssl_ticket_setup %#x", -rc));
+ }
+#endif
+ mgr->tls_ctx = ctx;
}
- mg_sha1_update(context, finalcount, 8);
- for (i = 0; i < 20; i++) {
- digest[i] =
- (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+}
+
+void mg_tls_ctx_free(struct mg_mgr *mgr) {
+ struct mg_tls_ctx *ctx = (struct mg_tls_ctx *) mgr->tls_ctx;
+ if (ctx != NULL) {
+#ifdef MBEDTLS_SSL_SESSION_TICKETS
+ mbedtls_ssl_ticket_free(&ctx->tickets);
+#endif
+ free(ctx);
+ mgr->tls_ctx = NULL;
}
- memset(context, '\0', sizeof(*context));
- memset(&finalcount, '\0', sizeof(finalcount));
}
+#endif
#ifdef MG_ENABLE_LINES
-#line 1 "src/sntp.c"
+#line 1 "src/tls_openssl.c"
#endif
+#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL
+static int tls_err_cb(const char *s, size_t len, void *c) {
+ int n = (int) len - 1;
+ MG_ERROR(("%lu %.*s", ((struct mg_connection *) c)->id, n, s));
+ return 0; // undocumented
+}
+static int mg_tls_err(struct mg_connection *c, struct mg_tls *tls, int res) {
+ int err = SSL_get_error(tls->ssl, res);
+ // We've just fetched the last error from the queue.
+ // Now we need to clear the error queue. If we do not, then the following
+ // can happen (actually reported):
+ // - A new connection is accept()-ed with cert error (e.g. self-signed cert)
+ // - Since all accept()-ed connections share listener's context,
+ // - *ALL* SSL accepted connection report read error on the next poll cycle.
+ // Thus a single errored connection can close all the rest, unrelated ones.
+ // Clearing the error keeps the shared SSL_CTX in an OK state.
-#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds
-#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1
+ if (err != 0) ERR_print_errors_cb(tls_err_cb, c);
+ ERR_clear_error();
+ if (err == SSL_ERROR_WANT_READ) return 0;
+ if (err == SSL_ERROR_WANT_WRITE) return 0;
+ return err;
+}
-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;
- return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0);
+static STACK_OF(X509_INFO) * load_ca_certs(struct mg_str ca) {
+ BIO *bio = BIO_new_mem_buf(ca.buf, (int) ca.len);
+ STACK_OF(X509_INFO) *certs =
+ bio ? PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL) : NULL;
+ if (bio) BIO_free(bio);
+ return certs;
}
-int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
- int64_t res = -1;
- int mode = len > 0 ? buf[0] & 7 : 0;
- int version = len > 0 ? (buf[0] >> 3) & 7 : 0;
- if (len < 48) {
- MG_ERROR(("%s", "corrupt packet"));
- } else if (mode != 4 && mode != 5) {
- MG_ERROR(("%s", "not a server reply"));
- } else if (buf[1] == 0) {
- 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;
- } else {
- MG_ERROR(("unexpected version: %d", version));
+static bool add_ca_certs(SSL_CTX *ctx, STACK_OF(X509_INFO) * certs) {
+ X509_STORE *cert_store = SSL_CTX_get_cert_store(ctx);
+ for (int i = 0; i < sk_X509_INFO_num(certs); i++) {
+ X509_INFO *cert_info = sk_X509_INFO_value(certs, i);
+ if (cert_info->x509 && !X509_STORE_add_cert(cert_store, cert_info->x509))
+ return false;
+ }
+ return true;
+}
+
+static EVP_PKEY *load_key(struct mg_str s) {
+ BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
+ EVP_PKEY *key = bio ? PEM_read_bio_PrivateKey(bio, NULL, 0, NULL) : NULL;
+ if (bio) BIO_free(bio);
+ return key;
+}
+
+static X509 *load_cert(struct mg_str s) {
+ BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
+ X509 *cert = bio == NULL ? NULL
+ : s.buf[0] == '-'
+ ? PEM_read_bio_X509(bio, NULL, NULL, NULL) // PEM
+ : d2i_X509_bio(bio, NULL); // DER
+ if (bio) BIO_free(bio);
+ return cert;
+}
+
+static long mg_bio_ctrl(BIO *b, int cmd, long larg, void *pargs) {
+ long ret = 0;
+ if (cmd == BIO_CTRL_PUSH) ret = 1;
+ if (cmd == BIO_CTRL_POP) ret = 1;
+ if (cmd == BIO_CTRL_FLUSH) ret = 1;
+#if MG_TLS == MG_TLS_OPENSSL
+ if (cmd == BIO_C_SET_NBIO) ret = 1;
+#endif
+ // MG_DEBUG(("%d -> %ld", cmd, ret));
+ (void) b, (void) cmd, (void) larg, (void) pargs;
+ return ret;
+}
+
+static int mg_bio_read(BIO *bio, char *buf, int len) {
+ struct mg_connection *c = (struct mg_connection *) BIO_get_data(bio);
+ long res = mg_io_recv(c, buf, (size_t) len);
+ // MG_DEBUG(("%p %d %ld", buf, len, res));
+ len = res > 0 ? (int) res : -1;
+ if (res == MG_IO_WAIT) BIO_set_retry_read(bio);
+ return len;
+}
+
+static int mg_bio_write(BIO *bio, const char *buf, int len) {
+ struct mg_connection *c = (struct mg_connection *) BIO_get_data(bio);
+ long res = mg_io_send(c, buf, (size_t) len);
+ // MG_DEBUG(("%p %d %ld", buf, len, res));
+ len = res > 0 ? (int) res : -1;
+ if (res == MG_IO_WAIT) BIO_set_retry_write(bio);
+ return len;
+}
+
+void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
+ struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
+ const char *id = "mongoose";
+ static unsigned char s_initialised = 0;
+ BIO *bio = NULL;
+ int rc;
+
+ if (tls == NULL) {
+ mg_error(c, "TLS OOM");
+ goto fail;
+ }
+
+ if (!s_initialised) {
+ SSL_library_init();
+ s_initialised++;
+ }
+ MG_DEBUG(("%lu Setting TLS", c->id));
+ tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method())
+ : SSL_CTX_new(SSLv23_server_method());
+ if ((tls->ssl = SSL_new(tls->ctx)) == NULL) {
+ mg_error(c, "SSL_new");
+ goto fail;
+ }
+ SSL_set_session_id_context(tls->ssl, (const uint8_t *) id,
+ (unsigned) strlen(id));
+ // Disable deprecated protocols
+ SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2);
+ SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3);
+ SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1);
+ SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1);
+#ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION
+ SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION);
+#endif
+#ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE
+ SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
+#endif
+
+#if MG_TLS == MG_TLS_WOLFSSL && !defined(OPENSSL_COMPATIBLE_DEFAULTS)
+ if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
+ // Older versions require that either the CA is loaded or SSL_VERIFY_NONE
+ // explicitly set
+ SSL_set_verify(tls->ssl, SSL_VERIFY_NONE, NULL);
+ }
+#endif
+ if (opts->ca.buf != NULL && opts->ca.buf[0] != '\0') {
+ SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ NULL);
+ STACK_OF(X509_INFO) *certs = load_ca_certs(opts->ca);
+ rc = add_ca_certs(tls->ctx, certs);
+ sk_X509_INFO_pop_free(certs, X509_INFO_free);
+ if (!rc) {
+ mg_error(c, "CA err");
+ goto fail;
+ }
+ }
+ if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') {
+ X509 *cert = load_cert(opts->cert);
+ rc = cert == NULL ? 0 : SSL_use_certificate(tls->ssl, cert);
+ X509_free(cert);
+ if (cert == NULL || rc != 1) {
+ mg_error(c, "CERT err %d", mg_tls_err(c, tls, rc));
+ goto fail;
+ }
+ }
+ if (opts->key.buf != NULL && opts->key.buf[0] != '\0') {
+ EVP_PKEY *key = load_key(opts->key);
+ rc = key == NULL ? 0 : SSL_use_PrivateKey(tls->ssl, key);
+ EVP_PKEY_free(key);
+ if (key == NULL || rc != 1) {
+ mg_error(c, "KEY err %d", mg_tls_err(c, tls, rc));
+ goto fail;
+ }
+ }
+
+ SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+#if MG_TLS == MG_TLS_OPENSSL && OPENSSL_VERSION_NUMBER > 0x10002000L
+ (void) SSL_set_ecdh_auto(tls->ssl, 1);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (opts->name.len > 0) {
+ char *s = mg_mprintf("%.*s", (int) opts->name.len, opts->name.buf);
+#if MG_TLS != MG_TLS_WOLFSSL || LIBWOLFSSL_VERSION_HEX >= 0x05005002
+ SSL_set1_host(tls->ssl, s);
+#else
+ X509_VERIFY_PARAM_set1_host(SSL_get0_param(tls->ssl), s, 0);
+#endif
+ SSL_set_tlsext_host_name(tls->ssl, s);
+ free(s);
}
- return res;
-}
+#endif
+#if MG_TLS == MG_TLS_WOLFSSL
+ tls->bm = BIO_meth_new(0, "bio_mg");
+#else
+ tls->bm = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "bio_mg");
+#endif
+ BIO_meth_set_write(tls->bm, mg_bio_write);
+ BIO_meth_set_read(tls->bm, mg_bio_read);
+ BIO_meth_set_ctrl(tls->bm, mg_bio_ctrl);
-static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
- if (ev == MG_EV_READ) {
- int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
- 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_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
- (unsigned) (milliseconds % 1000)));
- }
- mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer
- } else if (ev == MG_EV_CONNECT) {
- mg_sntp_request(c);
- } else if (ev == MG_EV_CLOSE) {
+ bio = BIO_new(tls->bm);
+ BIO_set_data(bio, c);
+ SSL_set_bio(tls->ssl, bio, bio);
+
+ c->tls = tls;
+ c->is_tls = 1;
+ c->is_tls_hs = 1;
+ if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
+ mg_tls_handshake(c);
}
- (void) fnd;
- (void) evd;
+ MG_DEBUG(("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
+ return;
+fail:
+ free(tls);
}
-void mg_sntp_request(struct mg_connection *c) {
- if (c->is_resolving) {
- MG_ERROR(("%lu wait until resolved", c->id));
+void mg_tls_handshake(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
+ if (rc == 1) {
+ MG_DEBUG(("%lu success", c->id));
+ c->is_tls_hs = 0;
+ mg_call(c, MG_EV_TLS_HS, NULL);
} else {
- int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98
- uint8_t buf[48] = {0};
- uint32_t *t = (uint32_t *) &buf[40];
- double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC;
- buf[0] = (0 << 6) | (4 << 3) | 3;
- t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET);
- t[1] = mg_htonl((uint32_t) frac);
- mg_send(c, buf, sizeof(buf));
+ int code = mg_tls_err(c, tls, rc);
+ if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
}
}
-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;
- return c;
+void mg_tls_free(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ if (tls == NULL) return;
+ SSL_free(tls->ssl);
+ SSL_CTX_free(tls->ctx);
+ BIO_meth_free(tls->bm);
+ free(tls);
+ c->tls = NULL;
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/sock.c"
-#endif
-
+size_t mg_tls_pending(struct mg_connection *c) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ return tls == NULL ? 0 : (size_t) SSL_pending(tls->ssl);
+}
+long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int n = SSL_read(tls->ssl, buf, (int) len);
+ if (n < 0 && mg_tls_err(c, 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) {
+ struct mg_tls *tls = (struct mg_tls *) c->tls;
+ int n = SSL_write(tls->ssl, buf, (int) len);
+ if (n < 0 && mg_tls_err(c, tls, n) == 0) return MG_IO_WAIT;
+ if (n <= 0) return MG_IO_ERR;
+ return n;
+}
+void mg_tls_ctx_init(struct mg_mgr *mgr) {
+ (void) mgr;
+}
+void mg_tls_ctx_free(struct mg_mgr *mgr) {
+ (void) mgr;
+}
+#endif
+#ifdef MG_ENABLE_LINES
+#line 1 "src/tls_uecc.c"
+#endif
+/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
-#if MG_ENABLE_SOCKET
+#if MG_TLS == MG_TLS_BUILTIN
-#ifndef closesocket
-#define closesocket(x) close(x)
+#ifndef MG_UECC_RNG_MAX_TRIES
+#define MG_UECC_RNG_MAX_TRIES 64
#endif
-#define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd)
-#define S2PTR(s_) ((void *) (size_t) (s_))
-
-#ifndef MSG_NONBLOCKING
-#define MSG_NONBLOCKING 0
+#if MG_UECC_ENABLE_VLI_API
+#define MG_UECC_VLI_API
+#else
+#define MG_UECC_VLI_API static
#endif
-#ifndef AF_INET6
-#define AF_INET6 10
+#if (MG_UECC_PLATFORM == mg_uecc_avr) || (MG_UECC_PLATFORM == mg_uecc_arm) || \
+ (MG_UECC_PLATFORM == mg_uecc_arm_thumb) || \
+ (MG_UECC_PLATFORM == mg_uecc_arm_thumb2)
+#define CONCATX(a, ...) a##__VA_ARGS__
+#define CONCAT(a, ...) CONCATX(a, __VA_ARGS__)
+
+#define STRX(a) #a
+#define STR(a) STRX(a)
+
+#define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
+#define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
+#define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__))))
+#define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__))))
+#define EVAL4(...) __VA_ARGS__
+
+#define DEC_1 0
+#define DEC_2 1
+#define DEC_3 2
+#define DEC_4 3
+#define DEC_5 4
+#define DEC_6 5
+#define DEC_7 6
+#define DEC_8 7
+#define DEC_9 8
+#define DEC_10 9
+#define DEC_11 10
+#define DEC_12 11
+#define DEC_13 12
+#define DEC_14 13
+#define DEC_15 14
+#define DEC_16 15
+#define DEC_17 16
+#define DEC_18 17
+#define DEC_19 18
+#define DEC_20 19
+#define DEC_21 20
+#define DEC_22 21
+#define DEC_23 22
+#define DEC_24 23
+#define DEC_25 24
+#define DEC_26 25
+#define DEC_27 26
+#define DEC_28 27
+#define DEC_29 28
+#define DEC_30 29
+#define DEC_31 30
+#define DEC_32 31
+
+#define DEC(N) CONCAT(DEC_, N)
+
+#define SECOND_ARG(_, val, ...) val
+#define SOME_CHECK_0 ~, 0
+#define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME, )
+#define SOME_OR_0(N) GET_SECOND_ARG(CONCAT(SOME_CHECK_, N))
+
+#define EMPTY(...)
+#define DEFER(...) __VA_ARGS__ EMPTY()
+
+#define REPEAT_NAME_0() REPEAT_0
+#define REPEAT_NAME_SOME() REPEAT_SOME
+#define REPEAT_0(...)
+#define REPEAT_SOME(N, stuff) \
+ DEFER(CONCAT(REPEAT_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), stuff) stuff
+#define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff))
+
+#define REPEATM_NAME_0() REPEATM_0
+#define REPEATM_NAME_SOME() REPEATM_SOME
+#define REPEATM_0(...)
+#define REPEATM_SOME(N, macro) \
+ macro(N) DEFER(CONCAT(REPEATM_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), macro)
+#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro))
#endif
-union usa {
- struct sockaddr sa;
- struct sockaddr_in sin;
-#if MG_ENABLE_IPV6
- struct sockaddr_in6 sin6;
-#endif
-};
+//
-static socklen_t tousa(struct mg_addr *a, union usa *usa) {
- socklen_t len = sizeof(usa->sin);
- memset(usa, 0, sizeof(*usa));
- usa->sin.sin_family = AF_INET;
- usa->sin.sin_port = a->port;
- *(uint32_t *) &usa->sin.sin_addr = a->ip;
-#if MG_ENABLE_IPV6
- if (a->is_ip6) {
- usa->sin.sin_family = AF_INET6;
- usa->sin6.sin6_port = a->port;
- memcpy(&usa->sin6.sin6_addr, a->ip6, sizeof(a->ip6));
- len = sizeof(usa->sin6);
- }
+#if (MG_UECC_WORD_SIZE == 1)
+#if MG_UECC_SUPPORTS_secp160r1
+#define MG_UECC_MAX_WORDS 21 /* Due to the size of curve_n. */
#endif
- return len;
-}
-
-static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
- a->is_ip6 = is_ip6;
- a->port = usa->sin.sin_port;
- memcpy(&a->ip, &usa->sin.sin_addr, sizeof(a->ip));
-#if MG_ENABLE_IPV6
- if (is_ip6) {
- memcpy(a->ip6, &usa->sin6.sin6_addr, sizeof(a->ip6));
- a->port = usa->sin6.sin6_port;
- }
+#if MG_UECC_SUPPORTS_secp192r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 24
#endif
-}
-
-static bool mg_sock_would_block(void) {
- int err = MG_SOCKET_ERRNO;
- return err == EINPROGRESS || err == EWOULDBLOCK
-#ifndef WINCE
- || err == EAGAIN || err == EINTR
+#if MG_UECC_SUPPORTS_secp224r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 28
#endif
-#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- || err == WSAEINTR || err == WSAEWOULDBLOCK
+#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 32
#endif
- ;
-}
-
-static bool mg_sock_conn_reset(void) {
- int err = MG_SOCKET_ERRNO;
-#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- return err == WSAECONNRESET;
-#else
- return err == EPIPE || err == ECONNRESET;
+#elif (MG_UECC_WORD_SIZE == 4)
+#if MG_UECC_SUPPORTS_secp160r1
+#define MG_UECC_MAX_WORDS 6 /* Due to the size of curve_n. */
#endif
-}
+#if MG_UECC_SUPPORTS_secp192r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 6
+#endif
+#if MG_UECC_SUPPORTS_secp224r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 7
+#endif
+#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 8
+#endif
+#elif (MG_UECC_WORD_SIZE == 8)
+#if MG_UECC_SUPPORTS_secp160r1
+#define MG_UECC_MAX_WORDS 3
+#endif
+#if MG_UECC_SUPPORTS_secp192r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 3
+#endif
+#if MG_UECC_SUPPORTS_secp224r1
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 4
+#endif
+#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
+#undef MG_UECC_MAX_WORDS
+#define MG_UECC_MAX_WORDS 4
+#endif
+#endif /* MG_UECC_WORD_SIZE */
+
+#define BITS_TO_WORDS(num_bits) \
+ ((wordcount_t) ((num_bits + ((MG_UECC_WORD_SIZE * 8) - 1)) / \
+ (MG_UECC_WORD_SIZE * 8)))
+#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8)
+
+struct MG_UECC_Curve_t {
+ wordcount_t num_words;
+ wordcount_t num_bytes;
+ bitcount_t num_n_bits;
+ mg_uecc_word_t p[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t n[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t G[MG_UECC_MAX_WORDS * 2];
+ mg_uecc_word_t b[MG_UECC_MAX_WORDS];
+ void (*double_jacobian)(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *Z1, MG_UECC_Curve curve);
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ void (*mod_sqrt)(mg_uecc_word_t *a, MG_UECC_Curve curve);
+#endif
+ void (*x_side)(mg_uecc_word_t *result, const mg_uecc_word_t *x,
+ MG_UECC_Curve curve);
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ void (*mmod_fast)(mg_uecc_word_t *result, mg_uecc_word_t *product);
+#endif
+};
-static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
- union usa usa;
- socklen_t n = sizeof(usa);
- if (getsockname(fd, &usa.sa, &n) == 0) {
- tomgaddr(&usa, addr, n != sizeof(usa.sin));
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+static void bcopy(uint8_t *dst, const uint8_t *src, unsigned num_bytes) {
+ while (0 != num_bytes) {
+ num_bytes--;
+ dst[num_bytes] = src[num_bytes];
}
}
+#endif
-static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
- if (n == MG_IO_WAIT) {
- // Do nothing
- } else if (n <= 0) {
- c->is_closing = 1; // Termination. Don't call mg_error(): #1529
- } else if (n > 0) {
- if (c->is_hexdumping) {
- union usa usa;
- socklen_t slen = sizeof(usa.sin);
- if (getsockname(FD(c), &usa.sa, &slen) < 0) (void) 0; // Ignore result
- MG_INFO(("\n-- %lu %I %s %I %ld", c->id, 4, &usa.sin.sin_addr,
- r ? "<-" : "->", 4, &c->rem.ip, n));
+static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words);
- mg_hexdump(buf, (size_t) n);
- }
- if (r) {
- c->recv.len += (size_t) n;
- mg_call(c, MG_EV_READ, &n);
- } else {
- mg_iobuf_del(&c->send, 0, (size_t) n);
- // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
- if (c->send.len == 0) {
- MG_EPOLL_MOD(c, 0);
- }
- mg_call(c, MG_EV_WRITE, &n);
- }
- }
-}
+#if (MG_UECC_PLATFORM == mg_uecc_arm || \
+ MG_UECC_PLATFORM == mg_uecc_arm_thumb || \
+ MG_UECC_PLATFORM == mg_uecc_arm_thumb2)
-long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
- long n;
- if (c->is_udp) {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
- if (n > 0) setlocaddr(FD(c), &c->loc);
- } else {
- n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
-#if MG_ARCH == MG_ARCH_RTX
- if (n == EWOULDBLOCK) return MG_IO_WAIT;
#endif
- }
- 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) {
- if (c->is_udp) {
- 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,
- (int) c->recv.len, n, MG_SOCKET_ERRNO));
- iolog(c, (char *) buf, n, false);
- return n > 0;
- } else {
- return mg_iobuf_add(&c->send, c->send.len, buf, len);
- }
-}
+#if (MG_UECC_PLATFORM == mg_uecc_avr)
-static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
-#if defined(MG_CUSTOM_NONBLOCK)
- MG_CUSTOM_NONBLOCK(fd);
-#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- unsigned long on = 1;
- ioctlsocket(fd, FIONBIO, &on);
-#elif MG_ARCH == MG_ARCH_RTX
- unsigned long on = 1;
- ioctlsocket(fd, FIONBIO, &on);
-#elif MG_ENABLE_FREERTOS_TCP
- const BaseType_t off = 0;
- if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0;
- if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0;
-#elif MG_ENABLE_LWIP
- lwip_fcntl(fd, F_SETFL, O_NONBLOCK);
-#elif MG_ARCH == MG_ARCH_AZURERTOS
- fcntl(fd, F_SETFL, O_NONBLOCK);
-#elif MG_ARCH == MG_ARCH_TIRTOS
- int val = 0;
- setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
- // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT
- int sz = sizeof(val);
- getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz);
- val /= 2; // set send low-water mark at half send buffer size
- setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val));
-#else
- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode
- fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
#endif
-}
-bool mg_open_listener(struct mg_connection *c, const char *url) {
- MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
- bool success = false;
- c->loc.port = mg_htons(mg_url_port(url));
- if (!mg_aton(mg_url_host(url), &c->loc)) {
- MG_ERROR(("invalid listening URL: %s", url));
- } else {
- union usa usa;
- socklen_t slen = tousa(&c->loc, &usa);
- int on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
- int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
- int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
- (void) on;
+#ifndef asm_clear
+#define asm_clear 0
+#endif
+#ifndef asm_set
+#define asm_set 0
+#endif
+#ifndef asm_add
+#define asm_add 0
+#endif
+#ifndef asm_sub
+#define asm_sub 0
+#endif
+#ifndef asm_mult
+#define asm_mult 0
+#endif
+#ifndef asm_rshift1
+#define asm_rshift1 0
+#endif
+#ifndef asm_mmod_fast_secp256r1
+#define asm_mmod_fast_secp256r1 0
+#endif
- if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
- MG_ERROR(("socket: %d", MG_SOCKET_ERRNO));
-#if ((MG_ARCH == MG_ARCH_WIN32) || (MG_ARCH == MG_ARCH_UNIX) || \
- (defined(LWIP_SOCKET) && SO_REUSE == 1))
- } else if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
- sizeof(on)) != 0) {
- // 1. SO_RESUSEADDR is not enabled on Windows because the semantics of
- // SO_REUSEADDR on UNIX and Windows is different. On Windows,
- // SO_REUSEADDR allows to bind a socket to a port without error even
- // if the port is already open by another program. This is not the
- // behavior SO_REUSEADDR was designed for, and leads to hard-to-track
- // failure scenarios. Therefore, SO_REUSEADDR was disabled on Windows
- // unless SO_EXCLUSIVEADDRUSE is supported and set on a socket.
- // 2. In case of LWIP, SO_REUSEADDR should be explicitly enabled, by
- // defining
- // SO_REUSE (in lwipopts.h), otherwise the code below will compile
- // but won't work! (setsockopt will return EINVAL)
- MG_ERROR(("reuseaddr: %d", MG_SOCKET_ERRNO));
-#endif
-#if MG_ARCH == MG_ARCH_WIN32 && !defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE)
- } else if (setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &on,
- sizeof(on)) != 0) {
- // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
- MG_ERROR(("exclusiveaddruse: %d", MG_SOCKET_ERRNO));
+#if defined(default_RNG_defined) && default_RNG_defined
+static MG_UECC_RNG_Function g_rng_function = &default_RNG;
+#else
+static MG_UECC_RNG_Function g_rng_function = 0;
#endif
- } else if (bind(fd, &usa.sa, slen) != 0) {
- MG_ERROR(("bind: %d", MG_SOCKET_ERRNO));
- } else if ((type == SOCK_STREAM &&
- listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE) != 0)) {
- // NOTE(lsm): FreeRTOS uses backlog value as a connection limit
- // In case port was set to 0, get the real port number
- MG_ERROR(("listen: %d", MG_SOCKET_ERRNO));
- } else {
- setlocaddr(fd, &c->loc);
- mg_set_non_blocking_mode(fd);
- c->fd = S2PTR(fd);
- MG_EPOLL_ADD(c);
- success = true;
- }
- }
- if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd);
- return success;
+
+void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) {
+ g_rng_function = rng_function;
+}
+
+MG_UECC_RNG_Function mg_uecc_get_rng(void) {
+ return g_rng_function;
}
-long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
- long n = 0;
- if (c->is_udp) {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
- if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
- } else {
- n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
+int mg_uecc_curve_private_key_size(MG_UECC_Curve curve) {
+ return BITS_TO_BYTES(curve->num_n_bits);
+}
+
+int mg_uecc_curve_public_key_size(MG_UECC_Curve curve) {
+ return 2 * curve->num_bytes;
+}
+
+#if !asm_clear
+MG_UECC_VLI_API void mg_uecc_vli_clear(mg_uecc_word_t *vli,
+ wordcount_t num_words) {
+ wordcount_t i;
+ for (i = 0; i < num_words; ++i) {
+ vli[i] = 0;
}
- 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;
}
+#endif /* !asm_clear */
-// NOTE(lsm): do only one iteration of reads, cause some systems
-// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data
-static void read_conn(struct mg_connection *c) {
- long n = -1;
- if (c->recv.len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "max_recv_buf_size reached");
- } else if (c->recv.size <= c->recv.len &&
- !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) {
- mg_error(c, "oom");
- } else {
- char *buf = (char *) &c->recv.buf[c->recv.len];
- size_t len = c->recv.size - c->recv.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,
- (long) c->send.len, (long) c->send.size, (long) c->recv.len,
- (long) c->recv.size, n, MG_SOCKET_ERRNO));
- iolog(c, buf, n, true);
+/* Constant-time comparison to zero - secure way to compare long integers */
+/* Returns 1 if vli == 0, 0 otherwise. */
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli,
+ wordcount_t num_words) {
+ mg_uecc_word_t bits = 0;
+ wordcount_t i;
+ for (i = 0; i < num_words; ++i) {
+ bits |= vli[i];
}
+ return (bits == 0);
}
-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);
- 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->recv.size, n, MG_SOCKET_ERRNO));
- iolog(c, buf, n, false);
+/* Returns nonzero if bit 'bit' of vli is set. */
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli,
+ bitcount_t bit) {
+ return (vli[bit >> MG_UECC_WORD_BITS_SHIFT] &
+ ((mg_uecc_word_t) 1 << (bit & MG_UECC_WORD_BITS_MASK)));
}
-static void close_conn(struct mg_connection *c) {
- if (FD(c) != MG_INVALID_SOCKET) {
-#if MG_ENABLE_EPOLL
- epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL);
-#endif
- closesocket(FD(c));
-#if MG_ENABLE_FREERTOS_TCP
- FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
-#endif
+/* Counts the number of words in vli. */
+static wordcount_t vli_numDigits(const mg_uecc_word_t *vli,
+ const wordcount_t max_words) {
+ wordcount_t i;
+ /* Search from the end until we find a non-zero digit.
+ We do it in reverse because we expect that most digits will be nonzero. */
+ for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) {
}
- mg_close_conn(c);
+
+ return (i + 1);
}
-static void connect_conn(struct mg_connection *c) {
- union usa usa;
- socklen_t n = sizeof(usa);
- // Use getpeername() to test whether we have connected
- if (getpeername(FD(c), &usa.sa, &n) == 0) {
- c->is_connecting = 0;
- mg_call(c, MG_EV_CONNECT, NULL);
- MG_EPOLL_MOD(c, 0);
- if (c->is_tls_hs) mg_tls_handshake(c);
- } else {
- mg_error(c, "socket error");
+/* Counts the number of bits required to represent vli. */
+MG_UECC_VLI_API bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli,
+ const wordcount_t max_words) {
+ mg_uecc_word_t i;
+ mg_uecc_word_t digit;
+
+ wordcount_t num_digits = vli_numDigits(vli, max_words);
+ if (num_digits == 0) {
+ return 0;
}
-}
-static void setsockopts(struct mg_connection *c) {
-#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \
- MG_ARCH == MG_ARCH_TIRTOS
- (void) c;
-#else
- int on = 1;
-#if !defined(SOL_TCP)
-#define SOL_TCP IPPROTO_TCP
-#endif
- if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0)
- (void) 0;
- if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) !=
- 0)
- (void) 0;
-#endif
+ digit = vli[num_digits - 1];
+ for (i = 0; digit; ++i) {
+ digit >>= 1;
+ }
+
+ return (((bitcount_t) ((num_digits - 1) << MG_UECC_WORD_BITS_SHIFT)) +
+ (bitcount_t) i);
}
-void mg_connect_resolved(struct mg_connection *c) {
- int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
- 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->is_resolving = 0; // Clear resolving flag
- if (FD(c) == MG_INVALID_SOCKET) {
- mg_error(c, "socket(): %d", MG_SOCKET_ERRNO);
- } else if (c->is_udp) {
- MG_EPOLL_ADD(c);
-#if MG_ARCH == MG_ARCH_TIRTOS
- union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets
- socklen_t slen = tousa(&c->loc, &usa);
- if (bind(c->fd, &usa.sa, slen) != 0)
- MG_ERROR(("bind: %d", MG_SOCKET_ERRNO));
-#endif
- mg_call(c, MG_EV_RESOLVE, NULL);
- mg_call(c, MG_EV_CONNECT, NULL);
- } else {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- mg_set_non_blocking_mode(FD(c));
- setsockopts(c);
- MG_EPOLL_ADD(c);
- mg_call(c, MG_EV_RESOLVE, NULL);
- if ((rc = connect(FD(c), &usa.sa, slen)) == 0) {
- mg_call(c, MG_EV_CONNECT, NULL);
- } else if (mg_sock_would_block()) {
- MG_DEBUG(("%lu %p -> %I:%hu pend", c->id, c->fd, 4, &c->rem.ip,
- mg_ntohs(c->rem.port)));
- c->is_connecting = 1;
- } else {
- mg_error(c, "connect: %d", MG_SOCKET_ERRNO);
+/* Sets dest = src. */
+#if !asm_set
+MG_UECC_VLI_API void mg_uecc_vli_set(mg_uecc_word_t *dest,
+ const mg_uecc_word_t *src,
+ wordcount_t num_words) {
+ wordcount_t i;
+ for (i = 0; i < num_words; ++i) {
+ dest[i] = src[i];
+ }
+}
+#endif /* !asm_set */
+
+/* Returns sign of left - right. */
+static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ wordcount_t i;
+ for (i = num_words - 1; i >= 0; --i) {
+ if (left[i] > right[i]) {
+ return 1;
+ } else if (left[i] < right[i]) {
+ return -1;
}
}
- (void) rc;
+ return 0;
}
-static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa,
- socklen_t *len) {
- MG_SOCKET_TYPE s = MG_INVALID_SOCKET;
- do {
- memset(usa, 0, sizeof(*usa));
- s = accept(sock, &usa->sa, len);
- } while (s == MG_INVALID_SOCKET && errno == EINTR);
- return s;
+/* Constant-time comparison function - secure way to compare long integers */
+/* Returns one if left == right, zero otherwise. */
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ mg_uecc_word_t diff = 0;
+ wordcount_t i;
+ for (i = num_words - 1; i >= 0; --i) {
+ diff |= (left[i] ^ right[i]);
+ }
+ return (diff == 0);
+}
+
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words);
+
+/* Returns sign of left - right, in constant time. */
+MG_UECC_VLI_API cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t neg = !!mg_uecc_vli_sub(tmp, left, right, num_words);
+ mg_uecc_word_t equal = mg_uecc_vli_isZero(tmp, num_words);
+ return (cmpresult_t) (!equal - 2 * neg);
+}
+
+/* Computes vli = vli >> 1. */
+#if !asm_rshift1
+MG_UECC_VLI_API void mg_uecc_vli_rshift1(mg_uecc_word_t *vli,
+ wordcount_t num_words) {
+ mg_uecc_word_t *end = vli;
+ mg_uecc_word_t carry = 0;
+
+ vli += num_words;
+ while (vli-- > end) {
+ mg_uecc_word_t temp = *vli;
+ *vli = (temp >> 1) | carry;
+ carry = temp << (MG_UECC_WORD_BITS - 1);
+ }
}
+#endif /* !asm_rshift1 */
+
+/* Computes result = left + right, returning carry. Can modify in place. */
+#if !asm_add
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ mg_uecc_word_t carry = 0;
+ wordcount_t i;
+ for (i = 0; i < num_words; ++i) {
+ mg_uecc_word_t sum = left[i] + right[i] + carry;
+ if (sum != left[i]) {
+ carry = (sum < left[i]);
+ }
+ result[i] = sum;
+ }
+ return carry;
+}
+#endif /* !asm_add */
+
+/* Computes result = left - right, returning borrow. Can modify in place. */
+#if !asm_sub
+MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ mg_uecc_word_t borrow = 0;
+ wordcount_t i;
+ for (i = 0; i < num_words; ++i) {
+ mg_uecc_word_t diff = left[i] - right[i] - borrow;
+ if (diff != left[i]) {
+ borrow = (diff > left[i]);
+ }
+ result[i] = diff;
+ }
+ return borrow;
+}
+#endif /* !asm_sub */
+
+#if !asm_mult || (MG_UECC_SQUARE_FUNC && !asm_square) || \
+ (MG_UECC_SUPPORTS_secp256k1 && (MG_UECC_OPTIMIZATION_LEVEL > 0) && \
+ ((MG_UECC_WORD_SIZE == 1) || (MG_UECC_WORD_SIZE == 8)))
+static void muladd(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0,
+ mg_uecc_word_t *r1, mg_uecc_word_t *r2) {
+#if MG_UECC_WORD_SIZE == 8
+ uint64_t a0 = a & 0xffffffff;
+ uint64_t a1 = a >> 32;
+ uint64_t b0 = b & 0xffffffff;
+ uint64_t b1 = b >> 32;
+
+ uint64_t i0 = a0 * b0;
+ uint64_t i1 = a0 * b1;
+ uint64_t i2 = a1 * b0;
+ uint64_t i3 = a1 * b1;
+
+ uint64_t p0, p1;
+
+ i2 += (i0 >> 32);
+ i2 += i1;
+ if (i2 < i1) { /* overflow */
+ i3 += 0x100000000;
+ }
-static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
- struct mg_connection *c = NULL;
- union usa usa;
- socklen_t sa_len = sizeof(usa);
- MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len);
- if (fd == MG_INVALID_SOCKET) {
-#if MG_ARCH == MG_ARCH_AZURERTOS
- // AzureRTOS, in non-block socket mode can mark listening socket readable
- // even it is not. See comment for 'select' func implementation in
- // nx_bsd.c That's not an error, just should try later
- if (MG_SOCKET_ERRNO != EAGAIN)
-#endif
- MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCKET_ERRNO));
-#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \
- (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL
- } else if ((long) fd >= FD_SETSIZE) {
- MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE));
- closesocket(fd);
+ p0 = (i0 & 0xffffffff) | (i2 << 32);
+ p1 = i3 + (i2 >> 32);
+
+ *r0 += p0;
+ *r1 += (p1 + (*r0 < p0));
+ *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0));
+#else
+ mg_uecc_dword_t p = (mg_uecc_dword_t) a * b;
+ mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0;
+ r01 += p;
+ *r2 += (r01 < p);
+ *r1 = (mg_uecc_word_t) (r01 >> MG_UECC_WORD_BITS);
+ *r0 = (mg_uecc_word_t) r01;
#endif
- } else if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("%lu OOM", lsn->id));
- closesocket(fd);
- } else {
- tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin));
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->fd = S2PTR(fd);
- MG_EPOLL_ADD(c);
- mg_set_non_blocking_mode(FD(c));
- setsockopts(c);
- c->is_accepted = 1;
- c->is_hexdumping = lsn->is_hexdumping;
- c->loc = lsn->loc;
- c->pfn = lsn->pfn;
- c->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- MG_DEBUG(("%lu %p accepted %I.%hu -> %I.%hu", c->id, c->fd, 4, &c->rem.ip,
- mg_ntohs(c->rem.port), 4, &c->loc.ip, mg_ntohs(c->loc.port)));
- mg_call(c, MG_EV_OPEN, NULL);
- mg_call(c, MG_EV_ACCEPT, NULL);
+}
+#endif /* muladd needed */
+
+#if !asm_mult
+MG_UECC_VLI_API void mg_uecc_vli_mult(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ wordcount_t num_words) {
+ mg_uecc_word_t r0 = 0;
+ mg_uecc_word_t r1 = 0;
+ mg_uecc_word_t r2 = 0;
+ wordcount_t i, k;
+
+ /* Compute each digit of result in sequence, maintaining the carries. */
+ for (k = 0; k < num_words; ++k) {
+ for (i = 0; i <= k; ++i) {
+ muladd(left[i], right[k - i], &r0, &r1, &r2);
+ }
+ result[k] = r0;
+ r0 = r1;
+ r1 = r2;
+ r2 = 0;
}
+ for (k = num_words; k < num_words * 2 - 1; ++k) {
+ for (i = (wordcount_t) ((k + 1) - num_words); i < num_words; ++i) {
+ muladd(left[i], right[k - i], &r0, &r1, &r2);
+ }
+ result[k] = r0;
+ r0 = r1;
+ r1 = r2;
+ r2 = 0;
+ }
+ result[num_words * 2 - 1] = r0;
}
+#endif /* !asm_mult */
-static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2], bool udp) {
- MG_SOCKET_TYPE sock;
- socklen_t n = sizeof(usa[0].sin);
- bool success = false;
+#if MG_UECC_SQUARE_FUNC
- sock = sp[0] = sp[1] = MG_INVALID_SOCKET;
- (void) memset(&usa[0], 0, sizeof(usa[0]));
- usa[0].sin.sin_family = AF_INET;
- *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1
- usa[1] = usa[0];
+#if !asm_square
+static void mul2add(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0,
+ mg_uecc_word_t *r1, mg_uecc_word_t *r2) {
+#if MG_UECC_WORD_SIZE == 8
+ uint64_t a0 = a & 0xffffffffull;
+ uint64_t a1 = a >> 32;
+ uint64_t b0 = b & 0xffffffffull;
+ uint64_t b1 = b >> 32;
- if (udp && (sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
- (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != 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 &&
- getsockname(sp[1], &usa[1].sa, &n) == 0 &&
- connect(sp[0], &usa[1].sa, n) == 0 &&
- connect(sp[1], &usa[0].sa, n) == 0) {
- success = true;
- } else if (!udp &&
- (sock = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
- bind(sock, &usa[0].sa, n) == 0 &&
- listen(sock, MG_SOCK_LISTEN_BACKLOG_SIZE) == 0 &&
- getsockname(sock, &usa[0].sa, &n) == 0 &&
- (sp[0] = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
- connect(sp[0], &usa[0].sa, n) == 0 &&
- (sp[1] = raccept(sock, &usa[1], &n)) != MG_INVALID_SOCKET) {
- success = true;
- }
- if (success) {
- mg_set_non_blocking_mode(sp[1]);
- } else {
- if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]);
- if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]);
- sp[0] = sp[1] = MG_INVALID_SOCKET;
+ uint64_t i0 = a0 * b0;
+ uint64_t i1 = a0 * b1;
+ uint64_t i2 = a1 * b0;
+ uint64_t i3 = a1 * b1;
+
+ uint64_t p0, p1;
+
+ i2 += (i0 >> 32);
+ i2 += i1;
+ if (i2 < i1) { /* overflow */
+ i3 += 0x100000000ull;
}
- if (sock != MG_INVALID_SOCKET) closesocket(sock);
- return success;
+
+ p0 = (i0 & 0xffffffffull) | (i2 << 32);
+ p1 = i3 + (i2 >> 32);
+
+ *r2 += (p1 >> 63);
+ p1 = (p1 << 1) | (p0 >> 63);
+ p0 <<= 1;
+
+ *r0 += p0;
+ *r1 += (p1 + (*r0 < p0));
+ *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0));
+#else
+ mg_uecc_dword_t p = (mg_uecc_dword_t) a * b;
+ mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0;
+ *r2 += (p >> (MG_UECC_WORD_BITS * 2 - 1));
+ p *= 2;
+ r01 += p;
+ *r2 += (r01 < p);
+ *r1 = r01 >> MG_UECC_WORD_BITS;
+ *r0 = (mg_uecc_word_t) r01;
+#endif
}
-int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data,
- bool udp) {
- union usa usa[2];
- MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET};
- struct mg_connection *c = NULL;
- if (!mg_socketpair(sp, usa, udp)) {
- MG_ERROR(("Cannot create socket pair"));
- } else if ((c = mg_wrapfd(mgr, (int) sp[1], fn, fn_data)) == NULL) {
- closesocket(sp[0]);
- closesocket(sp[1]);
- sp[0] = sp[1] = MG_INVALID_SOCKET;
- } else {
- tomgaddr(&usa[0], &c->rem, false);
- MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0]));
+MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ wordcount_t num_words) {
+ mg_uecc_word_t r0 = 0;
+ mg_uecc_word_t r1 = 0;
+ mg_uecc_word_t r2 = 0;
+
+ wordcount_t i, k;
+
+ for (k = 0; k < num_words * 2 - 1; ++k) {
+ mg_uecc_word_t min = (k < num_words ? 0 : (k + 1) - num_words);
+ for (i = min; i <= k && i <= k - i; ++i) {
+ if (i < k - i) {
+ mul2add(left[i], left[k - i], &r0, &r1, &r2);
+ } else {
+ muladd(left[i], left[k - i], &r0, &r1, &r2);
+ }
+ }
+ result[k] = r0;
+ r0 = r1;
+ r1 = r2;
+ r2 = 0;
}
- return (int) sp[0];
-}
-static bool can_read(const struct mg_connection *c) {
- return c->is_full == false;
+ result[num_words * 2 - 1] = r0;
}
+#endif /* !asm_square */
-static bool can_write(const struct mg_connection *c) {
- return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0);
-}
+#else /* MG_UECC_SQUARE_FUNC */
-static bool skip_iotest(const struct mg_connection *c) {
- return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) ||
- (can_read(c) == false && can_write(c) == false);
+#if MG_UECC_ENABLE_VLI_API
+MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ wordcount_t num_words) {
+ mg_uecc_vli_mult(result, left, left, num_words);
}
+#endif /* MG_UECC_ENABLE_VLI_API */
-static void mg_iotest(struct mg_mgr *mgr, int ms) {
-#if MG_ENABLE_FREERTOS_TCP
- struct mg_connection *c;
- for (c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) continue;
- if (can_read(c))
- FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT);
- if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE);
- }
- FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms));
- for (c = mgr->conns; c != NULL; c = c->next) {
- EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
- c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
- c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
- FreeRTOS_FD_CLR(c->fd, mgr->ss,
- eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
+#endif /* MG_UECC_SQUARE_FUNC */
+
+/* Computes result = (left + right) % mod.
+ Assumes that left < mod and right < mod, and that result does not overlap
+ mod. */
+MG_UECC_VLI_API void mg_uecc_vli_modAdd(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t carry = mg_uecc_vli_add(result, left, right, num_words);
+ if (carry || mg_uecc_vli_cmp_unsafe(mod, result, num_words) != 1) {
+ /* result > mod (result = mod + remainder), so subtract mod to get
+ * remainder. */
+ mg_uecc_vli_sub(result, result, mod, num_words);
}
-#elif MG_ENABLE_EPOLL
- size_t max = 1;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1;
- if (can_write(c)) MG_EPOLL_MOD(c, 1);
- max++;
+}
+
+/* Computes result = (left - right) % mod.
+ Assumes that left < mod and right < mod, and that result does not overlap
+ mod. */
+MG_UECC_VLI_API void mg_uecc_vli_modSub(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t l_borrow = mg_uecc_vli_sub(result, left, right, num_words);
+ if (l_borrow) {
+ /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x,
+ we can get the correct result from result + mod (with overflow). */
+ mg_uecc_vli_add(result, result, mod, num_words);
}
- struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0]));
- int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms);
- for (int i = 0; i < n; i++) {
- struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr;
- if (evs[i].events & EPOLLERR) {
- mg_error(c, "socket error");
- } else if (c->is_readable == 0) {
- bool rd = evs[i].events & (EPOLLIN | EPOLLHUP);
- bool wr = evs[i].events & EPOLLOUT;
- c->is_readable = can_read(c) && rd ? 1U : 0;
- c->is_writable = can_write(c) && wr ? 1U : 0;
+}
+
+/* Computes result = product % mod, where product is 2N words long. */
+/* Currently only designed to work for curve_p or curve_n. */
+MG_UECC_VLI_API void mg_uecc_vli_mmod(mg_uecc_word_t *result,
+ mg_uecc_word_t *product,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t mod_multiple[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tmp[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_word_t *v[2] = {tmp, product};
+ mg_uecc_word_t index;
+
+ /* Shift mod so its highest set bit is at the maximum position. */
+ bitcount_t shift = (bitcount_t) ((num_words * 2 * MG_UECC_WORD_BITS) -
+ mg_uecc_vli_numBits(mod, num_words));
+ wordcount_t word_shift = (wordcount_t) (shift / MG_UECC_WORD_BITS);
+ wordcount_t bit_shift = (wordcount_t) (shift % MG_UECC_WORD_BITS);
+ mg_uecc_word_t carry = 0;
+ mg_uecc_vli_clear(mod_multiple, word_shift);
+ if (bit_shift > 0) {
+ for (index = 0; index < (mg_uecc_word_t) num_words; ++index) {
+ mod_multiple[(mg_uecc_word_t) word_shift + index] =
+ (mg_uecc_word_t) (mod[index] << bit_shift) | carry;
+ carry = mod[index] >> (MG_UECC_WORD_BITS - bit_shift);
}
+ } else {
+ mg_uecc_vli_set(mod_multiple + word_shift, mod, num_words);
}
- (void) skip_iotest;
-#elif MG_ENABLE_POLL
- nfds_t n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++;
- struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0]));
- memset(fds, 0, n * sizeof(fds[0]));
- n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) {
- // Socket not valid, ignore
- } else if (mg_tls_pending(c) > 0) {
- ms = 1; // Don't wait if TLS is ready
- } else {
- fds[n].fd = FD(c);
- if (can_read(c)) fds[n].events |= POLLIN;
- if (can_write(c)) fds[n].events |= POLLOUT;
- n++;
+
+ for (index = 1; shift >= 0; --shift) {
+ mg_uecc_word_t borrow = 0;
+ wordcount_t i;
+ for (i = 0; i < num_words * 2; ++i) {
+ mg_uecc_word_t diff = v[index][i] - mod_multiple[i] - borrow;
+ if (diff != v[index][i]) {
+ borrow = (diff > v[index][i]);
+ }
+ v[1 - index][i] = diff;
}
+ index = !(index ^ borrow); /* Swap the index if there was no borrow */
+ mg_uecc_vli_rshift1(mod_multiple, num_words);
+ mod_multiple[num_words - 1] |= mod_multiple[num_words]
+ << (MG_UECC_WORD_BITS - 1);
+ mg_uecc_vli_rshift1(mod_multiple + num_words, num_words);
+ }
+ mg_uecc_vli_set(result, v[index], num_words);
+}
+
+/* Computes result = (left * right) % mod. */
+MG_UECC_VLI_API void mg_uecc_vli_modMult(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_vli_mult(product, left, right, num_words);
+ mg_uecc_vli_mmod(result, product, mod, num_words);
+}
+
+MG_UECC_VLI_API void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *right,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_vli_mult(product, left, right, curve->num_words);
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ curve->mmod_fast(result, product);
+#else
+ mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
+#endif
+}
+
+#if MG_UECC_SQUARE_FUNC
+
+#if MG_UECC_ENABLE_VLI_API
+/* Computes result = left^2 % mod. */
+MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_vli_square(product, left, num_words);
+ mg_uecc_vli_mmod(result, product, mod, num_words);
+}
+#endif /* MG_UECC_ENABLE_VLI_API */
+
+MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
+ mg_uecc_vli_square(product, left, curve->num_words);
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ curve->mmod_fast(result, product);
+#else
+ mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
+#endif
+}
+
+#else /* MG_UECC_SQUARE_FUNC */
+
+#if MG_UECC_ENABLE_VLI_API
+MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_vli_modMult(result, left, left, mod, num_words);
+}
+#endif /* MG_UECC_ENABLE_VLI_API */
+
+MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
+ const mg_uecc_word_t *left,
+ MG_UECC_Curve curve) {
+ mg_uecc_vli_modMult_fast(result, left, left, curve);
+}
+
+#endif /* MG_UECC_SQUARE_FUNC */
+
+#define EVEN(vli) (!(vli[0] & 1))
+static void vli_modInv_update(mg_uecc_word_t *uv, const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t carry = 0;
+ if (!EVEN(uv)) {
+ carry = mg_uecc_vli_add(uv, uv, mod, num_words);
}
+ mg_uecc_vli_rshift1(uv, num_words);
+ if (carry) {
+ uv[num_words - 1] |= HIGH_BIT_SET;
+ }
+}
- // MG_INFO(("poll n=%d ms=%d", (int) n, ms));
- if (poll(fds, n, ms) < 0) {
-#if MG_ARCH == MG_ARCH_WIN32
- if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets
-#endif
- memset(fds, 0, n * sizeof(fds[0]));
+/* Computes result = (1 / input) % mod. All VLIs are the same size.
+ See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */
+MG_UECC_VLI_API void mg_uecc_vli_modInv(mg_uecc_word_t *result,
+ const mg_uecc_word_t *input,
+ const mg_uecc_word_t *mod,
+ wordcount_t num_words) {
+ mg_uecc_word_t a[MG_UECC_MAX_WORDS], b[MG_UECC_MAX_WORDS],
+ u[MG_UECC_MAX_WORDS], v[MG_UECC_MAX_WORDS];
+ cmpresult_t cmpResult;
+
+ if (mg_uecc_vli_isZero(input, num_words)) {
+ mg_uecc_vli_clear(result, num_words);
+ return;
}
- n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- if (skip_iotest(c)) {
- // Socket not valid, ignore
- } else if (mg_tls_pending(c) > 0) {
- c->is_readable = 1;
+
+ mg_uecc_vli_set(a, input, num_words);
+ mg_uecc_vli_set(b, mod, num_words);
+ mg_uecc_vli_clear(u, num_words);
+ u[0] = 1;
+ mg_uecc_vli_clear(v, num_words);
+ while ((cmpResult = mg_uecc_vli_cmp_unsafe(a, b, num_words)) != 0) {
+ if (EVEN(a)) {
+ mg_uecc_vli_rshift1(a, num_words);
+ vli_modInv_update(u, mod, num_words);
+ } else if (EVEN(b)) {
+ mg_uecc_vli_rshift1(b, num_words);
+ vli_modInv_update(v, mod, num_words);
+ } else if (cmpResult > 0) {
+ mg_uecc_vli_sub(a, a, b, num_words);
+ mg_uecc_vli_rshift1(a, num_words);
+ if (mg_uecc_vli_cmp_unsafe(u, v, num_words) < 0) {
+ mg_uecc_vli_add(u, u, mod, num_words);
+ }
+ mg_uecc_vli_sub(u, u, v, num_words);
+ vli_modInv_update(u, mod, num_words);
} else {
- if (fds[n].revents & POLLERR) {
- mg_error(c, "socket error");
- } else {
- c->is_readable =
- (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0);
- c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0);
+ mg_uecc_vli_sub(b, b, a, num_words);
+ mg_uecc_vli_rshift1(b, num_words);
+ if (mg_uecc_vli_cmp_unsafe(v, u, num_words) < 0) {
+ mg_uecc_vli_add(v, v, mod, num_words);
}
- n++;
+ mg_uecc_vli_sub(v, v, u, num_words);
+ vli_modInv_update(v, mod, num_words);
}
}
-#else
- struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0};
- struct mg_connection *c;
- fd_set rset, wset, eset;
- MG_SOCKET_TYPE maxfd = 0;
- int rc;
+ mg_uecc_vli_set(result, u, num_words);
+}
- FD_ZERO(&rset);
- FD_ZERO(&wset);
- FD_ZERO(&eset);
- for (c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) continue;
- FD_SET(FD(c), &eset);
- if (can_read(c)) FD_SET(FD(c), &rset);
- if (can_write(c)) FD_SET(FD(c), &wset);
- if (mg_tls_pending(c) > 0) tv = tv_zero;
- if (FD(c) > maxfd) maxfd = FD(c);
+/* ------ Point operations ------ */
+
+/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
+
+#ifndef _UECC_CURVE_SPECIFIC_H_
+#define _UECC_CURVE_SPECIFIC_H_
+
+#define num_bytes_secp160r1 20
+#define num_bytes_secp192r1 24
+#define num_bytes_secp224r1 28
+#define num_bytes_secp256r1 32
+#define num_bytes_secp256k1 32
+
+#if (MG_UECC_WORD_SIZE == 1)
+
+#define num_words_secp160r1 20
+#define num_words_secp192r1 24
+#define num_words_secp224r1 28
+#define num_words_secp256r1 32
+#define num_words_secp256k1 32
+
+#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \
+ 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h
+#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d
+
+#elif (MG_UECC_WORD_SIZE == 4)
+
+#define num_words_secp160r1 5
+#define num_words_secp192r1 6
+#define num_words_secp224r1 7
+#define num_words_secp256r1 8
+#define num_words_secp256k1 8
+
+#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e
+#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a
+
+#elif (MG_UECC_WORD_SIZE == 8)
+
+#define num_words_secp160r1 3
+#define num_words_secp192r1 3
+#define num_words_secp224r1 4
+#define num_words_secp256r1 4
+#define num_words_secp256k1 4
+
+#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##U
+#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##U
+
+#endif /* MG_UECC_WORD_SIZE */
+
+#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \
+ MG_UECC_SUPPORTS_secp224r1 || MG_UECC_SUPPORTS_secp256r1
+static void double_jacobian_default(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *Z1, MG_UECC_Curve curve) {
+ /* t1 = X, t2 = Y, t3 = Z */
+ mg_uecc_word_t t4[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t t5[MG_UECC_MAX_WORDS];
+ wordcount_t num_words = curve->num_words;
+
+ if (mg_uecc_vli_isZero(Z1, num_words)) {
+ return;
}
- if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, &tv)) < 0) {
-#if MG_ARCH == MG_ARCH_WIN32
- if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets
+ mg_uecc_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */
+ mg_uecc_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */
+ mg_uecc_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */
+ mg_uecc_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */
+ mg_uecc_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */
+
+ mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */
+ mg_uecc_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */
+ mg_uecc_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */
+ mg_uecc_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */
+
+ mg_uecc_vli_modAdd(Z1, X1, X1, curve->p,
+ num_words); /* t3 = 2*(x1^2 - z1^4) */
+ mg_uecc_vli_modAdd(X1, X1, Z1, curve->p,
+ num_words); /* t1 = 3*(x1^2 - z1^4) */
+ if (mg_uecc_vli_testBit(X1, 0)) {
+ mg_uecc_word_t l_carry = mg_uecc_vli_add(X1, X1, curve->p, num_words);
+ mg_uecc_vli_rshift1(X1, num_words);
+ X1[num_words - 1] |= l_carry << (MG_UECC_WORD_BITS - 1);
+ } else {
+ mg_uecc_vli_rshift1(X1, num_words);
+ }
+ /* t1 = 3/2*(x1^2 - z1^4) = B */
+
+ mg_uecc_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */
+ mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */
+ mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */
+ mg_uecc_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */
+ mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */
+ mg_uecc_vli_modSub(t4, X1, t4, curve->p,
+ num_words); /* t4 = B * (A - x3) - y1^4 = y3 */
+
+ mg_uecc_vli_set(X1, Z1, num_words);
+ mg_uecc_vli_set(Z1, Y1, num_words);
+ mg_uecc_vli_set(Y1, t4, num_words);
+}
+
+/* Computes result = x^3 + ax + b. result must not overlap x. */
+static void x_side_default(mg_uecc_word_t *result, const mg_uecc_word_t *x,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t _3[MG_UECC_MAX_WORDS] = {3}; /* -a = 3 */
+ wordcount_t num_words = curve->num_words;
+
+ mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */
+ mg_uecc_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */
+ mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */
+ mg_uecc_vli_modAdd(result, result, curve->b, curve->p,
+ num_words); /* r = x^3 - 3x + b */
+}
+#endif /* MG_UECC_SUPPORTS_secp... */
+
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \
+ MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1
+/* Compute a = sqrt(a) (mod curve_p). */
+static void mod_sqrt_default(mg_uecc_word_t *a, MG_UECC_Curve curve) {
+ bitcount_t i;
+ mg_uecc_word_t p1[MG_UECC_MAX_WORDS] = {1};
+ mg_uecc_word_t l_result[MG_UECC_MAX_WORDS] = {1};
+ wordcount_t num_words = curve->num_words;
+
+ /* When curve->p == 3 (mod 4), we can compute
+ sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */
+ mg_uecc_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */
+ for (i = mg_uecc_vli_numBits(p1, num_words) - 1; i > 1; --i) {
+ mg_uecc_vli_modSquare_fast(l_result, l_result, curve);
+ if (mg_uecc_vli_testBit(p1, i)) {
+ mg_uecc_vli_modMult_fast(l_result, l_result, a, curve);
+ }
+ }
+ mg_uecc_vli_set(a, l_result, num_words);
+}
+#endif /* MG_UECC_SUPPORTS_secp... */
+#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
+
+#if MG_UECC_SUPPORTS_secp160r1
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product);
+#endif
+
+static const struct MG_UECC_Curve_t curve_secp160r1 = {
+ num_words_secp160r1,
+ num_bytes_secp160r1,
+ 161, /* num_n_bits */
+ {BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_4(FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9),
+ BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00)},
+ {BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68),
+ BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E),
+ BYTES_TO_WORDS_4(68, B5, 96, 4A),
+
+ BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04),
+ BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31),
+ BYTES_TO_WORDS_4(55, 28, A6, 23)},
+ {BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81),
+ BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54),
+ BYTES_TO_WORDS_4(FC, BE, 97, 1C)},
+ &double_jacobian_default,
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ &mod_sqrt_default,
+#endif
+ &x_side_default,
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ &vli_mmod_fast_secp160r1
+#endif
+};
+
+MG_UECC_Curve mg_uecc_secp160r1(void) {
+ return &curve_secp160r1;
+}
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1)
+/* Computes result = product % curve_p
+ see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354
+
+ Note that this only works if log2(omega) < log2(p) / 2 */
+static void omega_mult_secp160r1(mg_uecc_word_t *result,
+ const mg_uecc_word_t *right);
+#if MG_UECC_WORD_SIZE == 8
+static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product) {
+ mg_uecc_word_t tmp[2 * num_words_secp160r1];
+ mg_uecc_word_t copy;
+
+ mg_uecc_vli_clear(tmp, num_words_secp160r1);
+ mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1);
+
+ omega_mult_secp160r1(tmp,
+ product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */
+
+ product[num_words_secp160r1 - 1] &= 0xffffffff;
+ copy = tmp[num_words_secp160r1 - 1];
+ tmp[num_words_secp160r1 - 1] &= 0xffffffff;
+ mg_uecc_vli_add(result, product, tmp,
+ num_words_secp160r1); /* (C, r) = r + q */
+ mg_uecc_vli_clear(product, num_words_secp160r1);
+ tmp[num_words_secp160r1 - 1] = copy;
+ omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */
+ mg_uecc_vli_add(result, result, product,
+ num_words_secp160r1); /* (C1, r) = r + Rq*c */
+
+ while (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p,
+ num_words_secp160r1) > 0) {
+ mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
+ }
+}
+
+static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) {
+ uint32_t carry;
+ unsigned i;
+
+ /* Multiply by (2^31 + 1). */
+ carry = 0;
+ for (i = 0; i < num_words_secp160r1; ++i) {
+ uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32);
+ result[i] = (tmp << 31) + tmp + carry;
+ carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp));
+ }
+ result[i] = carry;
+}
#else
- MG_ERROR(("select: %d %d", rc, MG_SOCKET_ERRNO));
+static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product) {
+ mg_uecc_word_t tmp[2 * num_words_secp160r1];
+ mg_uecc_word_t carry;
+
+ mg_uecc_vli_clear(tmp, num_words_secp160r1);
+ mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1);
+
+ omega_mult_secp160r1(tmp,
+ product + num_words_secp160r1); /* (Rq, q) = q * c */
+
+ carry = mg_uecc_vli_add(result, product, tmp,
+ num_words_secp160r1); /* (C, r) = r + q */
+ mg_uecc_vli_clear(product, num_words_secp160r1);
+ omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */
+ carry += mg_uecc_vli_add(result, result, product,
+ num_words_secp160r1); /* (C1, r) = r + Rq*c */
+
+ while (carry > 0) {
+ --carry;
+ mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
+ }
+ if (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) >
+ 0) {
+ mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
+ }
+}
#endif
- FD_ZERO(&rset);
- FD_ZERO(&wset);
- FD_ZERO(&eset);
+
+#if MG_UECC_WORD_SIZE == 1
+static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) {
+ uint8_t carry;
+ uint8_t i;
+
+ /* Multiply by (2^31 + 1). */
+ mg_uecc_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */
+ mg_uecc_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */
+ result[3] = right[0] << 7; /* get last bit from shift */
+
+ carry = mg_uecc_vli_add(result, result, right,
+ num_words_secp160r1); /* 2^31 + 1 */
+ for (i = num_words_secp160r1; carry; ++i) {
+ uint16_t sum = (uint16_t) result[i] + carry;
+ result[i] = (uint8_t) sum;
+ carry = sum >> 8;
}
+}
+#elif MG_UECC_WORD_SIZE == 4
+static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) {
+ uint32_t carry;
+ unsigned i;
- for (c = mgr->conns; c != NULL; c = c->next) {
- if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) {
- mg_error(c, "socket error");
- } else {
- c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset);
- c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset);
- if (mg_tls_pending(c) > 0) c->is_readable = 1;
- }
+ /* Multiply by (2^31 + 1). */
+ mg_uecc_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */
+ mg_uecc_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */
+ result[0] = right[0] << 31; /* get last bit from shift */
+
+ carry = mg_uecc_vli_add(result, result, right,
+ num_words_secp160r1); /* 2^31 + 1 */
+ for (i = num_words_secp160r1; carry; ++i) {
+ uint64_t sum = (uint64_t) result[i] + carry;
+ result[i] = (uint32_t) sum;
+ carry = sum >> 32;
}
+}
+#endif /* MG_UECC_WORD_SIZE */
+#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */
+
+#endif /* MG_UECC_SUPPORTS_secp160r1 */
+
+#if MG_UECC_SUPPORTS_secp192r1
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+static void vli_mmod_fast_secp192r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product);
#endif
+
+static const struct MG_UECC_Curve_t curve_secp192r1 = {
+ num_words_secp192r1,
+ num_bytes_secp192r1,
+ 192, /* num_n_bits */
+ {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14),
+ BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4),
+ BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C),
+ BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18),
+
+ BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73),
+ BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63),
+ BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07)},
+ {BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE),
+ BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F),
+ BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64)},
+ &double_jacobian_default,
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ &mod_sqrt_default,
+#endif
+ &x_side_default,
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ &vli_mmod_fast_secp192r1
+#endif
+};
+
+MG_UECC_Curve mg_uecc_secp192r1(void) {
+ return &curve_secp192r1;
}
-void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
- struct mg_connection *c, *tmp;
- uint64_t now;
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+/* Computes result = product % curve_p.
+ See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf
+ */
+#if MG_UECC_WORD_SIZE == 1
+static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) {
+ uint8_t tmp[num_words_secp192r1];
+ uint8_t carry;
+
+ mg_uecc_vli_set(result, product, num_words_secp192r1);
+
+ mg_uecc_vli_set(tmp, &product[24], num_words_secp192r1);
+ carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
+ tmp[8] = product[24];
+ tmp[9] = product[25];
+ tmp[10] = product[26];
+ tmp[11] = product[27];
+ tmp[12] = product[28];
+ tmp[13] = product[29];
+ tmp[14] = product[30];
+ tmp[15] = product[31];
+ tmp[16] = product[32];
+ tmp[17] = product[33];
+ tmp[18] = product[34];
+ tmp[19] = product[35];
+ tmp[20] = product[36];
+ tmp[21] = product[37];
+ tmp[22] = product[38];
+ tmp[23] = product[39];
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ tmp[0] = tmp[8] = product[40];
+ tmp[1] = tmp[9] = product[41];
+ tmp[2] = tmp[10] = product[42];
+ tmp[3] = tmp[11] = product[43];
+ tmp[4] = tmp[12] = product[44];
+ tmp[5] = tmp[13] = product[45];
+ tmp[6] = tmp[14] = product[46];
+ tmp[7] = tmp[15] = product[47];
+ tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] =
+ tmp[23] = 0;
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
+ num_words_secp192r1) != 1) {
+ carry -=
+ mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
+ }
+}
+#elif MG_UECC_WORD_SIZE == 4
+static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) {
+ uint32_t tmp[num_words_secp192r1];
+ int carry;
- mg_iotest(mgr, ms);
- now = mg_millis();
- mg_timer_poll(&mgr->timers, now);
+ mg_uecc_vli_set(result, product, num_words_secp192r1);
- for (c = mgr->conns; c != NULL; c = tmp) {
- bool is_resp = c->is_resp;
- tmp = c->next;
- mg_call(c, MG_EV_POLL, &now);
- if (is_resp && !c->is_resp) {
- long n = 0;
- mg_call(c, MG_EV_READ, &n);
- }
- MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-',
- c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't',
- c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
- c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
- if (c->is_resolving || c->is_closing) {
- // Do nothing
- } else if (c->is_listening && c->is_udp == 0) {
- if (c->is_readable) accept_conn(mgr, c);
- } else if (c->is_connecting) {
- if (c->is_readable || c->is_writable) connect_conn(c);
- } else if (c->is_tls_hs) {
- if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
- } else {
- if (c->is_readable) read_conn(c);
- if (c->is_writable) write_conn(c);
- }
+ mg_uecc_vli_set(tmp, &product[6], num_words_secp192r1);
+ carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- if (c->is_draining && c->send.len == 0) c->is_closing = 1;
- if (c->is_closing) close_conn(c);
+ tmp[0] = tmp[1] = 0;
+ tmp[2] = product[6];
+ tmp[3] = product[7];
+ tmp[4] = product[8];
+ tmp[5] = product[9];
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ tmp[0] = tmp[2] = product[10];
+ tmp[1] = tmp[3] = product[11];
+ tmp[4] = tmp[5] = 0;
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
+ num_words_secp192r1) != 1) {
+ carry -=
+ mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
+ }
+}
+#else
+static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) {
+ uint64_t tmp[num_words_secp192r1];
+ int carry;
+
+ mg_uecc_vli_set(result, product, num_words_secp192r1);
+
+ mg_uecc_vli_set(tmp, &product[3], num_words_secp192r1);
+ carry = (int) mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ tmp[0] = 0;
+ tmp[1] = product[3];
+ tmp[2] = product[4];
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ tmp[0] = tmp[1] = product[5];
+ tmp[2] = 0;
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
+
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
+ num_words_secp192r1) != 1) {
+ carry -=
+ mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
}
}
+#endif /* MG_UECC_WORD_SIZE */
+#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */
+
+#endif /* MG_UECC_SUPPORTS_secp192r1 */
+
+#if MG_UECC_SUPPORTS_secp224r1
+
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve);
+#endif
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+static void vli_mmod_fast_secp224r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product);
#endif
-#ifdef MG_ENABLE_LINES
-#line 1 "src/ssi.c"
+static const struct MG_UECC_Curve_t curve_secp224r1 = {
+ num_words_secp224r1,
+ num_bytes_secp224r1,
+ 224, /* num_n_bits */
+ {BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_4(FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13),
+ BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_4(FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34),
+ BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A),
+ BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B),
+ BYTES_TO_WORDS_4(BD, 0C, 0E, B7),
+
+ BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44),
+ BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD),
+ BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5),
+ BYTES_TO_WORDS_4(88, 63, 37, BD)},
+ {BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27),
+ BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50),
+ BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C),
+ BYTES_TO_WORDS_4(85, 0A, 05, B4)},
+ &double_jacobian_default,
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ &mod_sqrt_secp224r1,
#endif
+ &x_side_default,
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ &vli_mmod_fast_secp224r1
+#endif
+};
+
+MG_UECC_Curve mg_uecc_secp224r1(void) {
+ return &curve_secp224r1;
+}
+
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void mod_sqrt_secp224r1_rs(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
+ mg_uecc_word_t *f1, const mg_uecc_word_t *d0,
+ const mg_uecc_word_t *e0,
+ const mg_uecc_word_t *f0) {
+ mg_uecc_word_t t[num_words_secp224r1];
+
+ mg_uecc_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */
+ mg_uecc_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */
+ mg_uecc_vli_modAdd(d1, t, f0, curve_secp224r1.p,
+ num_words_secp224r1); /* d1 <-- t + f0 */
+ mg_uecc_vli_modAdd(e1, e1, e1, curve_secp224r1.p,
+ num_words_secp224r1); /* e1 <-- e1 + e1 */
+ mg_uecc_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */
+ mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p,
+ num_words_secp224r1); /* f1 <-- f1 + f1 */
+ mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p,
+ num_words_secp224r1); /* f1 <-- f1 + f1 */
+}
+
+/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void mod_sqrt_secp224r1_rss(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
+ mg_uecc_word_t *f1, const mg_uecc_word_t *d0,
+ const mg_uecc_word_t *e0,
+ const mg_uecc_word_t *f0,
+ const bitcount_t j) {
+ bitcount_t i;
+
+ mg_uecc_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */
+ mg_uecc_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */
+ mg_uecc_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */
+ for (i = 1; i <= j; i++) {
+ mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */
+ }
+}
+
+/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void mod_sqrt_secp224r1_rm(mg_uecc_word_t *d2, mg_uecc_word_t *e2,
+ mg_uecc_word_t *f2, const mg_uecc_word_t *c,
+ const mg_uecc_word_t *d0,
+ const mg_uecc_word_t *e0,
+ const mg_uecc_word_t *d1,
+ const mg_uecc_word_t *e1) {
+ mg_uecc_word_t t1[num_words_secp224r1];
+ mg_uecc_word_t t2[num_words_secp224r1];
+
+ mg_uecc_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */
+ mg_uecc_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */
+ /* t1 <-- p - t1 */
+ mg_uecc_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p,
+ num_words_secp224r1);
+ mg_uecc_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */
+ mg_uecc_vli_modAdd(t2, t2, t1, curve_secp224r1.p,
+ num_words_secp224r1); /* t2 <-- t2 + t1 */
+ mg_uecc_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */
+ mg_uecc_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */
+ mg_uecc_vli_modAdd(e2, e2, t1, curve_secp224r1.p,
+ num_words_secp224r1); /* e2 <-- e2 + t1 */
+ mg_uecc_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */
+ mg_uecc_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */
+ /* f2 <-- p - f2 */
+ mg_uecc_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p,
+ num_words_secp224r1);
+ mg_uecc_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */
+}
+
+/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void mod_sqrt_secp224r1_rp(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
+ mg_uecc_word_t *f1, const mg_uecc_word_t *c,
+ const mg_uecc_word_t *r) {
+ wordcount_t i;
+ wordcount_t pow2i = 1;
+ mg_uecc_word_t d0[num_words_secp224r1];
+ mg_uecc_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */
+ mg_uecc_word_t f0[num_words_secp224r1];
+
+ mg_uecc_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */
+ /* f0 <-- p - c */
+ mg_uecc_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p,
+ num_words_secp224r1);
+ for (i = 0; i <= 6; i++) {
+ mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0,
+ pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */
+ mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0,
+ e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */
+ mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */
+ mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */
+ mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */
+ pow2i *= 2;
+ }
+}
+/* Compute a = sqrt(a) (mod curve_p). */
+/* Routine 3.2.8 mp_mod_sqrt_224; from
+ * http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve) {
+ (void) curve;
+ bitcount_t i;
+ mg_uecc_word_t e1[num_words_secp224r1];
+ mg_uecc_word_t f1[num_words_secp224r1];
+ mg_uecc_word_t d0[num_words_secp224r1];
+ mg_uecc_word_t e0[num_words_secp224r1];
+ mg_uecc_word_t f0[num_words_secp224r1];
+ mg_uecc_word_t d1[num_words_secp224r1];
+
+ /* s = a; using constant instead of random value */
+ mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */
+ mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0,
+ f0); /* RS (d1, e1, f1, d0, e0, f0) */
+ for (i = 1; i <= 95; i++) {
+ mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */
+ mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */
+ mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */
+ mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0,
+ f0); /* RS (d1, e1, f1, d0, e0, f0) */
+ if (mg_uecc_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */
+ break;
+ }
+ }
+ mg_uecc_vli_modInv(f1, e0, curve_secp224r1.p,
+ num_words_secp224r1); /* f1 <-- 1 / e0 */
+ mg_uecc_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */
+}
+#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+/* Computes result = product % curve_p
+ from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+#if MG_UECC_WORD_SIZE == 1
+static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) {
+ uint8_t tmp[num_words_secp224r1];
+ int8_t carry;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp224r1);
+
+ /* s1 */
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+ tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
+ tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
+ tmp[12] = product[28];
+ tmp[13] = product[29];
+ tmp[14] = product[30];
+ tmp[15] = product[31];
+ tmp[16] = product[32];
+ tmp[17] = product[33];
+ tmp[18] = product[34];
+ tmp[19] = product[35];
+ tmp[20] = product[36];
+ tmp[21] = product[37];
+ tmp[22] = product[38];
+ tmp[23] = product[39];
+ tmp[24] = product[40];
+ tmp[25] = product[41];
+ tmp[26] = product[42];
+ tmp[27] = product[43];
+ carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* s2 */
+ tmp[12] = product[44];
+ tmp[13] = product[45];
+ tmp[14] = product[46];
+ tmp[15] = product[47];
+ tmp[16] = product[48];
+ tmp[17] = product[49];
+ tmp[18] = product[50];
+ tmp[19] = product[51];
+ tmp[20] = product[52];
+ tmp[21] = product[53];
+ tmp[22] = product[54];
+ tmp[23] = product[55];
+ tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* d1 */
+ tmp[0] = product[28];
+ tmp[1] = product[29];
+ tmp[2] = product[30];
+ tmp[3] = product[31];
+ tmp[4] = product[32];
+ tmp[5] = product[33];
+ tmp[6] = product[34];
+ tmp[7] = product[35];
+ tmp[8] = product[36];
+ tmp[9] = product[37];
+ tmp[10] = product[38];
+ tmp[11] = product[39];
+ tmp[12] = product[40];
+ tmp[13] = product[41];
+ tmp[14] = product[42];
+ tmp[15] = product[43];
+ tmp[16] = product[44];
+ tmp[17] = product[45];
+ tmp[18] = product[46];
+ tmp[19] = product[47];
+ tmp[20] = product[48];
+ tmp[21] = product[49];
+ tmp[22] = product[50];
+ tmp[23] = product[51];
+ tmp[24] = product[52];
+ tmp[25] = product[53];
+ tmp[26] = product[54];
+ tmp[27] = product[55];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ /* d2 */
+ tmp[0] = product[44];
+ tmp[1] = product[45];
+ tmp[2] = product[46];
+ tmp[3] = product[47];
+ tmp[4] = product[48];
+ tmp[5] = product[49];
+ tmp[6] = product[50];
+ tmp[7] = product[51];
+ tmp[8] = product[52];
+ tmp[9] = product[53];
+ tmp[10] = product[54];
+ tmp[11] = product[55];
+ tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
+ tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
+ tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
+ tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ if (carry < 0) {
+ do {
+ carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
+ num_words_secp224r1);
+ } while (carry < 0);
+ } else {
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
+ num_words_secp224r1) != 1) {
+ carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p,
+ num_words_secp224r1);
+ }
+ }
+}
+#elif MG_UECC_WORD_SIZE == 4
+static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) {
+ uint32_t tmp[num_words_secp224r1];
+ int carry;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp224r1);
+
+ /* s1 */
+ tmp[0] = tmp[1] = tmp[2] = 0;
+ tmp[3] = product[7];
+ tmp[4] = product[8];
+ tmp[5] = product[9];
+ tmp[6] = product[10];
+ carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* s2 */
+ tmp[3] = product[11];
+ tmp[4] = product[12];
+ tmp[5] = product[13];
+ tmp[6] = 0;
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* d1 */
+ tmp[0] = product[7];
+ tmp[1] = product[8];
+ tmp[2] = product[9];
+ tmp[3] = product[10];
+ tmp[4] = product[11];
+ tmp[5] = product[12];
+ tmp[6] = product[13];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ /* d2 */
+ tmp[0] = product[11];
+ tmp[1] = product[12];
+ tmp[2] = product[13];
+ tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0;
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ if (carry < 0) {
+ do {
+ carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
+ num_words_secp224r1);
+ } while (carry < 0);
+ } else {
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
+ num_words_secp224r1) != 1) {
+ carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p,
+ num_words_secp224r1);
+ }
+ }
+}
+#else
+static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) {
+ uint64_t tmp[num_words_secp224r1];
+ int carry = 0;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp224r1);
+ result[num_words_secp224r1 - 1] &= 0xffffffff;
+
+ /* s1 */
+ tmp[0] = 0;
+ tmp[1] = product[3] & 0xffffffff00000000ull;
+ tmp[2] = product[4];
+ tmp[3] = product[5] & 0xffffffff;
+ mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* s2 */
+ tmp[1] = product[5] & 0xffffffff00000000ull;
+ tmp[2] = product[6];
+ tmp[3] = 0;
+ mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
+
+ /* d1 */
+ tmp[0] = (product[3] >> 32) | (product[4] << 32);
+ tmp[1] = (product[4] >> 32) | (product[5] << 32);
+ tmp[2] = (product[5] >> 32) | (product[6] << 32);
+ tmp[3] = product[6] >> 32;
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ /* d2 */
+ tmp[0] = (product[5] >> 32) | (product[6] << 32);
+ tmp[1] = product[6] >> 32;
+ tmp[2] = tmp[3] = 0;
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
+
+ if (carry < 0) {
+ do {
+ carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
+ num_words_secp224r1);
+ } while (carry < 0);
+ } else {
+ while (mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
+ num_words_secp224r1) != 1) {
+ mg_uecc_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1);
+ }
+ }
+}
+#endif /* MG_UECC_WORD_SIZE */
+#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */
+#endif /* MG_UECC_SUPPORTS_secp224r1 */
+#if MG_UECC_SUPPORTS_secp256r1
-#ifndef MG_MAX_SSI_DEPTH
-#define MG_MAX_SSI_DEPTH 5
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+static void vli_mmod_fast_secp256r1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product);
#endif
-#ifndef MG_SSI_BUFSIZ
-#define MG_SSI_BUFSIZ 1024
+static const struct MG_UECC_Curve_t curve_secp256r1 = {
+ num_words_secp256r1,
+ num_bytes_secp256r1,
+ 256, /* num_n_bits */
+ {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3),
+ BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4),
+ BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77),
+ BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8),
+ BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B),
+
+ BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB),
+ BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B),
+ BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E),
+ BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F)},
+ {BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B),
+ BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65),
+ BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3),
+ BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A)},
+ &double_jacobian_default,
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ &mod_sqrt_default,
+#endif
+ &x_side_default,
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ &vli_mmod_fast_secp256r1
#endif
+};
-#if MG_ENABLE_SSI
-static char *mg_ssi(const char *path, const char *root, int depth) {
- struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE};
- FILE *fp = fopen(path, "rb");
- if (fp != NULL) {
- char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)];
- int ch, intag = 0;
- size_t len = 0;
- buf[0] = arg[0] = '\0';
- while ((ch = fgetc(fp)) != EOF) {
- if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
- buf[len++] = (char) (ch & 0xff);
- buf[len] = '\0';
- if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
- char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10],
- *p = (char *) path + strlen(path), *data;
- while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
- 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));
- free(data);
- } else {
- MG_ERROR(("%s: file=%s error or too deep", path, arg));
- }
- } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
- char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
- mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
- if (depth < MG_MAX_SSI_DEPTH &&
- (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- mg_iobuf_add(&b, b.len, data, strlen(data));
- free(data);
- } else {
- MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
- }
- } else {
- // Unknown SSI tag
- MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
- mg_iobuf_add(&b, b.len, buf, len);
- }
- intag = 0;
- len = 0;
- } else if (ch == '<') {
- intag = 1;
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
- len = 0;
- buf[len++] = (char) (ch & 0xff);
- } else if (intag) {
- if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
- intag = 0;
- } else if (len >= sizeof(buf) - 2) {
- MG_ERROR(("%s: SSI tag is too large", path));
- len = 0;
- }
- buf[len++] = (char) (ch & 0xff);
- } else {
- buf[len++] = (char) (ch & 0xff);
- if (len >= sizeof(buf)) {
- mg_iobuf_add(&b, b.len, buf, len);
- len = 0;
- }
- }
+MG_UECC_Curve mg_uecc_secp256r1(void) {
+ return &curve_secp256r1;
+}
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1)
+/* Computes result = product % curve_p
+ from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+#if MG_UECC_WORD_SIZE == 1
+static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) {
+ uint8_t tmp[num_words_secp256r1];
+ int8_t carry;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp256r1);
+
+ /* s1 */
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+ tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
+ tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
+ tmp[12] = product[44];
+ tmp[13] = product[45];
+ tmp[14] = product[46];
+ tmp[15] = product[47];
+ tmp[16] = product[48];
+ tmp[17] = product[49];
+ tmp[18] = product[50];
+ tmp[19] = product[51];
+ tmp[20] = product[52];
+ tmp[21] = product[53];
+ tmp[22] = product[54];
+ tmp[23] = product[55];
+ tmp[24] = product[56];
+ tmp[25] = product[57];
+ tmp[26] = product[58];
+ tmp[27] = product[59];
+ tmp[28] = product[60];
+ tmp[29] = product[61];
+ tmp[30] = product[62];
+ tmp[31] = product[63];
+ carry = mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s2 */
+ tmp[12] = product[48];
+ tmp[13] = product[49];
+ tmp[14] = product[50];
+ tmp[15] = product[51];
+ tmp[16] = product[52];
+ tmp[17] = product[53];
+ tmp[18] = product[54];
+ tmp[19] = product[55];
+ tmp[20] = product[56];
+ tmp[21] = product[57];
+ tmp[22] = product[58];
+ tmp[23] = product[59];
+ tmp[24] = product[60];
+ tmp[25] = product[61];
+ tmp[26] = product[62];
+ tmp[27] = product[63];
+ tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0;
+ carry += mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s3 */
+ tmp[0] = product[32];
+ tmp[1] = product[33];
+ tmp[2] = product[34];
+ tmp[3] = product[35];
+ tmp[4] = product[36];
+ tmp[5] = product[37];
+ tmp[6] = product[38];
+ tmp[7] = product[39];
+ tmp[8] = product[40];
+ tmp[9] = product[41];
+ tmp[10] = product[42];
+ tmp[11] = product[43];
+ tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
+ tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
+ tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
+ tmp[24] = product[56];
+ tmp[25] = product[57];
+ tmp[26] = product[58];
+ tmp[27] = product[59];
+ tmp[28] = product[60];
+ tmp[29] = product[61];
+ tmp[30] = product[62];
+ tmp[31] = product[63];
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s4 */
+ tmp[0] = product[36];
+ tmp[1] = product[37];
+ tmp[2] = product[38];
+ tmp[3] = product[39];
+ tmp[4] = product[40];
+ tmp[5] = product[41];
+ tmp[6] = product[42];
+ tmp[7] = product[43];
+ tmp[8] = product[44];
+ tmp[9] = product[45];
+ tmp[10] = product[46];
+ tmp[11] = product[47];
+ tmp[12] = product[52];
+ tmp[13] = product[53];
+ tmp[14] = product[54];
+ tmp[15] = product[55];
+ tmp[16] = product[56];
+ tmp[17] = product[57];
+ tmp[18] = product[58];
+ tmp[19] = product[59];
+ tmp[20] = product[60];
+ tmp[21] = product[61];
+ tmp[22] = product[62];
+ tmp[23] = product[63];
+ tmp[24] = product[52];
+ tmp[25] = product[53];
+ tmp[26] = product[54];
+ tmp[27] = product[55];
+ tmp[28] = product[32];
+ tmp[29] = product[33];
+ tmp[30] = product[34];
+ tmp[31] = product[35];
+ carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* d1 */
+ tmp[0] = product[44];
+ tmp[1] = product[45];
+ tmp[2] = product[46];
+ tmp[3] = product[47];
+ tmp[4] = product[48];
+ tmp[5] = product[49];
+ tmp[6] = product[50];
+ tmp[7] = product[51];
+ tmp[8] = product[52];
+ tmp[9] = product[53];
+ tmp[10] = product[54];
+ tmp[11] = product[55];
+ tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
+ tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
+ tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
+ tmp[24] = product[32];
+ tmp[25] = product[33];
+ tmp[26] = product[34];
+ tmp[27] = product[35];
+ tmp[28] = product[40];
+ tmp[29] = product[41];
+ tmp[30] = product[42];
+ tmp[31] = product[43];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d2 */
+ tmp[0] = product[48];
+ tmp[1] = product[49];
+ tmp[2] = product[50];
+ tmp[3] = product[51];
+ tmp[4] = product[52];
+ tmp[5] = product[53];
+ tmp[6] = product[54];
+ tmp[7] = product[55];
+ tmp[8] = product[56];
+ tmp[9] = product[57];
+ tmp[10] = product[58];
+ tmp[11] = product[59];
+ tmp[12] = product[60];
+ tmp[13] = product[61];
+ tmp[14] = product[62];
+ tmp[15] = product[63];
+ tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
+ tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
+ tmp[24] = product[36];
+ tmp[25] = product[37];
+ tmp[26] = product[38];
+ tmp[27] = product[39];
+ tmp[28] = product[44];
+ tmp[29] = product[45];
+ tmp[30] = product[46];
+ tmp[31] = product[47];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d3 */
+ tmp[0] = product[52];
+ tmp[1] = product[53];
+ tmp[2] = product[54];
+ tmp[3] = product[55];
+ tmp[4] = product[56];
+ tmp[5] = product[57];
+ tmp[6] = product[58];
+ tmp[7] = product[59];
+ tmp[8] = product[60];
+ tmp[9] = product[61];
+ tmp[10] = product[62];
+ tmp[11] = product[63];
+ tmp[12] = product[32];
+ tmp[13] = product[33];
+ tmp[14] = product[34];
+ tmp[15] = product[35];
+ tmp[16] = product[36];
+ tmp[17] = product[37];
+ tmp[18] = product[38];
+ tmp[19] = product[39];
+ tmp[20] = product[40];
+ tmp[21] = product[41];
+ tmp[22] = product[42];
+ tmp[23] = product[43];
+ tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
+ tmp[28] = product[48];
+ tmp[29] = product[49];
+ tmp[30] = product[50];
+ tmp[31] = product[51];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d4 */
+ tmp[0] = product[56];
+ tmp[1] = product[57];
+ tmp[2] = product[58];
+ tmp[3] = product[59];
+ tmp[4] = product[60];
+ tmp[5] = product[61];
+ tmp[6] = product[62];
+ tmp[7] = product[63];
+ tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
+ tmp[12] = product[36];
+ tmp[13] = product[37];
+ tmp[14] = product[38];
+ tmp[15] = product[39];
+ tmp[16] = product[40];
+ tmp[17] = product[41];
+ tmp[18] = product[42];
+ tmp[19] = product[43];
+ tmp[20] = product[44];
+ tmp[21] = product[45];
+ tmp[22] = product[46];
+ tmp[23] = product[47];
+ tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
+ tmp[28] = product[52];
+ tmp[29] = product[53];
+ tmp[30] = product[54];
+ tmp[31] = product[55];
+ carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ if (carry < 0) {
+ do {
+ carry += mg_uecc_vli_add(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
+ } while (carry < 0);
+ } else {
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
+ num_words_secp256r1) != 1) {
+ carry -= mg_uecc_vli_sub(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
}
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
- if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
- fclose(fp);
}
- (void) depth;
- (void) root;
- return (char *) b.buf;
}
-
-void mg_http_serve_ssi(struct mg_connection *c, const char *root,
- const char *fullpath) {
- const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
- char *data = mg_ssi(fullpath, root, 0);
- mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
- free(data);
+#elif MG_UECC_WORD_SIZE == 4
+static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) {
+ uint32_t tmp[num_words_secp256r1];
+ int carry;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp256r1);
+
+ /* s1 */
+ tmp[0] = tmp[1] = tmp[2] = 0;
+ tmp[3] = product[11];
+ tmp[4] = product[12];
+ tmp[5] = product[13];
+ tmp[6] = product[14];
+ tmp[7] = product[15];
+ carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s2 */
+ tmp[3] = product[12];
+ tmp[4] = product[13];
+ tmp[5] = product[14];
+ tmp[6] = product[15];
+ tmp[7] = 0;
+ carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s3 */
+ tmp[0] = product[8];
+ tmp[1] = product[9];
+ tmp[2] = product[10];
+ tmp[3] = tmp[4] = tmp[5] = 0;
+ tmp[6] = product[14];
+ tmp[7] = product[15];
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s4 */
+ tmp[0] = product[9];
+ tmp[1] = product[10];
+ tmp[2] = product[11];
+ tmp[3] = product[13];
+ tmp[4] = product[14];
+ tmp[5] = product[15];
+ tmp[6] = product[13];
+ tmp[7] = product[8];
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* d1 */
+ tmp[0] = product[11];
+ tmp[1] = product[12];
+ tmp[2] = product[13];
+ tmp[3] = tmp[4] = tmp[5] = 0;
+ tmp[6] = product[8];
+ tmp[7] = product[10];
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d2 */
+ tmp[0] = product[12];
+ tmp[1] = product[13];
+ tmp[2] = product[14];
+ tmp[3] = product[15];
+ tmp[4] = tmp[5] = 0;
+ tmp[6] = product[9];
+ tmp[7] = product[11];
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d3 */
+ tmp[0] = product[13];
+ tmp[1] = product[14];
+ tmp[2] = product[15];
+ tmp[3] = product[8];
+ tmp[4] = product[9];
+ tmp[5] = product[10];
+ tmp[6] = 0;
+ tmp[7] = product[12];
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d4 */
+ tmp[0] = product[14];
+ tmp[1] = product[15];
+ tmp[2] = 0;
+ tmp[3] = product[9];
+ tmp[4] = product[10];
+ tmp[5] = product[11];
+ tmp[6] = 0;
+ tmp[7] = product[13];
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ if (carry < 0) {
+ do {
+ carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
+ } while (carry < 0);
+ } else {
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
+ num_words_secp256r1) != 1) {
+ carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
+ }
+ }
}
#else
-void mg_http_serve_ssi(struct mg_connection *c, const char *root,
- const char *fullpath) {
- mg_http_reply(c, 501, NULL, "SSI not enabled");
- (void) root, (void) fullpath;
+static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) {
+ uint64_t tmp[num_words_secp256r1];
+ int carry;
+
+ /* t */
+ mg_uecc_vli_set(result, product, num_words_secp256r1);
+
+ /* s1 */
+ tmp[0] = 0;
+ tmp[1] = product[5] & 0xffffffff00000000U;
+ tmp[2] = product[6];
+ tmp[3] = product[7];
+ carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s2 */
+ tmp[1] = product[6] << 32;
+ tmp[2] = (product[6] >> 32) | (product[7] << 32);
+ tmp[3] = product[7] >> 32;
+ carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s3 */
+ tmp[0] = product[4];
+ tmp[1] = product[5] & 0xffffffff;
+ tmp[2] = 0;
+ tmp[3] = product[7];
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* s4 */
+ tmp[0] = (product[4] >> 32) | (product[5] << 32);
+ tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000U);
+ tmp[2] = product[7];
+ tmp[3] = (product[6] >> 32) | (product[4] << 32);
+ carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
+
+ /* d1 */
+ tmp[0] = (product[5] >> 32) | (product[6] << 32);
+ tmp[1] = (product[6] >> 32);
+ tmp[2] = 0;
+ tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d2 */
+ tmp[0] = product[6];
+ tmp[1] = product[7];
+ tmp[2] = 0;
+ tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000);
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d3 */
+ tmp[0] = (product[6] >> 32) | (product[7] << 32);
+ tmp[1] = (product[7] >> 32) | (product[4] << 32);
+ tmp[2] = (product[4] >> 32) | (product[5] << 32);
+ tmp[3] = (product[6] << 32);
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ /* d4 */
+ tmp[0] = product[7];
+ tmp[1] = product[4] & 0xffffffff00000000U;
+ tmp[2] = product[5];
+ tmp[3] = product[6] & 0xffffffff00000000U;
+ carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
+
+ if (carry < 0) {
+ do {
+ carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
+ } while (carry < 0);
+ } else {
+ while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
+ num_words_secp256r1) != 1) {
+ carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p,
+ num_words_secp256r1);
+ }
+ }
}
-#endif
+#endif /* MG_UECC_WORD_SIZE */
+#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */
-#ifdef MG_ENABLE_LINES
-#line 1 "src/str.c"
+#endif /* MG_UECC_SUPPORTS_secp256r1 */
+
+#if MG_UECC_SUPPORTS_secp256k1
+
+static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *Z1, MG_UECC_Curve curve);
+static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x,
+ MG_UECC_Curve curve);
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product);
#endif
+static const struct MG_UECC_Curve_t curve_secp256k1 = {
+ num_words_secp256k1,
+ num_bytes_secp256k1,
+ 256, /* num_n_bits */
+ {BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF),
+ BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA),
+ BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF),
+ BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
+ {BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59),
+ BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02),
+ BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55),
+ BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79),
+
+ BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C),
+ BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD),
+ BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D),
+ BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48)},
+ {BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
+ BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00)},
+ &double_jacobian_secp256k1,
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+ &mod_sqrt_default,
+#endif
+ &x_side_secp256k1,
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ &vli_mmod_fast_secp256k1
+#endif
+};
-struct mg_str mg_str_s(const char *s) {
- struct mg_str str = {s, s == NULL ? 0 : strlen(s)};
- return str;
+MG_UECC_Curve mg_uecc_secp256k1(void) {
+ return &curve_secp256k1;
}
-struct mg_str mg_str_n(const char *s, size_t n) {
- struct mg_str str = {s, n};
- return str;
-}
+/* Double in place */
+static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *Z1, MG_UECC_Curve curve) {
+ /* t1 = X, t2 = Y, t3 = Z */
+ mg_uecc_word_t t4[num_words_secp256k1];
+ mg_uecc_word_t t5[num_words_secp256k1];
-int mg_lower(const char *s) {
- int c = *s;
- if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
- return c;
-}
+ if (mg_uecc_vli_isZero(Z1, num_words_secp256k1)) {
+ return;
+ }
-int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
- int diff = 0;
- if (len > 0) do {
- diff = mg_lower(s1++) - mg_lower(s2++);
- } while (diff == 0 && s1[-1] != '\0' && --len > 0);
- return diff;
+ mg_uecc_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */
+ mg_uecc_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */
+ mg_uecc_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */
+ mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */
+ mg_uecc_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */
+
+ mg_uecc_vli_modAdd(Y1, X1, X1, curve->p,
+ num_words_secp256k1); /* t2 = 2*x1^2 */
+ mg_uecc_vli_modAdd(Y1, Y1, X1, curve->p,
+ num_words_secp256k1); /* t2 = 3*x1^2 */
+ if (mg_uecc_vli_testBit(Y1, 0)) {
+ mg_uecc_word_t carry =
+ mg_uecc_vli_add(Y1, Y1, curve->p, num_words_secp256k1);
+ mg_uecc_vli_rshift1(Y1, num_words_secp256k1);
+ Y1[num_words_secp256k1 - 1] |= carry << (MG_UECC_WORD_BITS - 1);
+ } else {
+ mg_uecc_vli_rshift1(Y1, num_words_secp256k1);
+ }
+ /* t2 = 3/2*(x1^2) = B */
+
+ mg_uecc_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */
+ mg_uecc_vli_modSub(X1, X1, t4, curve->p,
+ num_words_secp256k1); /* t1 = B^2 - A */
+ mg_uecc_vli_modSub(X1, X1, t4, curve->p,
+ num_words_secp256k1); /* t1 = B^2 - 2A = x3 */
+
+ mg_uecc_vli_modSub(t4, t4, X1, curve->p,
+ num_words_secp256k1); /* t4 = A - x3 */
+ mg_uecc_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */
+ mg_uecc_vli_modSub(Y1, Y1, t5, curve->p,
+ num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */
+}
+
+/* Computes result = x^3 + b. result must not overlap x. */
+static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x,
+ MG_UECC_Curve curve) {
+ mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */
+ mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 */
+ mg_uecc_vli_modAdd(result, result, curve->b, curve->p,
+ num_words_secp256k1); /* r = x^3 + b */
+}
+
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1)
+static void omega_mult_secp256k1(mg_uecc_word_t *result,
+ const mg_uecc_word_t *right);
+static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result,
+ mg_uecc_word_t *product) {
+ mg_uecc_word_t tmp[2 * num_words_secp256k1];
+ mg_uecc_word_t carry;
+
+ mg_uecc_vli_clear(tmp, num_words_secp256k1);
+ mg_uecc_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1);
+
+ omega_mult_secp256k1(tmp,
+ product + num_words_secp256k1); /* (Rq, q) = q * c */
+
+ carry = mg_uecc_vli_add(result, product, tmp,
+ num_words_secp256k1); /* (C, r) = r + q */
+ mg_uecc_vli_clear(product, num_words_secp256k1);
+ omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */
+ carry += mg_uecc_vli_add(result, result, product,
+ num_words_secp256k1); /* (C1, r) = r + Rq*c */
+
+ while (carry > 0) {
+ --carry;
+ mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1);
+ }
+ if (mg_uecc_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) >
+ 0) {
+ mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1);
+ }
}
-int mg_casecmp(const char *s1, const char *s2) {
- return mg_ncasecmp(s1, s2, (size_t) ~0);
+#if MG_UECC_WORD_SIZE == 1
+static void omega_mult_secp256k1(uint8_t *result, const uint8_t *right) {
+ /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
+ mg_uecc_word_t r0 = 0;
+ mg_uecc_word_t r1 = 0;
+ mg_uecc_word_t r2 = 0;
+ wordcount_t k;
+
+ /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
+ muladd(0xD1, right[0], &r0, &r1, &r2);
+ result[0] = r0;
+ r0 = r1;
+ r1 = r2;
+ /* r2 is still 0 */
+
+ for (k = 1; k < num_words_secp256k1; ++k) {
+ muladd(0x03, right[k - 1], &r0, &r1, &r2);
+ muladd(0xD1, right[k], &r0, &r1, &r2);
+ result[k] = r0;
+ r0 = r1;
+ r1 = r2;
+ r2 = 0;
+ }
+ muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2);
+ result[num_words_secp256k1] = r0;
+ result[num_words_secp256k1 + 1] = r1;
+ /* add the 2^32 multiple */
+ result[4 + num_words_secp256k1] =
+ mg_uecc_vli_add(result + 4, result + 4, right, num_words_secp256k1);
+}
+#elif MG_UECC_WORD_SIZE == 4
+static void omega_mult_secp256k1(uint32_t *result, const uint32_t *right) {
+ /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
+ uint32_t carry = 0;
+ wordcount_t k;
+
+ for (k = 0; k < num_words_secp256k1; ++k) {
+ uint64_t p = (uint64_t) 0x3D1 * right[k] + carry;
+ result[k] = (uint32_t) p;
+ carry = p >> 32;
+ }
+ result[num_words_secp256k1] = carry;
+ /* add the 2^32 multiple */
+ result[1 + num_words_secp256k1] =
+ mg_uecc_vli_add(result + 1, result + 1, right, num_words_secp256k1);
}
-
-int mg_vcmp(const struct mg_str *s1, const char *s2) {
- size_t n2 = strlen(s2), n1 = s1->len;
- int r = strncmp(s1->ptr, s2, (n1 < n2) ? n1 : n2);
- if (r == 0) return (int) (n1 - n2);
- return r;
+#else
+static void omega_mult_secp256k1(uint64_t *result, const uint64_t *right) {
+ mg_uecc_word_t r0 = 0;
+ mg_uecc_word_t r1 = 0;
+ mg_uecc_word_t r2 = 0;
+ wordcount_t k;
+
+ /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
+ for (k = 0; k < num_words_secp256k1; ++k) {
+ muladd(0x1000003D1ull, right[k], &r0, &r1, &r2);
+ result[k] = r0;
+ r0 = r1;
+ r1 = r2;
+ r2 = 0;
+ }
+ result[num_words_secp256k1] = r0;
}
+#endif /* MG_UECC_WORD_SIZE */
+#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */
-int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
- size_t n2 = strlen(str2), n1 = str1->len;
- int r = mg_ncasecmp(str1->ptr, str2, (n1 < n2) ? n1 : n2);
- if (r == 0) return (int) (n1 - n2);
- return r;
+#endif /* MG_UECC_SUPPORTS_secp256k1 */
+
+#endif /* _UECC_CURVE_SPECIFIC_H_ */
+
+/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */
+#define EccPoint_isZero(point, curve) \
+ mg_uecc_vli_isZero((point), (wordcount_t) ((curve)->num_words * 2))
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z
+coordinates. From http://eprint.iacr.org/2011/338.pdf
+*/
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static void apply_z(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ const mg_uecc_word_t *const Z, MG_UECC_Curve curve) {
+ mg_uecc_word_t t1[MG_UECC_MAX_WORDS];
+
+ mg_uecc_vli_modSquare_fast(t1, Z, curve); /* z^2 */
+ mg_uecc_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */
+ mg_uecc_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */
+ mg_uecc_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */
}
-struct mg_str mg_strdup(const struct mg_str s) {
- struct mg_str r = {NULL, 0};
- if (s.len > 0 && s.ptr != NULL) {
- char *sc = (char *) calloc(1, s.len + 1);
- if (sc != NULL) {
- memcpy(sc, s.ptr, s.len);
- sc[s.len] = '\0';
- r.ptr = sc;
- r.len = s.len;
- }
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static void XYcZ_initial_double(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *X2, mg_uecc_word_t *Y2,
+ const mg_uecc_word_t *const initial_Z,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t z[MG_UECC_MAX_WORDS];
+ wordcount_t num_words = curve->num_words;
+ if (initial_Z) {
+ mg_uecc_vli_set(z, initial_Z, num_words);
+ } else {
+ mg_uecc_vli_clear(z, num_words);
+ z[0] = 1;
}
- return r;
-}
-int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
- size_t i = 0;
- while (i < str1.len && i < str2.len) {
- int c1 = str1.ptr[i];
- int c2 = str2.ptr[i];
- if (c1 < c2) return -1;
- if (c1 > c2) return 1;
- i++;
+ mg_uecc_vli_set(X2, X1, num_words);
+ mg_uecc_vli_set(Y2, Y1, num_words);
+
+ apply_z(X1, Y1, z, curve);
+ curve->double_jacobian(X1, Y1, z, curve);
+ apply_z(X2, Y2, z, curve);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+ or P => P', Q => P + Q
+*/
+static void XYcZ_add(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, mg_uecc_word_t *X2,
+ mg_uecc_word_t *Y2, MG_UECC_Curve curve) {
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0};
+ wordcount_t num_words = curve->num_words;
+
+ mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */
+ mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */
+ mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */
+ mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */
+ mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */
+ mg_uecc_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */
+
+ mg_uecc_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */
+ mg_uecc_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */
+ mg_uecc_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */
+ mg_uecc_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */
+ mg_uecc_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */
+ mg_uecc_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */
+ mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */
+
+ mg_uecc_vli_set(X2, t5, num_words);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+ or P => P - Q, Q => P + Q
+*/
+static void XYcZ_addC(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
+ mg_uecc_word_t *X2, mg_uecc_word_t *Y2,
+ MG_UECC_Curve curve) {
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0};
+ mg_uecc_word_t t6[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t t7[MG_UECC_MAX_WORDS];
+ wordcount_t num_words = curve->num_words;
+
+ mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */
+ mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */
+ mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */
+ mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */
+ mg_uecc_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */
+ mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */
+
+ mg_uecc_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */
+ mg_uecc_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */
+ mg_uecc_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */
+ mg_uecc_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */
+ mg_uecc_vli_modSub(X2, X2, t6, curve->p,
+ num_words); /* t3 = D - (B + C) = x3 */
+
+ mg_uecc_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */
+ mg_uecc_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */
+ mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p,
+ num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */
+
+ mg_uecc_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */
+ mg_uecc_vli_modSub(t7, t7, t6, curve->p,
+ num_words); /* t7 = F - (B + C) = x3' */
+ mg_uecc_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */
+ mg_uecc_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */
+ mg_uecc_vli_modSub(Y1, t6, Y1, curve->p,
+ num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */
+
+ mg_uecc_vli_set(X1, t7, num_words);
+}
+
+/* result may overlap point. */
+static void EccPoint_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
+ const mg_uecc_word_t *scalar,
+ const mg_uecc_word_t *initial_Z, bitcount_t num_bits,
+ MG_UECC_Curve curve) {
+ /* R0 and R1 */
+ mg_uecc_word_t Rx[2][MG_UECC_MAX_WORDS];
+ mg_uecc_word_t Ry[2][MG_UECC_MAX_WORDS];
+ mg_uecc_word_t z[MG_UECC_MAX_WORDS];
+ bitcount_t i;
+ mg_uecc_word_t nb;
+ wordcount_t num_words = curve->num_words;
+
+ mg_uecc_vli_set(Rx[1], point, num_words);
+ mg_uecc_vli_set(Ry[1], point + num_words, num_words);
+
+ XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve);
+
+ for (i = num_bits - 2; i > 0; --i) {
+ nb = !mg_uecc_vli_testBit(scalar, i);
+ XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve);
+ XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve);
+ }
+
+ nb = !mg_uecc_vli_testBit(scalar, 0);
+ XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve);
+
+ /* Find final 1/Z value. */
+ mg_uecc_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */
+ mg_uecc_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */
+ mg_uecc_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */
+ mg_uecc_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */
+ /* yP / (xP * Yb * (X1 - X0)) */
+ mg_uecc_vli_modMult_fast(z, z, point + num_words, curve);
+ mg_uecc_vli_modMult_fast(z, z, Rx[1 - nb],
+ curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */
+ /* End 1/Z calculation */
+
+ XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve);
+ apply_z(Rx[0], Ry[0], z, curve);
+
+ mg_uecc_vli_set(result, Rx[0], num_words);
+ mg_uecc_vli_set(result + num_words, Ry[0], num_words);
+}
+
+static mg_uecc_word_t regularize_k(const mg_uecc_word_t *const k,
+ mg_uecc_word_t *k0, mg_uecc_word_t *k1,
+ MG_UECC_Curve curve) {
+ wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
+ bitcount_t num_n_bits = curve->num_n_bits;
+ mg_uecc_word_t carry =
+ mg_uecc_vli_add(k0, k, curve->n, num_n_words) ||
+ (num_n_bits < ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8) &&
+ mg_uecc_vli_testBit(k0, num_n_bits));
+ mg_uecc_vli_add(k1, k0, curve->n, num_n_words);
+ return carry;
+}
+
+/* Generates a random integer in the range 0 < random < top.
+ Both random and top have num_words words. */
+MG_UECC_VLI_API int mg_uecc_generate_random_int(mg_uecc_word_t *random,
+ const mg_uecc_word_t *top,
+ wordcount_t num_words) {
+ mg_uecc_word_t mask = (mg_uecc_word_t) -1;
+ mg_uecc_word_t tries;
+ bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words);
+
+ if (!g_rng_function) {
+ return 0;
+ }
+
+ for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
+ if (!g_rng_function((uint8_t *) random,
+ (unsigned int) (num_words * MG_UECC_WORD_SIZE))) {
+ return 0;
+ }
+ random[num_words - 1] &=
+ mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits));
+ if (!mg_uecc_vli_isZero(random, num_words) &&
+ mg_uecc_vli_cmp(top, random, num_words) == 1) {
+ return 1;
+ }
}
- if (i < str1.len) return 1;
- if (i < str2.len) return -1;
return 0;
}
-const char *mg_strstr(const struct mg_str haystack,
- const struct mg_str needle) {
- size_t i;
- if (needle.len > haystack.len) return NULL;
- for (i = 0; i <= haystack.len - needle.len; i++) {
- if (memcmp(haystack.ptr + i, needle.ptr, needle.len) == 0) {
- return haystack.ptr + i;
+static mg_uecc_word_t EccPoint_compute_public_key(mg_uecc_word_t *result,
+ mg_uecc_word_t *private_key,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t *p2[2] = {tmp1, tmp2};
+ mg_uecc_word_t *initial_Z = 0;
+ mg_uecc_word_t carry;
+
+ /* Regularize the bitcount for the private key so that attackers cannot use a
+ side channel attack to learn the number of leading zeros. */
+ carry = regularize_k(private_key, tmp1, tmp2, curve);
+
+ /* If an RNG function was specified, try to get a random initial Z value to
+ improve protection against side-channel attacks. */
+ if (g_rng_function) {
+ if (!mg_uecc_generate_random_int(p2[carry], curve->p, curve->num_words)) {
+ return 0;
}
+ initial_Z = p2[carry];
}
- return NULL;
+ EccPoint_mult(result, curve->G, p2[!carry], initial_Z,
+ (bitcount_t) (curve->num_n_bits + 1), curve);
+
+ if (EccPoint_isZero(result, curve)) {
+ return 0;
+ }
+ return 1;
}
-static bool is_space(int c) {
- return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+#if MG_UECC_WORD_SIZE == 1
+
+MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
+ const uint8_t *native) {
+ wordcount_t i;
+ for (i = 0; i < num_bytes; ++i) {
+ bytes[i] = native[(num_bytes - 1) - i];
+ }
}
-struct mg_str mg_strstrip(struct mg_str s) {
- while (s.len > 0 && is_space((int) *s.ptr)) s.ptr++, s.len--;
- while (s.len > 0 && is_space((int) *(s.ptr + s.len - 1))) s.len--;
- return s;
+MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(uint8_t *native,
+ const uint8_t *bytes,
+ int num_bytes) {
+ mg_uecc_vli_nativeToBytes(native, num_bytes, bytes);
}
-bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
- size_t i = 0, j = 0, ni = 0, nj = 0;
- if (caps) caps->ptr = NULL, caps->len = 0;
- while (i < p.len || j < s.len) {
- if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) {
- if (caps == NULL) {
- } else if (p.ptr[i] == '?') {
- caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap
- caps++, caps->ptr = NULL, caps->len = 0; // Init next cap
- } else if (caps->ptr != NULL && caps->len == 0) {
- caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap
- caps++, caps->len = 0, caps->ptr = NULL; // Init next cap
- }
- i++, j++;
- } else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) {
- if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap
- ni = i++, nj = j + 1;
- } else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) {
- i = ni, j = nj;
- if (caps && caps->ptr == NULL && caps->len == 0) {
- caps--, caps->len = 0; // Restart previous cap
- }
- } else {
- return false;
- }
- }
- if (caps && caps->ptr && caps->len == 0) {
- caps->len = (size_t) (&s.ptr[j] - caps->ptr);
+#else
+
+MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
+ const mg_uecc_word_t *native) {
+ int i;
+ for (i = 0; i < num_bytes; ++i) {
+ unsigned b = (unsigned) (num_bytes - 1 - i);
+ bytes[i] = (uint8_t) (native[b / MG_UECC_WORD_SIZE] >>
+ (8 * (b % MG_UECC_WORD_SIZE)));
}
- return true;
}
-bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
- return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
+MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native,
+ const uint8_t *bytes,
+ int num_bytes) {
+ int i;
+ mg_uecc_vli_clear(native,
+ (wordcount_t) ((num_bytes + (MG_UECC_WORD_SIZE - 1)) /
+ MG_UECC_WORD_SIZE));
+ for (i = 0; i < num_bytes; ++i) {
+ unsigned b = (unsigned) (num_bytes - 1 - i);
+ native[b / MG_UECC_WORD_SIZE] |= (mg_uecc_word_t) bytes[i]
+ << (8 * (b % MG_UECC_WORD_SIZE));
+ }
}
-static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
- size_t *klen, size_t *voff, size_t *vlen, char delim) {
- size_t kvlen, kl;
- for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != delim;) kvlen++;
- for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
- if (koff != NULL) *koff = ofs;
- if (klen != NULL) *klen = kl;
- if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
- if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
- ofs += kvlen + 1;
- return ofs > n ? n : ofs;
+#endif /* MG_UECC_WORD_SIZE */
+
+int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key,
+ MG_UECC_Curve curve) {
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key;
+ mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
+#else
+ mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
+#endif
+ mg_uecc_word_t tries;
+
+ for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
+ if (!mg_uecc_generate_random_int(_private, curve->n,
+ BITS_TO_WORDS(curve->num_n_bits))) {
+ return 0;
+ }
+
+ if (EccPoint_compute_public_key(_public, _private, curve)) {
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits),
+ _private);
+ mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public);
+ mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes,
+ _public + curve->num_words);
+#endif
+ return 1;
+ }
+ }
+ return 0;
}
-bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char sep) {
- size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
- if (s->ptr == NULL || s->len == 0) return 0;
- off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen, sep);
- if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
- if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
- *s = mg_str_n(s->ptr + off, s->len - off);
- return off > 0;
+int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
+ uint8_t *secret, MG_UECC_Curve curve) {
+ mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
+ mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
+
+ mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t *p2[2] = {_private, tmp};
+ mg_uecc_word_t *initial_Z = 0;
+ mg_uecc_word_t carry;
+ wordcount_t num_words = curve->num_words;
+ wordcount_t num_bytes = curve->num_bytes;
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) _private, private_key, num_bytes);
+ bcopy((uint8_t *) _public, public_key, num_bytes * 2);
+#else
+ mg_uecc_vli_bytesToNative(_private, private_key,
+ BITS_TO_BYTES(curve->num_n_bits));
+ mg_uecc_vli_bytesToNative(_public, public_key, num_bytes);
+ mg_uecc_vli_bytesToNative(_public + num_words, public_key + num_bytes,
+ num_bytes);
+#endif
+
+ /* Regularize the bitcount for the private key so that attackers cannot use a
+ side channel attack to learn the number of leading zeros. */
+ carry = regularize_k(_private, _private, tmp, curve);
+
+ /* If an RNG function was specified, try to get a random initial Z value to
+ improve protection against side-channel attacks. */
+ if (g_rng_function) {
+ if (!mg_uecc_generate_random_int(p2[carry], curve->p, num_words)) {
+ return 0;
+ }
+ initial_Z = p2[carry];
+ }
+
+ EccPoint_mult(_public, _public, p2[!carry], initial_Z,
+ (bitcount_t) (curve->num_n_bits + 1), curve);
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes);
+#else
+ mg_uecc_vli_nativeToBytes(secret, num_bytes, _public);
+#endif
+ return !EccPoint_isZero(_public, curve);
}
-bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
- return mg_split(s, k, v, ',');
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed,
+ MG_UECC_Curve curve) {
+ wordcount_t i;
+ for (i = 0; i < curve->num_bytes; ++i) {
+ compressed[i + 1] = public_key[i];
+ }
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01);
+#else
+ compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01);
+#endif
}
-char *mg_hex(const void *buf, size_t len, char *to) {
- const unsigned char *p = (const unsigned char *) buf;
- const char *hex = "0123456789abcdef";
- size_t i = 0;
- for (; len--; p++) {
- to[i++] = hex[p[0] >> 4];
- to[i++] = hex[p[0] & 0x0f];
+void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key,
+ MG_UECC_Curve curve) {
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *point = (mg_uecc_word_t *) public_key;
+#else
+ mg_uecc_word_t point[MG_UECC_MAX_WORDS * 2];
+#endif
+ mg_uecc_word_t *y = point + curve->num_words;
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy(public_key, compressed + 1, curve->num_bytes);
+#else
+ mg_uecc_vli_bytesToNative(point, compressed + 1, curve->num_bytes);
+#endif
+ curve->x_side(y, point, curve);
+ curve->mod_sqrt(y, curve);
+
+ if ((uint8_t) (y[0] & 0x01) != (compressed[0] & 0x01)) {
+ mg_uecc_vli_sub(y, curve->p, y, curve->num_words);
}
- to[i] = '\0';
- return to;
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, point);
+ mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y);
+#endif
}
+#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
+
+MG_UECC_VLI_API int mg_uecc_valid_point(const mg_uecc_word_t *point,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
+ wordcount_t num_words = curve->num_words;
+
+ /* The point at infinity is invalid. */
+ if (EccPoint_isZero(point, curve)) {
+ return 0;
+ }
-static unsigned char mg_unhex_nimble(unsigned char c) {
- return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
- : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
- : (unsigned char) (c - 'W');
+ /* x and y must be smaller than p. */
+ if (mg_uecc_vli_cmp_unsafe(curve->p, point, num_words) != 1 ||
+ mg_uecc_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) {
+ return 0;
+ }
+
+ mg_uecc_vli_modSquare_fast(tmp1, point + num_words, curve);
+ curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */
+
+ /* Make sure that y^2 == x^3 + ax + b */
+ return (int) (mg_uecc_vli_equal(tmp1, tmp2, num_words));
}
-unsigned long mg_unhexn(const char *s, size_t len) {
- unsigned long i = 0, v = 0;
- for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
- return v;
+int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve) {
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
+#else
+ mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
+#endif
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes);
+ mg_uecc_vli_bytesToNative(_public + curve->num_words,
+ public_key + curve->num_bytes, curve->num_bytes);
+#endif
+ return mg_uecc_valid_point(_public, curve);
}
-void mg_unhex(const char *buf, size_t len, unsigned char *to) {
- size_t i;
- for (i = 0; i < len; i += 2) {
- to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2);
+int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key,
+ MG_UECC_Curve curve) {
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key;
+ mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
+#else
+ mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
+#endif
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_bytesToNative(_private, private_key,
+ BITS_TO_BYTES(curve->num_n_bits));
+#endif
+
+ /* Make sure the private key is in the range [1, n-1]. */
+ if (mg_uecc_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) {
+ return 0;
}
-}
-uint64_t mg_tou64(struct mg_str str) {
- uint64_t result = 0;
- size_t i = 0;
- while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
- while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- result *= 10;
- result += (unsigned) (str.ptr[i] - '0');
- i++;
+ if (mg_uecc_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) !=
+ 1) {
+ return 0;
}
- return result;
+
+ /* Compute public key. */
+ if (!EccPoint_compute_public_key(_public, _private, curve)) {
+ return 0;
+ }
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public);
+ mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes,
+ _public + curve->num_words);
+#endif
+ return 1;
}
-int64_t mg_to64(struct mg_str str) {
- int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */;
- size_t i = 0;
- while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
- if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
- while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- if (result > max) return 0;
- result *= 10;
- result += (str.ptr[i] - '0');
- i++;
+/* -------- ECDSA code -------- */
+
+static void bits2int(mg_uecc_word_t *native, const uint8_t *bits,
+ unsigned bits_size, MG_UECC_Curve curve) {
+ unsigned num_n_bytes = (unsigned) BITS_TO_BYTES(curve->num_n_bits);
+ unsigned num_n_words = (unsigned) BITS_TO_WORDS(curve->num_n_bits);
+ int shift;
+ mg_uecc_word_t carry;
+ mg_uecc_word_t *ptr;
+
+ if (bits_size > num_n_bytes) {
+ bits_size = num_n_bytes;
+ }
+
+ mg_uecc_vli_clear(native, (wordcount_t) num_n_words);
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) native, bits, bits_size);
+#else
+ mg_uecc_vli_bytesToNative(native, bits, (int) bits_size);
+#endif
+ if (bits_size * 8 <= (unsigned) curve->num_n_bits) {
+ return;
+ }
+ shift = (int) bits_size * 8 - curve->num_n_bits;
+ carry = 0;
+ ptr = native + num_n_words;
+ while (ptr-- > native) {
+ mg_uecc_word_t temp = *ptr;
+ *ptr = (temp >> shift) | carry;
+ carry = temp << (MG_UECC_WORD_BITS - shift);
+ }
+
+ /* Reduce mod curve_n */
+ if (mg_uecc_vli_cmp_unsafe(curve->n, native, (wordcount_t) num_n_words) !=
+ 1) {
+ mg_uecc_vli_sub(native, native, curve->n, (wordcount_t) num_n_words);
}
- return result * neg;
}
-char *mg_remove_double_dots(char *s) {
- char *saved = s, *p = s;
- while (*s != '\0') {
- *p++ = *s++;
- if (s[-1] == '/' || s[-1] == '\\') {
- while (s[0] != '\0') {
- if (s[0] == '/' || s[0] == '\\') {
- s++;
- } else if (s[0] == '.' && s[1] == '.' &&
- (s[2] == '/' || s[2] == '\\')) {
- s += 2;
- } else {
- break;
- }
- }
+static int mg_uecc_sign_with_k_internal(const uint8_t *private_key,
+ const uint8_t *message_hash,
+ unsigned hash_size, mg_uecc_word_t *k,
+ uint8_t *signature,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t s[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t *k2[2] = {tmp, s};
+ mg_uecc_word_t *initial_Z = 0;
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *p = (mg_uecc_word_t *) signature;
+#else
+ mg_uecc_word_t p[MG_UECC_MAX_WORDS * 2];
+#endif
+ mg_uecc_word_t carry;
+ wordcount_t num_words = curve->num_words;
+ wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
+ bitcount_t num_n_bits = curve->num_n_bits;
+
+ /* Make sure 0 < k < curve_n */
+ if (mg_uecc_vli_isZero(k, num_words) ||
+ mg_uecc_vli_cmp(curve->n, k, num_n_words) != 1) {
+ return 0;
+ }
+
+ carry = regularize_k(k, tmp, s, curve);
+ /* If an RNG function was specified, try to get a random initial Z value to
+ improve protection against side-channel attacks. */
+ if (g_rng_function) {
+ if (!mg_uecc_generate_random_int(k2[carry], curve->p, num_words)) {
+ return 0;
}
+ initial_Z = k2[carry];
+ }
+ EccPoint_mult(p, curve->G, k2[!carry], initial_Z,
+ (bitcount_t) (num_n_bits + 1), curve);
+ if (mg_uecc_vli_isZero(p, num_words)) {
+ return 0;
}
- *p = '\0';
- return saved;
-}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/timer.c"
+ /* If an RNG function was specified, get a random number
+ to prevent side channel analysis of k. */
+ if (!g_rng_function) {
+ mg_uecc_vli_clear(tmp, num_n_words);
+ tmp[0] = 1;
+ } else if (!mg_uecc_generate_random_int(tmp, curve->n, num_n_words)) {
+ return 0;
+ }
+
+ /* Prevent side channel analysis of mg_uecc_vli_modInv() to determine
+ bits of k / the private key by premultiplying by a random number */
+ mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */
+ mg_uecc_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */
+ mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */
+
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
+ mg_uecc_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */
#endif
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits));
+#else
+ mg_uecc_vli_bytesToNative(tmp, private_key,
+ BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */
+#endif
+ s[num_n_words - 1] = 0;
+ mg_uecc_vli_set(s, p, num_words);
+ mg_uecc_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */
-#define MG_TIMER_CALLED 4
+ bits2int(tmp, message_hash, hash_size, curve);
+ mg_uecc_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */
+ mg_uecc_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */
+ if (mg_uecc_vli_numBits(s, num_n_words) > (bitcount_t) curve->num_bytes * 8) {
+ return 0;
+ }
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s,
+ curve->num_bytes);
+#else
+ mg_uecc_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s);
+#endif
+ return 1;
+}
-void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms,
- unsigned flags, void (*fn)(void *), void *arg) {
- t->id = 0, t->period_ms = ms, t->expire = 0;
- t->flags = flags, t->fn = fn, t->arg = arg, t->next = *head;
- *head = t;
+#if 0
+/* For testing - sign with an explicitly specified k value */
+int mg_uecc_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash,
+ unsigned hash_size, const uint8_t *k, uint8_t *signature,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t k2[MG_UECC_MAX_WORDS];
+ bits2int(k2, k, (unsigned) BITS_TO_BYTES(curve->num_n_bits), curve);
+ return mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k2,
+ signature, curve);
}
+#endif
-void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
- while (*head && *head != t) head = &(*head)->next;
- if (*head) *head = t->next;
+int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash,
+ unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve) {
+ mg_uecc_word_t k[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tries;
+
+ for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
+ if (!mg_uecc_generate_random_int(k, curve->n,
+ BITS_TO_WORDS(curve->num_n_bits))) {
+ return 0;
+ }
+
+ if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k,
+ signature, curve)) {
+ return 1;
+ }
+ }
+ return 0;
}
-// t: expiration time, prd: period, now: current time. Return true if expired
-bool mg_timer_expired(uint64_t *t, uint64_t prd, uint64_t now) {
- if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
- if (*t == 0) *t = now + prd; // Firt poll? Set expiration
- if (*t > now) return false; // Not expired yet, return
- *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
- return true; // Expired, return true
+/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always
+ the same size as the hash result size. */
+static void HMAC_init(const MG_UECC_HashContext *hash_context,
+ const uint8_t *K) {
+ uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size;
+ unsigned i;
+ for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x36;
+ for (; i < hash_context->block_size; ++i) pad[i] = 0x36;
+
+ hash_context->init_hash(hash_context);
+ hash_context->update_hash(hash_context, pad, hash_context->block_size);
}
-void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
- struct mg_timer *t, *tmp;
- for (t = *head; t != NULL; t = tmp) {
- bool once = t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) &&
- !(t->flags & MG_TIMER_CALLED); // Handle MG_TIMER_NOW only once
- bool expired = mg_timer_expired(&t->expire, t->period_ms, now_ms);
- tmp = t->next;
- if (!once && !expired) continue;
- if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) {
- t->fn(t->arg);
+static void HMAC_update(const MG_UECC_HashContext *hash_context,
+ const uint8_t *message, unsigned message_size) {
+ hash_context->update_hash(hash_context, message, message_size);
+}
+
+static void HMAC_finish(const MG_UECC_HashContext *hash_context,
+ const uint8_t *K, uint8_t *result) {
+ uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size;
+ unsigned i;
+ for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x5c;
+ for (; i < hash_context->block_size; ++i) pad[i] = 0x5c;
+
+ hash_context->finish_hash(hash_context, result);
+
+ hash_context->init_hash(hash_context);
+ hash_context->update_hash(hash_context, pad, hash_context->block_size);
+ hash_context->update_hash(hash_context, result, hash_context->result_size);
+ hash_context->finish_hash(hash_context, result);
+}
+
+/* V = HMAC_K(V) */
+static void update_V(const MG_UECC_HashContext *hash_context, uint8_t *K,
+ uint8_t *V) {
+ HMAC_init(hash_context, K);
+ HMAC_update(hash_context, V, hash_context->result_size);
+ HMAC_finish(hash_context, K, V);
+}
+
+/* Deterministic signing, similar to RFC 6979. Differences are:
+ * We just use H(m) directly rather than bits2octets(H(m))
+ (it is not reduced modulo curve_n).
+ * We generate a value for k (aka T) directly rather than converting
+ endianness.
+
+ Layout of hash_context->tmp: <K> | <V> | (1 byte overlapped 0x00 or 0x01) /
+ <HMAC pad> */
+int mg_uecc_sign_deterministic(const uint8_t *private_key,
+ const uint8_t *message_hash, unsigned hash_size,
+ const MG_UECC_HashContext *hash_context,
+ uint8_t *signature, MG_UECC_Curve curve) {
+ uint8_t *K = hash_context->tmp;
+ uint8_t *V = K + hash_context->result_size;
+ wordcount_t num_bytes = curve->num_bytes;
+ wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
+ bitcount_t num_n_bits = curve->num_n_bits;
+ mg_uecc_word_t tries;
+ unsigned i;
+ for (i = 0; i < hash_context->result_size; ++i) {
+ V[i] = 0x01;
+ K[i] = 0;
+ }
+
+ /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */
+ HMAC_init(hash_context, K);
+ V[hash_context->result_size] = 0x00;
+ HMAC_update(hash_context, V, hash_context->result_size + 1);
+ HMAC_update(hash_context, private_key, (unsigned int) num_bytes);
+ HMAC_update(hash_context, message_hash, hash_size);
+ HMAC_finish(hash_context, K, K);
+
+ update_V(hash_context, K, V);
+
+ /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */
+ HMAC_init(hash_context, K);
+ V[hash_context->result_size] = 0x01;
+ HMAC_update(hash_context, V, hash_context->result_size + 1);
+ HMAC_update(hash_context, private_key, (unsigned int) num_bytes);
+ HMAC_update(hash_context, message_hash, hash_size);
+ HMAC_finish(hash_context, K, K);
+
+ update_V(hash_context, K, V);
+
+ for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
+ mg_uecc_word_t T[MG_UECC_MAX_WORDS];
+ uint8_t *T_ptr = (uint8_t *) T;
+ wordcount_t T_bytes = 0;
+ for (;;) {
+ update_V(hash_context, K, V);
+ for (i = 0; i < hash_context->result_size; ++i) {
+ T_ptr[T_bytes++] = V[i];
+ if (T_bytes >= num_n_words * MG_UECC_WORD_SIZE) {
+ goto filled;
+ }
+ }
+ }
+ filled:
+ if ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8 > num_n_bits) {
+ mg_uecc_word_t mask = (mg_uecc_word_t) -1;
+ T[num_n_words - 1] &=
+ mask >>
+ ((bitcount_t) (num_n_words * MG_UECC_WORD_SIZE * 8 - num_n_bits));
+ }
+
+ if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, T,
+ signature, curve)) {
+ return 1;
}
- t->flags |= MG_TIMER_CALLED;
+
+ /* K = HMAC_K(V || 0x00) */
+ HMAC_init(hash_context, K);
+ V[hash_context->result_size] = 0x00;
+ HMAC_update(hash_context, V, hash_context->result_size + 1);
+ HMAC_finish(hash_context, K, K);
+
+ update_V(hash_context, K, V);
}
+ return 0;
}
-#ifdef MG_ENABLE_LINES
-#line 1 "src/tls_dummy.c"
+static bitcount_t smax(bitcount_t a, bitcount_t b) {
+ return (a > b ? a : b);
+}
+
+int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash,
+ unsigned hash_size, const uint8_t *signature,
+ MG_UECC_Curve curve) {
+ mg_uecc_word_t u1[MG_UECC_MAX_WORDS], u2[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t z[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t sum[MG_UECC_MAX_WORDS * 2];
+ mg_uecc_word_t rx[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t ry[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tx[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t ty[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tz[MG_UECC_MAX_WORDS];
+ const mg_uecc_word_t *points[4];
+ const mg_uecc_word_t *point;
+ bitcount_t num_bits;
+ bitcount_t i;
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
+#else
+ mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
#endif
+ mg_uecc_word_t r[MG_UECC_MAX_WORDS], s[MG_UECC_MAX_WORDS];
+ wordcount_t num_words = curve->num_words;
+ wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
+ rx[num_n_words - 1] = 0;
+ r[num_n_words - 1] = 0;
+ s[num_n_words - 1] = 0;
-#if !MG_ENABLE_MBEDTLS && !MG_ENABLE_OPENSSL && !MG_ENABLE_CUSTOM_TLS
-void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- (void) opts;
- mg_error(c, "TLS is not enabled");
-}
-void mg_tls_handshake(struct mg_connection *c) {
- (void) c;
-}
-void mg_tls_free(struct mg_connection *c) {
- (void) c;
-}
-long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- return c == NULL || buf == NULL || len == 0 ? 0 : -1;
-}
-long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- return c == NULL || buf == NULL || len == 0 ? 0 : -1;
-}
-size_t mg_tls_pending(struct mg_connection *c) {
- (void) c;
- return 0;
-}
+#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
+ bcopy((uint8_t *) r, signature, curve->num_bytes);
+ bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes);
+#else
+ mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes);
+ mg_uecc_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes,
+ curve->num_bytes);
+ mg_uecc_vli_bytesToNative(r, signature, curve->num_bytes);
+ mg_uecc_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes);
#endif
-#ifdef MG_ENABLE_LINES
-#line 1 "src/tls_mbed.c"
-#endif
+ /* r, s must not be 0. */
+ if (mg_uecc_vli_isZero(r, num_words) || mg_uecc_vli_isZero(s, num_words)) {
+ return 0;
+ }
+ /* r, s must be < n. */
+ if (mg_uecc_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 ||
+ mg_uecc_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) {
+ return 0;
+ }
+ /* Calculate u1 and u2. */
+ mg_uecc_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */
+ u1[num_n_words - 1] = 0;
+ bits2int(u1, message_hash, hash_size, curve);
+ mg_uecc_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */
+ mg_uecc_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */
+
+ /* Calculate sum = G + Q. */
+ mg_uecc_vli_set(sum, _public, num_words);
+ mg_uecc_vli_set(sum + num_words, _public + num_words, num_words);
+ mg_uecc_vli_set(tx, curve->G, num_words);
+ mg_uecc_vli_set(ty, curve->G + num_words, num_words);
+ mg_uecc_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */
+ XYcZ_add(tx, ty, sum, sum + num_words, curve);
+ mg_uecc_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */
+ apply_z(sum, sum + num_words, z, curve);
+
+ /* Use Shamir's trick to calculate u1*G + u2*Q */
+ points[0] = 0;
+ points[1] = curve->G;
+ points[2] = _public;
+ points[3] = sum;
+ num_bits = smax(mg_uecc_vli_numBits(u1, num_n_words),
+ mg_uecc_vli_numBits(u2, num_n_words));
+ point =
+ points[(!!mg_uecc_vli_testBit(u1, (bitcount_t) (num_bits - 1))) |
+ ((!!mg_uecc_vli_testBit(u2, (bitcount_t) (num_bits - 1))) << 1)];
+ mg_uecc_vli_set(rx, point, num_words);
+ mg_uecc_vli_set(ry, point + num_words, num_words);
+ mg_uecc_vli_clear(z, num_words);
+ z[0] = 1;
+
+ for (i = num_bits - 2; i >= 0; --i) {
+ mg_uecc_word_t index;
+ curve->double_jacobian(rx, ry, z, curve);
+
+ index = (!!mg_uecc_vli_testBit(u1, i)) |
+ (mg_uecc_word_t) ((!!mg_uecc_vli_testBit(u2, i)) << 1);
+ point = points[index];
+ if (point) {
+ mg_uecc_vli_set(tx, point, num_words);
+ mg_uecc_vli_set(ty, point + num_words, num_words);
+ apply_z(tx, ty, z, curve);
+ mg_uecc_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */
+ XYcZ_add(tx, ty, rx, ry, curve);
+ mg_uecc_vli_modMult_fast(z, z, tz, curve);
+ }
+ }
+ mg_uecc_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */
+ apply_z(rx, ry, z, curve);
-#if MG_ENABLE_MBEDTLS
+ /* v = x1 (mod n) */
+ if (mg_uecc_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) {
+ mg_uecc_vli_sub(rx, rx, curve->n, num_n_words);
+ }
-#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
-#define MGRNG , rng_get, NULL
-#else
-#define MGRNG
-#endif
+ /* Accept only if v == r. */
+ return (int) (mg_uecc_vli_equal(rx, r, num_words));
+}
-void mg_tls_free(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (tls != NULL) {
- free(tls->cafile);
- mbedtls_ssl_free(&tls->ssl);
- mbedtls_pk_free(&tls->pk);
- mbedtls_x509_crt_free(&tls->ca);
- mbedtls_x509_crt_free(&tls->cert);
- mbedtls_ssl_config_free(&tls->conf);
- free(tls);
- c->tls = NULL;
- }
+#if MG_UECC_ENABLE_VLI_API
+
+unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve) {
+ return curve->num_words;
}
-static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
- long n = mg_io_send((struct mg_connection *) ctx, buf, len);
- MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
- if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
- if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
- if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
- return (int) n;
+unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve) {
+ return curve->num_bytes;
}
-static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
- long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
- MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
- if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
- if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
- if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
- return (int) n;
+unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve) {
+ return curve->num_bytes * 8;
}
-void mg_tls_handshake(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int rc = mbedtls_ssl_handshake(&tls->ssl);
- if (rc == 0) { // Success
- MG_DEBUG(("%lu success", c->id));
- c->is_tls_hs = 0;
- mg_call(c, MG_EV_TLS_HS, NULL);
- } else if (rc == MBEDTLS_ERR_SSL_WANT_READ ||
- rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending
- MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting,
- c->is_tls_hs, rc, -rc));
- } else {
- mg_error(c, "TLS handshake: -%#x", -rc); // Error
- }
+unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve) {
+ return BITS_TO_WORDS(curve->num_n_bits);
}
-static int mbed_rng(void *ctx, unsigned char *buf, size_t len) {
- mg_random(buf, len);
- (void) ctx;
- return 0;
+unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve) {
+ return BITS_TO_BYTES(curve->num_n_bits);
}
-static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
- n = (int) strlen(s2) - 1;
- MG_VERBOSE(("%lu %d %.*s", ((struct mg_connection *) c)->id, lev, n, s2));
- (void) s;
+unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve) {
+ return curve->num_n_bits;
}
-#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
-static int rng_get(void *p_rng, unsigned char *buf, size_t len) {
- (void) p_rng;
- mg_random(buf, len);
- return 0;
+const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve) {
+ return curve->p;
}
-#endif
-static struct mg_str mg_loadfile(struct mg_fs *fs, const char *path) {
- size_t n = 0;
- if (path[0] == '-') return mg_str(path);
- char *p = mg_file_read(fs, path, &n);
- return mg_str_n(p, n);
+const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve) {
+ return curve->n;
}
-void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
- int rc = 0;
- c->tls = tls;
- if (c->tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- MG_DEBUG(("%lu Setting TLS", c->id));
- mbedtls_ssl_init(&tls->ssl);
- mbedtls_ssl_config_init(&tls->conf);
- mbedtls_x509_crt_init(&tls->ca);
- mbedtls_x509_crt_init(&tls->cert);
- mbedtls_pk_init(&tls->pk);
- mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
-#if defined(MG_MBEDTLS_DEBUG_LEVEL)
- mbedtls_debug_set_threshold(MG_MBEDTLS_DEBUG_LEVEL);
+const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve) {
+ return curve->G;
+}
+
+const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve) {
+ return curve->b;
+}
+
+#if MG_UECC_SUPPORT_COMPRESSED_POINT
+void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve) {
+ curve->mod_sqrt(a, curve);
+}
#endif
- if ((rc = mbedtls_ssl_config_defaults(
- &tls->conf,
- c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
- MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
- mg_error(c, "tls defaults %#x", -rc);
- goto fail;
- }
- mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c);
- if (opts->ca == NULL || strcmp(opts->ca, "*") == 0) {
- mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
- } else if (opts->ca != NULL && opts->ca[0] != '\0') {
-#if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK)
- tls->cafile = strdup(opts->ca);
- rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL);
- if (rc != 0) {
- mg_error(c, "parse on-disk chain(%s) err %#x", tls->cafile, -rc);
- goto fail;
- }
+
+void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product,
+ MG_UECC_Curve curve) {
+#if (MG_UECC_OPTIMIZATION_LEVEL > 0)
+ curve->mmod_fast(result, product);
#else
- struct mg_str s = mg_loadfile(fs, opts->ca);
- rc = mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) s.ptr, s.len + 1);
- if (opts->ca[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "parse(%s) err %#x", opts->ca, -rc);
- goto fail;
- }
- mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
+ mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
#endif
- if (opts->srvname.len > 0) {
- char *x = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
- mbedtls_ssl_set_hostname(&tls->ssl, x);
- free(x);
- }
- mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
- }
- if (opts->cert != NULL && opts->cert[0] != '\0') {
- struct mg_str s = mg_loadfile(fs, opts->cert);
- const char *key = opts->certkey == NULL ? opts->cert : opts->certkey;
- rc = mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) s.ptr, s.len + 1);
- if (opts->cert[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "parse(%s) err %#x", opts->cert, -rc);
- goto fail;
- }
- s = mg_loadfile(fs, key);
- rc = mbedtls_pk_parse_key(&tls->pk, (uint8_t *) s.ptr, s.len + 1, NULL,
- 0 MGRNG);
- if (key[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "tls key(%s) %#x", key, -rc);
- goto fail;
- }
- rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk);
- if (rc != 0) {
- mg_error(c, "own cert %#x", -rc);
- goto fail;
- }
- }
- if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
- mg_error(c, "setup err %#x", -rc);
- goto fail;
- }
- c->tls = tls;
- c->is_tls = 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) {
- mg_tls_handshake(c);
- }
- return;
-fail:
- mg_tls_free(c);
}
-size_t mg_tls_pending(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl);
-}
+void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
+ const mg_uecc_word_t *scalar, MG_UECC_Curve curve) {
+ mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
+ mg_uecc_word_t *p2[2] = {tmp1, tmp2};
+ mg_uecc_word_t carry = regularize_k(scalar, tmp1, tmp2, curve);
-long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
- if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
- return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
+ EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve);
}
-long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
- if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
- return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
-}
-#endif
+#endif /* MG_UECC_ENABLE_VLI_API */
+#endif // MG_TLS_BUILTIN
+// End of uecc BSD-2
#ifdef MG_ENABLE_LINES
-#line 1 "src/tls_openssl.c"
+#line 1 "src/tls_x25519.c"
#endif
+/**
+ * Adapted from STROBE: https://strobe.sourceforge.io/
+ * Copyright (c) 2015-2016 Cryptography Research, Inc.
+ * Author: Mike Hamburg
+ * License: MIT License
+ */
-#if MG_ENABLE_OPENSSL
-static int mg_tls_err(struct mg_tls *tls, int res) {
- int err = SSL_get_error(tls->ssl, res);
- // We've just fetched the last error from the queue.
- // Now we need to clear the error queue. If we do not, then the following
- // can happen (actually reported):
- // - A new connection is accept()-ed with cert error (e.g. self-signed cert)
- // - Since all accept()-ed connections share listener's context,
- // - *ALL* SSL accepted connection report read error on the next poll cycle.
- // Thus a single errored connection can close all the rest, unrelated ones.
- // Clearing the error keeps the shared SSL_CTX in an OK state.
+const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};
- if (err != 0) ERR_print_errors_fp(stderr);
- ERR_clear_error();
- if (err == SSL_ERROR_WANT_READ) return 0;
- if (err == SSL_ERROR_WANT_WRITE) return 0;
- return err;
-}
+#define X25519_WBITS 32
-void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
- const char *id = "mongoose";
- static unsigned char s_initialised = 0;
- int rc;
+typedef uint32_t limb_t;
+typedef uint64_t dlimb_t;
+typedef int64_t sdlimb_t;
- if (tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
+#define NLIMBS (256 / X25519_WBITS)
+typedef limb_t mg_fe[NLIMBS];
- if (!s_initialised) {
- SSL_library_init();
- s_initialised++;
- }
- MG_DEBUG(("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id,
- opts->ca == NULL ? "null" : opts->ca,
- opts->cert == NULL ? "null" : opts->cert,
- opts->certkey == NULL ? "null" : opts->certkey));
- tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method())
- : SSL_CTX_new(SSLv23_server_method());
- if ((tls->ssl = SSL_new(tls->ctx)) == NULL) {
- mg_error(c, "SSL_new");
- goto fail;
- }
- SSL_set_session_id_context(tls->ssl, (const uint8_t *) id,
- (unsigned) strlen(id));
- // Disable deprecated protocols
- SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2);
- SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3);
- SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1);
- SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1);
-#ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION
- SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION);
-#endif
-#ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE
- SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
-#endif
+static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) {
+ dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;
+ *carry = (limb_t) (tmp >> X25519_WBITS);
+ return (limb_t) tmp;
+}
- if (opts->ca != NULL && opts->ca[0] != '\0') {
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- NULL);
- if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) {
- mg_error(c, "load('%s') %d err %d", opts->ca, rc, mg_tls_err(tls, rc));
- goto fail;
- }
- }
- if (opts->cert != NULL && opts->cert[0] != '\0') {
- const char *key = opts->certkey;
- if (key == NULL) key = opts->cert;
- if ((rc = SSL_use_certificate_file(tls->ssl, opts->cert, 1)) != 1) {
- mg_error(c, "Invalid SSL cert, err %d", mg_tls_err(tls, rc));
- goto fail;
- } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) {
- mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc));
- goto fail;
-#if OPENSSL_VERSION_NUMBER > 0x10100000L
- } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) !=
- 1) {
- mg_error(c, "Invalid chain, err %d", mg_tls_err(tls, rc));
- goto fail;
-#endif
- } else {
- SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-#if OPENSSL_VERSION_NUMBER > 0x10002000L
- SSL_set_ecdh_auto(tls->ssl, 1);
-#endif
- }
- }
- if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers);
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- if (opts->srvname.len > 0) {
- char *s = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
- SSL_set1_host(tls->ssl, s);
- free(s);
- }
-#endif
- c->tls = tls;
- c->is_tls = 1;
- c->is_tls_hs = 1;
- if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
- mg_tls_handshake(c);
- }
- MG_DEBUG(("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
- return;
-fail:
- c->is_closing = 1;
- free(tls);
+// These functions are implemented in terms of umaal on ARM
+static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
+ dlimb_t total = (dlimb_t) *carry + acc + mand;
+ *carry = (limb_t) (total >> X25519_WBITS);
+ return (limb_t) total;
+}
+
+static limb_t adc0(limb_t *carry, limb_t acc) {
+ dlimb_t total = (dlimb_t) *carry + acc;
+ *carry = (limb_t) (total >> X25519_WBITS);
+ return (limb_t) total;
+}
+
+// - Precondition: carry is small.
+// - Invariant: result of propagate is < 2^255 + 1 word
+// - In particular, always less than 2p.
+// - Also, output x >= min(x,19)
+static void propagate(mg_fe x, limb_t over) {
+ unsigned i;
+ limb_t carry;
+ over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1;
+ x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1));
+
+ carry = over * 19;
+ for (i = 0; i < NLIMBS; i++) {
+ x[i] = adc0(&carry, x[i]);
+ }
}
-void mg_tls_handshake(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int rc;
- SSL_set_fd(tls->ssl, (int) (size_t) c->fd);
- rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
- if (rc == 1) {
- MG_DEBUG(("%lu success", c->id));
- c->is_tls_hs = 0;
- mg_call(c, MG_EV_TLS_HS, NULL);
- } else {
- int code = mg_tls_err(tls, rc);
- if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
+static void add(mg_fe out, const mg_fe a, const mg_fe b) {
+ unsigned i;
+ limb_t carry = 0;
+ for (i = 0; i < NLIMBS; i++) {
+ out[i] = adc(&carry, a[i], b[i]);
}
+ propagate(out, carry);
}
-void mg_tls_free(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (tls == NULL) return;
- SSL_free(tls->ssl);
- SSL_CTX_free(tls->ctx);
- free(tls);
- c->tls = NULL;
+static void sub(mg_fe out, const mg_fe a, const mg_fe b) {
+ unsigned i;
+ sdlimb_t carry = -38;
+ for (i = 0; i < NLIMBS; i++) {
+ carry = carry + a[i] - b[i];
+ out[i] = (limb_t) carry;
+ carry >>= X25519_WBITS;
+ }
+ propagate(out, (limb_t) (1 + carry));
+}
+
+// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe`
+// to avoid build warnings
+static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) {
+ limb_t accum[2 * NLIMBS] = {0};
+ unsigned i, j;
+
+ limb_t carry2;
+ for (i = 0; i < nb; i++) {
+ limb_t mand = b[i];
+ carry2 = 0;
+ for (j = 0; j < NLIMBS; j++) {
+ limb_t tmp; // "a" may be misaligned
+ memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy
+ accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp);
+ }
+ accum[i + j] = carry2;
+ }
+
+ carry2 = 0;
+ for (j = 0; j < NLIMBS; j++) {
+ out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]);
+ }
+ propagate(out, carry2);
}
-size_t mg_tls_pending(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- return tls == NULL ? 0 : (size_t) SSL_pending(tls->ssl);
+static void sqr(mg_fe out, const mg_fe a) {
+ mul(out, a, a, NLIMBS);
+}
+static void mul1(mg_fe out, const mg_fe a) {
+ mul(out, a, out, NLIMBS);
+}
+static void sqr1(mg_fe a) {
+ mul1(a, a);
}
-long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int n = SSL_read(tls->ssl, buf, (int) len);
- if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
+static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS],
+ limb_t doswap) {
+ unsigned i;
+ for (i = 0; i < 2 * NLIMBS; i++) {
+ limb_t xor_ab = (a[i] ^ b[i]) & doswap;
+ a[i] ^= xor_ab;
+ b[i] ^= xor_ab;
+ }
}
-long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int n = SSL_write(tls->ssl, buf, (int) len);
- if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
+// Canonicalize a field element x, reducing it to the least residue which is
+// congruent to it mod 2^255-19
+// - Precondition: x < 2^255 + 1 word
+static limb_t canon(mg_fe x) {
+ // First, add 19.
+ unsigned i;
+ limb_t carry0 = 19;
+ limb_t res;
+ sdlimb_t carry;
+ for (i = 0; i < NLIMBS; i++) {
+ x[i] = adc0(&carry0, x[i]);
+ }
+ propagate(x, carry0);
+
+ // Here, 19 <= x2 < 2^255
+ // - This is because we added 19, so before propagate it can't be less
+ // than 19. After propagate, it still can't be less than 19, because if
+ // propagate does anything it adds 19.
+ // - We know that the high bit must be clear, because either the input was ~
+ // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or
+ // it was < 2^255. So now, if we subtract 19, we will get back to something in
+ // [0,2^255-19).
+ carry = -19;
+ res = 0;
+ for (i = 0; i < NLIMBS; i++) {
+ carry += x[i];
+ res |= x[i] = (limb_t) carry;
+ carry >>= X25519_WBITS;
+ }
+ return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS);
+}
+
+static const limb_t a24[1] = {121665};
+
+static void ladder_part1(mg_fe xs[5]) {
+ limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
+ add(t1, x2, z2); // t1 = A
+ sub(z2, x2, z2); // z2 = B
+ add(x2, x3, z3); // x2 = C
+ sub(z3, x3, z3); // z3 = D
+ mul1(z3, t1); // z3 = DA
+ mul1(x2, z2); // x3 = BC
+ add(x3, z3, x2); // x3 = DA+CB
+ sub(z3, z3, x2); // z3 = DA-CB
+ sqr1(t1); // t1 = AA
+ sqr1(z2); // z2 = BB
+ sub(x2, t1, z2); // x2 = E = AA-BB
+ mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24
+ add(z2, z2, t1); // z2 = E*a24 + AA
+}
+
+static void ladder_part2(mg_fe xs[5], const mg_fe x1) {
+ limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
+ sqr1(z3); // z3 = (DA-CB)^2
+ mul1(z3, x1); // z3 = x1 * (DA-CB)^2
+ sqr1(x3); // x3 = (DA+CB)^2
+ mul1(z2, x2); // z2 = AA*(E*a24+AA)
+ sub(x2, t1, x2); // x2 = BB again
+ mul1(x2, t1); // x2 = AA*BB
+}
+
+static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES],
+ const uint8_t *x1, int clamp) {
+ int i;
+ mg_fe x1_limbs;
+ limb_t swap = 0;
+ limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3];
+ memset(xs, 0, 4 * sizeof(mg_fe));
+ x2[0] = z3[0] = 1;
+ for (i = 0; i < NLIMBS; i++) {
+ x3[i] = x1_limbs[i] =
+ MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]);
+ }
+
+ for (i = 255; i >= 0; i--) {
+ uint8_t bytei = scalar[i / 8];
+ limb_t doswap;
+ if (clamp) {
+ if (i / 8 == 0) {
+ bytei &= (uint8_t) ~7U;
+ } else if (i / 8 == X25519_BYTES - 1) {
+ bytei &= 0x7F;
+ bytei |= 0x40;
+ }
+ }
+ doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1);
+ condswap(x2, x3, swap ^ doswap);
+ swap = doswap;
+
+ ladder_part1(xs);
+ ladder_part2(xs, (const limb_t *) x1_limbs);
+ }
+ condswap(x2, x3, swap);
+}
+
+int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
+ const uint8_t x1[X25519_BYTES], int clamp) {
+ int i, ret;
+ mg_fe xs[5], out_limbs;
+ limb_t *x2, *z2, *z3, *prev;
+ static const struct {
+ uint8_t a, c, n;
+ } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1},
+ {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125},
+ {3, 1, 2}, {3, 1, 2}, {3, 1, 1}};
+ x25519_core(xs, scalar, x1, clamp);
+
+ // Precomputed inversion chain
+ x2 = xs[0];
+ z2 = xs[1];
+ z3 = xs[3];
+
+ prev = z2;
+ for (i = 0; i < 13; i++) {
+ int j;
+ limb_t *a = xs[steps[i].a];
+ for (j = steps[i].n; j > 0; j--) {
+ sqr(a, prev);
+ prev = a;
+ }
+ mul1(a, xs[steps[i].c]);
+ }
+
+ // Here prev = z3
+ // x2 /= z2
+ mul(out_limbs, x2, z3, NLIMBS);
+ ret = (int) canon(out_limbs);
+ if (!clamp) ret = 0;
+ for (i = 0; i < NLIMBS; i++) {
+ uint32_t n = out_limbs[i];
+ out[i * 4] = (uint8_t) (n & 0xff);
+ out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff);
+ out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff);
+ out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff);
+ }
+ return ret;
}
-#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/url.c"
@@ -5382,7 +14797,7 @@ struct url {
int mg_url_is_ssl(const char *url) {
return strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0 ||
strncmp(url, "mqtts:", 6) == 0 || strncmp(url, "ssl:", 4) == 0 ||
- strncmp(url, "tls:", 4) == 0;
+ strncmp(url, "tls:", 4) == 0 || strncmp(url, "tcps:", 5) == 0;
}
static struct url urlparse(const char *url) {
@@ -5464,6 +14879,14 @@ struct mg_str mg_url_pass(const char *url) {
#endif
+// Not using memset for zeroing memory, cause it can be dropped by compiler
+// See https://github.com/cesanta/mongoose/pull/1265
+void mg_bzero(volatile unsigned char *buf, size_t len) {
+ if (buf != NULL) {
+ while (len--) *buf++ = 0;
+ }
+}
+
#if MG_ENABLE_CUSTOM_RANDOM
#else
void mg_random(void *buf, size_t len) {
@@ -5512,11 +14935,16 @@ uint16_t mg_ntohs(uint16_t net) {
}
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
- int i;
+ static const uint32_t crclut[16] = {
+ // table for polynomial 0xEDB88320 (reflected)
+ 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4,
+ 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
+ 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C};
crc = ~crc;
while (len--) {
- crc ^= *(unsigned char *) buf++;
- for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
+ uint8_t b = *(uint8_t *) buf++;
+ crc = crclut[(crc ^ b) & 0x0F] ^ (crc >> 4);
+ crc = crclut[(crc ^ (b >> 4)) & 0x0F] ^ (crc >> 4);
}
return ~crc;
}
@@ -5539,18 +14967,36 @@ static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
return len;
}
-int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) {
- struct mg_str k, v;
+int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip) {
+ struct mg_str entry;
int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default
- while (mg_commalist(&acl, &k, &v)) {
- uint32_t net, mask;
- if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1;
- if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2;
- if ((mg_ntohl(remote_ip) & mask) == net) allowed = k.ptr[0];
+ uint32_t remote_ip4;
+ if (remote_ip->is_ip6) {
+ return -1; // TODO(): handle IPv6 ACL and addresses
+ } else { // IPv4
+ memcpy((void *) &remote_ip4, remote_ip->ip, sizeof(remote_ip4));
+ while (mg_span(acl, &entry, &acl, ',')) {
+ uint32_t net, mask;
+ if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1;
+ if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2;
+ if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0];
+ }
}
return allowed == '+';
}
+bool mg_path_is_sane(const struct mg_str path) {
+ const char *s = path.buf;
+ size_t n = path.len;
+ if (path.buf[0] == '.' && path.buf[1] == '.') return false; // Starts with ..
+ for (; s[0] != '\0' && n > 0; s++, n--) {
+ if ((s[0] == '/' || s[0] == '\\') && n >= 2) { // Subdir?
+ if (s[1] == '.' && s[2] == '.') return false; // Starts with ..
+ }
+ }
+ return true;
+}
+
#if MG_ENABLE_CUSTOM_MILLIS
#else
uint64_t mg_millis(void) {
@@ -5558,9 +15004,8 @@ uint64_t mg_millis(void) {
return GetTickCount();
#elif MG_ARCH == MG_ARCH_RP2040
return time_us_64() / 1000;
-#elif MG_ARCH == MG_ARCH_ESP32
- return esp_timer_get_time() / 1000;
-#elif MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_FREERTOS
+#elif MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_ESP32 || \
+ MG_ARCH == MG_ARCH_FREERTOS
return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_AZURERTOS
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
@@ -5568,6 +15013,12 @@ uint64_t mg_millis(void) {
return (uint64_t) Clock_getTicks();
#elif MG_ARCH == MG_ARCH_ZEPHYR
return (uint64_t) k_uptime_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS1
+ return (uint64_t) rt_time_get();
+#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
+ return (uint64_t) ((osKernelGetTickCount() * 1000) / osKernelGetTickFreq());
+#elif MG_ARCH == MG_ARCH_RTTHREAD
+ return (uint64_t) ((rt_tick_get() * 1000) / RT_TICK_PER_SECOND);
#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
// Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux
// Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux
@@ -5595,7 +15046,6 @@ uint64_t mg_millis(void) {
}
#endif
-
#ifdef MG_ENABLE_LINES
#line 1 "src/ws.c"
#endif
@@ -5609,6 +15059,7 @@ uint64_t mg_millis(void) {
+
struct ws_msg {
uint8_t flags;
size_t header_len;
@@ -5640,10 +15091,10 @@ static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey,
mg_sha1_ctx sha_ctx;
mg_sha1_init(&sha_ctx);
- mg_sha1_update(&sha_ctx, (unsigned char *) wskey->ptr, wskey->len);
+ mg_sha1_update(&sha_ctx, (unsigned char *) wskey->buf, wskey->len);
mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36);
mg_sha1_final(sha, &sha_ctx);
- mg_base64_encode(sha, sizeof(sha), (char *) b64_sha);
+ mg_base64_encode(sha, sizeof(sha), (char *) b64_sha, sizeof(b64_sha));
mg_xprintf(mg_pfn_iobuf, &c->send,
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
@@ -5653,7 +15104,7 @@ static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey,
if (fmt != NULL) mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
if (wsproto != NULL) {
mg_printf(c, "Sec-WebSocket-Protocol: %.*s\r\n", (int) wsproto->len,
- wsproto->ptr);
+ wsproto->buf);
}
mg_send(c, "\r\n", 2);
}
@@ -5746,12 +15197,15 @@ static bool mg_ws_client_handshake(struct mg_connection *c) {
mg_error(c, "not http"); // Some just, not an HTTP request
} else if (n > 0) {
if (n < 15 || memcmp(c->recv.buf + 9, "101", 3) != 0) {
- mg_error(c, "handshake error");
+ mg_error(c, "ws handshake error");
} else {
struct mg_http_message hm;
- mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
- c->is_websocket = 1;
- mg_call(c, MG_EV_WS_OPEN, &hm);
+ if (mg_http_parse((char *) c->recv.buf, c->recv.len, &hm)) {
+ c->is_websocket = 1;
+ mg_call(c, MG_EV_WS_OPEN, &hm);
+ } else {
+ mg_error(c, "ws handshake error");
+ }
}
mg_iobuf_del(&c->recv, 0, (size_t) n);
} else {
@@ -5760,8 +15214,7 @@ static bool mg_ws_client_handshake(struct mg_connection *c) {
return false; // Continue event handler
}
-static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
+static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data) {
struct ws_msg msg;
size_t ofs = (size_t) c->pfn_data;
@@ -5775,7 +15228,7 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
size_t len = msg.header_len + msg.data_len;
uint8_t final = msg.flags & 128, op = msg.flags & 15;
// MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
- // (int) m.data.len, (int) m.data.len, m.data.ptr));
+ // (int) m.data.len, (int) m.data.len, m.data.buf));
switch (op) {
case WEBSOCKET_OP_CONTINUE:
mg_call(c, MG_EV_WS_CTL, &m);
@@ -5796,7 +15249,7 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
MG_DEBUG(("%lu WS CLOSE", c->id));
mg_call(c, MG_EV_WS_CTL, &m);
// Echo the payload of the received CLOSE message back to the sender
- mg_ws_send(c, m.data.ptr, m.data.len, WEBSOCKET_OP_CLOSE);
+ mg_ws_send(c, m.data.buf, m.data.len, WEBSOCKET_OP_CLOSE);
c->is_draining = 1;
break;
default:
@@ -5827,7 +15280,6 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
}
}
}
- (void) fn_data;
(void) ev_data;
}
@@ -5839,7 +15291,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
char nonce[16], key[30];
struct mg_str host = mg_url_host(url);
mg_random(nonce, sizeof(nonce));
- mg_base64_encode((unsigned char *) nonce, sizeof(nonce), key);
+ mg_base64_encode((unsigned char *) nonce, sizeof(nonce), key, sizeof(key));
mg_xprintf(mg_pfn_iobuf, &c->send,
"GET %s HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
@@ -5847,7 +15299,7 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
"Connection: Upgrade\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: %s\r\n",
- mg_url_uri(url), (int) host.len, host.ptr, key);
+ mg_url_uri(url), (int) host.len, host.buf, key);
if (fmt != NULL) {
va_list ap;
va_start(ap, fmt);
@@ -5896,1591 +15348,2247 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
}
#ifdef MG_ENABLE_LINES
-#line 1 "mip/driver_stm32.c"
+#line 1 "src/drivers/cmsis.c"
#endif
+// https://arm-software.github.io/CMSIS_5/Driver/html/index.html
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS
-#if MG_ENABLE_MIP && \
- (!defined(MG_ENABLE_DRIVER_TM4C) || MG_ENABLE_DRIVER_TM4C == 0)
-struct stm32_eth {
- volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
- MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
- MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
- MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
- RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
- RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
- RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
- PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
- DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
- DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
- DMACHRBAR;
-};
-#undef ETH
-#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
-#undef BIT
-#define BIT(x) ((uint32_t) 1 << (x))
-#define ETH_PKT_SIZE 1540 // Max frame size
-#define ETH_DESC_CNT 4 // Descriptors count
-#define ETH_DS 4 // Descriptor size (words)
-static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
-static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
-static struct mip_if *s_ifp; // MIP interface
-enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants
-static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
- ETH->MACMIIAR &= (7 << 2);
- ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
- ETH->MACMIIAR |= BIT(0);
- while (ETH->MACMIIAR & BIT(0)) (void) 0;
- return ETH->MACMIIDR;
-}
-static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- ETH->MACMIIDR = val;
- ETH->MACMIIAR &= (7 << 2);
- ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
- ETH->MACMIIAR |= BIT(0);
- while (ETH->MACMIIAR & BIT(0)) (void) 0;
-}
+extern ARM_DRIVER_ETH_MAC Driver_ETH_MAC0;
+extern ARM_DRIVER_ETH_PHY Driver_ETH_PHY0;
-static uint32_t get_hclk(void) {
- struct rcc {
- volatile uint32_t CR, PLLCFGR, CFGR;
- } *rcc = (struct rcc *) 0x40023800;
- uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
+static struct mg_tcpip_if *s_ifp;
- if (rcc->CFGR & (1 << 2)) {
- clk = hse;
- } else if (rcc->CFGR & (1 << 3)) {
- uint32_t vco, m, n, p;
- m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
- n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
- p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
- clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
- vco = (uint32_t) ((uint64_t) clk * n / m);
- clk = vco / p;
- } else {
- clk = hsi;
+static void mac_cb(uint32_t);
+static bool cmsis_init(struct mg_tcpip_if *);
+static bool cmsis_up(struct mg_tcpip_if *);
+static size_t cmsis_tx(const void *, size_t, struct mg_tcpip_if *);
+static size_t cmsis_rx(void *, size_t, struct mg_tcpip_if *);
+
+struct mg_tcpip_driver mg_tcpip_driver_cmsis = {cmsis_init, cmsis_tx, NULL,
+ cmsis_up};
+
+static bool cmsis_init(struct mg_tcpip_if *ifp) {
+ ARM_ETH_MAC_ADDR addr;
+ s_ifp = ifp;
+
+ ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
+ ARM_DRIVER_ETH_PHY *phy = &Driver_ETH_PHY0;
+ ARM_ETH_MAC_CAPABILITIES cap = mac->GetCapabilities();
+ if (mac->Initialize(mac_cb) != ARM_DRIVER_OK) return false;
+ if (phy->Initialize(mac->PHY_Read, mac->PHY_Write) != ARM_DRIVER_OK)
+ return false;
+ if (cap.event_rx_frame == 0) // polled mode driver
+ mg_tcpip_driver_cmsis.rx = cmsis_rx;
+ mac->PowerControl(ARM_POWER_FULL);
+ if (cap.mac_address) { // driver provides MAC address
+ mac->GetMacAddress(&addr);
+ memcpy(ifp->mac, &addr, sizeof(ifp->mac));
+ } else { // we provide MAC address
+ memcpy(&addr, ifp->mac, sizeof(addr));
+ mac->SetMacAddress(&addr);
}
- uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
- if (hpre < 8) return clk;
+ phy->PowerControl(ARM_POWER_FULL);
+ phy->SetInterface(cap.media_interface);
+ phy->SetMode(ARM_ETH_PHY_AUTO_NEGOTIATE);
+ return true;
+}
- uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
- return ((uint32_t) clk) >> ahbptab[hpre - 8];
+static size_t cmsis_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
+ ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
+ if (mac->SendFrame(buf, (uint32_t) len, 0) != ARM_DRIVER_OK) {
+ ifp->nerr++;
+ return 0;
+ }
+ ifp->nsent++;
+ return len;
}
-// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
-// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
-// from the HSI (internal RC), and it can go above specs, the datasheets
-// specify a range of frequencies and activate one of a series of dividers to
-// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
-// HCLK with a +5% drift. If the user uses a different clock from our
-// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
-// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
-static int guess_mdc_cr(void) {
- uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
- uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
- uint32_t hclk = get_hclk(); // Guess system HCLK
- int result = -1; // Invalid CR value
- if (hclk < 25000000) {
- MG_ERROR(("HCLK too low"));
- } else {
- for (int i = 0; i < 6; i++) {
- if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
- result = crs[i];
- break;
+static bool cmsis_up(struct mg_tcpip_if *ifp) {
+ ARM_DRIVER_ETH_PHY *phy = &Driver_ETH_PHY0;
+ ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
+ bool up = (phy->GetLinkState() == ARM_ETH_LINK_UP) ? 1 : 0; // link state
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // just went up
+ ARM_ETH_LINK_INFO st = phy->GetLinkInfo();
+ mac->Control(ARM_ETH_MAC_CONFIGURE,
+ (st.speed << ARM_ETH_MAC_SPEED_Pos) |
+ (st.duplex << ARM_ETH_MAC_DUPLEX_Pos) |
+ ARM_ETH_MAC_ADDRESS_BROADCAST);
+ MG_DEBUG(("Link is %uM %s-duplex",
+ (st.speed == 2) ? 1000
+ : st.speed ? 100
+ : 10,
+ st.duplex ? "full" : "half"));
+ mac->Control(ARM_ETH_MAC_CONTROL_TX, 1);
+ mac->Control(ARM_ETH_MAC_CONTROL_RX, 1);
+ } else if ((ifp->state != MG_TCPIP_STATE_DOWN) && !up) { // just went down
+ mac->Control(ARM_ETH_MAC_FLUSH,
+ ARM_ETH_MAC_FLUSH_TX | ARM_ETH_MAC_FLUSH_RX);
+ mac->Control(ARM_ETH_MAC_CONTROL_TX, 0);
+ mac->Control(ARM_ETH_MAC_CONTROL_RX, 0);
+ }
+ return up;
+}
+
+static void mac_cb(uint32_t ev) {
+ if ((ev & ARM_ETH_MAC_EVENT_RX_FRAME) == 0) return;
+ ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
+ uint32_t len = mac->GetRxFrameSize(); // CRC already stripped
+ if (len >= 60 && len <= 1518) { // proper frame
+ char *p;
+ if (mg_queue_book(&s_ifp->recv_queue, &p, len) >= len) { // have room
+ if ((len = mac->ReadFrame((uint8_t *) p, len)) > 0) { // copy succeeds
+ mg_queue_add(&s_ifp->recv_queue, len);
+ s_ifp->nrecv++;
}
+ return;
}
- if (result < 0) MG_ERROR(("HCLK too high"));
+ s_ifp->ndrop++;
}
- MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
- return result;
+ mac->ReadFrame(NULL, 0); // otherwise, discard
+}
+
+static size_t cmsis_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
+ ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
+ uint32_t len = mac->GetRxFrameSize(); // CRC already stripped
+ if (len >= 60 && len <= 1518 &&
+ ((len = mac->ReadFrame(buf, (uint32_t) buflen)) > 0))
+ return len;
+ if (len > 0) mac->ReadFrame(NULL, 0); // discard bad frames
+ (void) ifp;
+ return 0;
}
-static bool mip_driver_stm32_init(struct mip_if *ifp) {
- struct mip_driver_stm32_data *d = (struct mip_driver_stm32_data *) ifp->driver_data;
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/imxrt.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT) && MG_ENABLE_DRIVER_IMXRT
+struct imxrt_enet {
+ volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3],
+ ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR,
+ RESERVED6[15], TCR, RESERVED7[7], PALR, PAUR, OPD, TXIC0, TXIC1, TXIC2,
+ RESERVED8, RXIC0, RXIC1, RXIC2, RESERVED9[3], IAUR, IALR, GAUR, GALR,
+ RESERVED10[7], TFWR, RESERVED11[14], RDSR, TDSR, MRBR[2], RSFL, RSEM,
+ RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED12[3], TACC, RACC,
+ RESERVED13[15], RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT,
+ RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG,
+ RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255,
+ RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2048, RMON_T_GTE2048,
+ RMON_T_OCTETS, IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL,
+ IEEE_T_DEF, IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR,
+ IEEE_T_SQE, IEEE_T_FDXFC, IEEE_T_OCTETS_OK, RESERVED14[3], RMON_R_PACKETS,
+ RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, RMON_R_UNDERSIZE,
+ RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, RESERVED15, RMON_R_P64,
+ RMON_R_P65TO127, RMON_R_P128TO255, RMON_R_P256TO511, RMON_R_P512TO1023,
+ RMON_R_P1024TO2047, RMON_R_GTE2048, RMON_R_OCTETS, IEEE_R_DROP,
+ IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, IEEE_R_FDXFC,
+ IEEE_R_OCTETS_OK, RESERVED16[71], ATCR, ATVR, ATOFF, ATPER, ATCOR, ATINC,
+ ATSTMP, RESERVED17[122], TGSR, TCSR0, TCCR0, TCSR1, TCCR1, TCSR2, TCCR2,
+ TCSR3;
+};
+
+#undef ENET
+#if defined(MG_DRIVER_IMXRT_RT11) && MG_DRIVER_IMXRT_RT11
+#define ENET ((struct imxrt_enet *) (uintptr_t) 0x40424000U)
+#define ETH_DESC_CNT 5 // Descriptors count
+#else
+#define ENET ((struct imxrt_enet *) (uintptr_t) 0x402D8000U)
+#define ETH_DESC_CNT 4 // Descriptors count
+#endif
+
+#define ETH_PKT_SIZE 1536 // Max frame size, 64-bit aligned
+
+struct enet_desc {
+ uint16_t length; // Data length
+ uint16_t control; // Control and status
+ uint32_t *buffer; // Data ptr
+};
+
+// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
+#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
+
+// Descriptors: in non-cached area (TODO(scaprile)), (37.5.1.22.2 37.5.1.23.2)
+// Buffers: 64-byte aligned (37.3.14)
+static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
+static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_64BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
+static struct mg_tcpip_if *s_ifp; // MIP interface
+
+static uint16_t enet_read_phy(uint8_t addr, uint8_t reg) {
+ ENET->EIR |= MG_BIT(23); // MII interrupt clear
+ ENET->MMFR = (1 << 30) | (2 << 28) | (addr << 23) | (reg << 18) | (2 << 16);
+ while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
+ return ENET->MMFR & 0xffff;
+}
+
+static void enet_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ ENET->EIR |= MG_BIT(23); // MII interrupt clear
+ ENET->MMFR =
+ (1 << 30) | (1 << 28) | (addr << 23) | (reg << 18) | (2 << 16) | val;
+ while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
+}
+
+// MDC clock is generated from IPS Bus clock (ipg_clk); as per 802.3,
+// it must not exceed 2.5MHz
+// The PHY receives the PLL6-generated 50MHz clock
+static bool mg_tcpip_driver_imxrt_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_imxrt_data *d =
+ (struct mg_tcpip_driver_imxrt_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
- s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
- s_rxdesc[i][3] =
- (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ s_rxdesc[i].control = MG_BIT(15); // Own (E)
+ s_rxdesc[i].buffer = (uint32_t *) s_rxbuf[i]; // Point to data buffer
}
+ s_rxdesc[ETH_DESC_CNT - 1].control |= MG_BIT(13); // Wrap last descriptor
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- s_txdesc[i][3] =
- (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ // s_txdesc[i].control = MG_BIT(10); // Own (TC)
+ s_txdesc[i].buffer = (uint32_t *) s_txbuf[i];
}
-
- ETH->DMABMR |= BIT(0); // Software reset
- while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done
-
- // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
- int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
- ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
-
- // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
- // hardware checksum. Therefore, descriptor size is 4, not 8
- // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
- ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
- ETH->MACFCR = BIT(7); // Disable zero quarta pause
- // ETH->MACFFR = BIT(31); // Receive all
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
- ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
- ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
- ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE
- ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
- ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
-
- // MAC address filtering
- ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
- ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
- ((uint32_t) ifp->mac[2] << 16) |
- ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
- if (ifp->queue.len == 0) ifp->queue.len = 8192;
+ s_txdesc[ETH_DESC_CNT - 1].control |= MG_BIT(13); // Wrap last descriptor
+
+ ENET->ECR = MG_BIT(0); // Software reset, disable
+ while ((ENET->ECR & MG_BIT(0))) (void) 0; // Wait until done
+
+ // Set MDC clock divider. If user told us the value, use it.
+ // TODO(): Otherwise, guess (currently assuming max freq)
+ int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr;
+ ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks
+ struct mg_phy phy = {enet_read_phy, enet_write_phy};
+ mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY
+ // Select RMII mode, 100M, keep CRC, set max rx length, disable loop
+ ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2);
+ // ENET->RCR |= MG_BIT(3); // Receive all
+ ENET->TCR = MG_BIT(2); // Full-duplex
+ ENET->RDSR = (uint32_t) (uintptr_t) s_rxdesc;
+ ENET->TDSR = (uint32_t) (uintptr_t) s_txdesc;
+ ENET->MRBR[0] = ETH_PKT_SIZE; // Same size for RX/TX buffers
+ // MAC address filtering (bytes in reversed order)
+ ENET->PAUR = ((uint32_t) ifp->mac[4] << 24U) | (uint32_t) ifp->mac[5] << 16U;
+ ENET->PALR = (uint32_t) (ifp->mac[0] << 24U) |
+ ((uint32_t) ifp->mac[1] << 16U) |
+ ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
+ ENET->ECR = MG_BIT(8) | MG_BIT(1); // Little-endian CPU, Enable
+ ENET->EIMR = MG_BIT(25); // Set interrupt mask
+ ENET->RDAR = MG_BIT(24); // Receive Descriptors have changed
+ ENET->TDAR = MG_BIT(24); // Transmit Descriptors have changed
+ // ENET->OPD = 0x10014;
return true;
}
-static uint32_t s_txno;
-static size_t mip_driver_stm32_tx(const void *buf, size_t len, struct mip_if *ifp) {
- if (len > sizeof(s_txbuf[s_txno])) {
+// Transmit frame
+static size_t mg_tcpip_driver_imxrt_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ static int s_txno; // Current descriptor index
+ if (len > sizeof(s_txbuf[ETH_DESC_CNT])) {
MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // Frame is too big
- } else if ((s_txdesc[s_txno][0] & BIT(31))) {
- MG_ERROR(("No free descriptors"));
- // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
- len = 0; // All descriptors are busy, fail
+ len = (size_t) -1; // fail
+ } else if ((s_txdesc[s_txno].control & MG_BIT(15))) {
+ ifp->nerr++;
+ MG_ERROR(("No descriptors available"));
+ len = 0; // retry later
} else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS
- s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno].length = (uint16_t) len; // Set data len
+ // Table 37-34, R, L, TC (Ready, last, transmit CRC after frame
+ s_txdesc[s_txno].control |=
+ (uint16_t) (MG_BIT(15) | MG_BIT(11) | MG_BIT(10));
+ ENET->TDAR = MG_BIT(24); // Descriptor ring updated
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
- ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
- ETH->DMATPDR = 0; // and resume
- return len;
(void) ifp;
+ return len;
}
-static bool mip_driver_stm32_up(struct mip_if *ifp) {
- uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
- (void) ifp;
- return bsr & BIT(2) ? 1 : 0;
+static bool mg_tcpip_driver_imxrt_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_imxrt_data *d =
+ (struct mg_tcpip_driver_imxrt_data *) ifp->driver_data;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {enet_read_phy, enet_write_phy};
+ up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ // tmp = reg with flags set to the most likely situation: 100M full-duplex
+ // if(link is slow or half) set flags otherwise
+ // reg = tmp
+ uint32_t tcr = ENET->TCR | MG_BIT(2); // Full-duplex
+ uint32_t rcr = ENET->RCR & ~MG_BIT(9); // 100M
+ if (speed == MG_PHY_SPEED_10M) rcr |= MG_BIT(9); // 10M
+ if (full_duplex == false) tcr &= ~MG_BIT(2); // Half-duplex
+ ENET->TCR = tcr; // IRQ handler does not fiddle with these registers
+ ENET->RCR = rcr;
+ MG_DEBUG(("Link is %uM %s-duplex", rcr & MG_BIT(9) ? 10 : 100,
+ tcr & MG_BIT(2) ? "full" : "half"));
+ }
+ return up;
}
-void ETH_IRQHandler(void);
+void ENET_IRQHandler(void);
static uint32_t s_rxno;
-void ETH_IRQHandler(void) {
- qp_mark(QP_IRQTRIGGERED, 0);
- if (ETH->DMASR & BIT(6)) { // Frame received, loop
- ETH->DMASR = BIT(16) | BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
- // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
- // ETH->DMASR);
- mip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][0] = BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+void ENET_IRQHandler(void) {
+ ENET->EIR = MG_BIT(25); // Ack IRQ
+ // Frame received, loop
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ uint32_t r = s_rxdesc[s_rxno].control;
+ if (r & MG_BIT(15)) break; // exit when done
+ // skip partial/errored frames (Table 37-32)
+ if ((r & MG_BIT(11)) &&
+ !(r & (MG_BIT(5) | MG_BIT(4) | MG_BIT(2) | MG_BIT(1) | MG_BIT(0)))) {
+ size_t len = s_rxdesc[s_rxno].length;
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
+ s_rxdesc[s_rxno].control |= MG_BIT(15);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
- ETH->DMASR = BIT(7); // Clear possible RBUS while processing
- ETH->DMARPDR = 0; // and resume RX
+ ENET->RDAR = MG_BIT(24); // Receive Descriptors have changed
+ // If b24 == 0, descriptors were exhausted and probably frames were dropped
}
-struct mip_driver mip_driver_stm32 = {
- mip_driver_stm32_init, mip_driver_stm32_tx, mip_driver_rx, mip_driver_stm32_up};
+struct mg_tcpip_driver mg_tcpip_driver_imxrt = {mg_tcpip_driver_imxrt_init,
+ mg_tcpip_driver_imxrt_tx, NULL,
+ mg_tcpip_driver_imxrt_up};
+
#endif
#ifdef MG_ENABLE_LINES
-#line 1 "mip/driver_tm4c.c"
+#line 1 "src/drivers/phy.c"
#endif
-#if MG_ENABLE_MIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
-struct tm4c_emac {
- volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
- EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
- EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
- EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
- EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
- EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
- EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
- EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
- EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
- EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
- EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
- EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
- RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
- EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
- EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
- RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
- RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
- EMACEPHYIM, EMACEPHYIMSC;
+enum { // ID1 ID2
+ MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB
+ MG_PHY_DP83x = 0x2000, // 2000 a140 - TI DP83825I
+ MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I
+ MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720
+ MG_PHY_RTL8201 = 0x1C // 001c c816 - RTL8201
+};
+
+enum {
+ MG_PHY_REG_BCR = 0,
+ MG_PHY_REG_BSR = 1,
+ MG_PHY_REG_ID1 = 2,
+ MG_PHY_REG_ID2 = 3,
+ MG_PHY_DP83x_REG_PHYSTS = 16,
+ MG_PHY_DP83867_REG_PHYSTS = 17,
+ MG_PHY_DP83x_REG_RCSR = 23,
+ MG_PHY_DP83x_REG_LEDCR = 24,
+ MG_PHY_KSZ8x_REG_PC1R = 30,
+ MG_PHY_KSZ8x_REG_PC2R = 31,
+ MG_PHY_LAN87x_REG_SCSR = 31,
+ MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
+ MG_PHY_RTL8201_REG_PAGESEL = 31
+};
+
+static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
+ switch (id1) {
+ case MG_PHY_DP83x:
+ switch (id2) {
+ case MG_PHY_DP83867:
+ return "DP83867";
+ default:
+ return "DP83x";
+ }
+ case MG_PHY_KSZ8x:
+ return "KSZ8x";
+ case MG_PHY_LAN87x:
+ return "LAN87x";
+ case MG_PHY_RTL8201:
+ return "RTL8201";
+ default:
+ return "unknown";
+ }
+ (void) id2;
+}
+
+void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
+ uint16_t id1, id2;
+ phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(15)); // Reset PHY
+ while (phy->read_reg(phy_addr, MG_PHY_REG_BCR) & MG_BIT(15)) (void) 0;
+ // MG_PHY_REG_BCR[12]: Autonegotiation is default unless hw says otherwise
+
+ id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
+ id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
+ MG_INFO(("PHY ID: %#04x %#04x (%s)", id1, id2, mg_phy_id_to_str(id1, id2)));
+
+ if (id1 == MG_PHY_DP83x && id2 == MG_PHY_DP83867) {
+ phy->write_reg(phy_addr, 0x0d, 0x1f); // write 0x10d to IO_MUX_CFG (0x0170)
+ phy->write_reg(phy_addr, 0x0e, 0x170);
+ phy->write_reg(phy_addr, 0x0d, 0x401f);
+ phy->write_reg(phy_addr, 0x0e, 0x10d);
+ }
+
+ if (config & MG_PHY_CLOCKS_MAC) {
+ // Use PHY crystal oscillator (preserve defaults)
+ // nothing to do
+ } else { // MAC clocks PHY, PHY has no xtal
+ // Enable 50 MHz external ref clock at XI (preserve defaults)
+ if (id1 == MG_PHY_DP83x && id2 != MG_PHY_DP83867) {
+ phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0));
+ } else if (id1 == MG_PHY_KSZ8x) {
+ phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R,
+ MG_BIT(15) | MG_BIT(8) | MG_BIT(7));
+ } else if (id1 == MG_PHY_LAN87x) {
+ // nothing to do
+ } else if (id1 == MG_PHY_RTL8201) {
+ // assume PHY has been hardware strapped properly
+#if 0
+ phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 7); // Select page 7
+ phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x1ffa);
+ phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 0); // Select page 0
+#endif
+ }
+ }
+
+ if (config & MG_PHY_LEDS_ACTIVE_HIGH && id1 == MG_PHY_DP83x) {
+ phy->write_reg(phy_addr, MG_PHY_DP83x_REG_LEDCR,
+ MG_BIT(9) | MG_BIT(7)); // LED status, active high
+ } // Other PHYs do not support this feature
+}
+
+bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
+ uint8_t *speed) {
+ bool up = false;
+ uint16_t bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR);
+ if ((bsr & MG_BIT(5)) && !(bsr & MG_BIT(2))) // some PHYs latch down events
+ bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR); // read again
+ up = bsr & MG_BIT(2);
+ if (up && full_duplex != NULL && speed != NULL) {
+ uint16_t id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
+ if (id1 == MG_PHY_DP83x) {
+ uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
+ if (id2 == MG_PHY_DP83867) {
+ uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83867_REG_PHYSTS);
+ *full_duplex = physts & MG_BIT(13);
+ *speed = (physts & MG_BIT(15)) ? MG_PHY_SPEED_1000M
+ : (physts & MG_BIT(14)) ? MG_PHY_SPEED_100M
+ : MG_PHY_SPEED_10M;
+ } else {
+ uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83x_REG_PHYSTS);
+ *full_duplex = physts & MG_BIT(2);
+ *speed = (physts & MG_BIT(1)) ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
+ }
+ } else if (id1 == MG_PHY_KSZ8x) {
+ uint16_t pc1r = phy->read_reg(phy_addr, MG_PHY_KSZ8x_REG_PC1R);
+ *full_duplex = pc1r & MG_BIT(2);
+ *speed = (pc1r & 3) == 1 ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
+ } else if (id1 == MG_PHY_LAN87x) {
+ uint16_t scsr = phy->read_reg(phy_addr, MG_PHY_LAN87x_REG_SCSR);
+ *full_duplex = scsr & MG_BIT(4);
+ *speed = (scsr & MG_BIT(3)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
+ } else if (id1 == MG_PHY_RTL8201) {
+ uint16_t bcr = phy->read_reg(phy_addr, MG_PHY_REG_BCR);
+ *full_duplex = bcr & MG_BIT(8);
+ *speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
+ }
+ }
+ return up;
+}
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/ra.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RA) && MG_ENABLE_DRIVER_RA
+struct ra_etherc {
+ volatile uint32_t ECMR, RESERVED, RFLR, RESERVED1, ECSR, RESERVED2, ECSIPR,
+ RESERVED3, PIR, RESERVED4, PSR, RESERVED5[5], RDMLR, RESERVED6[3], IPGR,
+ APR, MPR, RESERVED7, RFCF, TPAUSER, TPAUSECR, BCFRR, RESERVED8[20], MAHR,
+ RESERVED9, MALR, RESERVED10, TROCR, CDCR, LCCR, CNDCR, RESERVED11, CEFCR,
+ FRECR, TSFRCR, TLFRCR, RFCR, MAFCR;
};
-#undef EMAC
-#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
-#undef BIT
-#define BIT(x) ((uint32_t) 1 << (x))
-#define ETH_PKT_SIZE 1540 // Max frame size
+struct ra_edmac {
+ volatile uint32_t EDMR, RESERVED, EDTRR, RESERVED1, EDRRR, RESERVED2, TDLAR,
+ RESERVED3, RDLAR, RESERVED4, EESR, RESERVED5, EESIPR, RESERVED6, TRSCER,
+ RESERVED7, RMFCR, RESERVED8, TFTR, RESERVED9, FDR, RESERVED10, RMCR,
+ RESERVED11[2], TFUCR, RFOCR, IOSR, FCFTR, RESERVED12, RPADIR, TRIMD,
+ RESERVED13[18], RBWAR, RDFAR, RESERVED14, TBRAR, TDFAR;
+};
+
+#undef ETHERC
+#define ETHERC ((struct ra_etherc *) (uintptr_t) 0x40114100U)
+#undef EDMAC
+#define EDMAC ((struct ra_edmac *) (uintptr_t) 0x40114000U)
+#undef RASYSC
+#define RASYSC ((uint32_t *) (uintptr_t) 0x4001E000U)
+#undef ICU_IELSR
+#define ICU_IELSR ((uint32_t *) (uintptr_t) 0x40006300U)
+
+#define ETH_PKT_SIZE 1536 // Max frame size, multiple of 32
#define ETH_DESC_CNT 4 // Descriptors count
-#define ETH_DS 4 // Descriptor size (words)
-static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
-static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
-static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
-static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
-static struct mip_if *s_ifp; // MIP interface
-enum { EPHY_ADDR = 0, EPHYBMCR = 0, EPHYBMSR = 1 }; // PHY constants
+// TODO(): handle these in a portable compiler-independent CMSIS-friendly way
+#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
+#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
-static inline void tm4cspin(volatile uint32_t count) {
+// Descriptors: 16-byte aligned
+// Buffers: 32-byte aligned (27.3.1)
+static volatile uint32_t s_rxdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
+static volatile uint32_t s_txdesc[ETH_DESC_CNT][4] MG_16BYTE_ALIGNED;
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
+static struct mg_tcpip_if *s_ifp; // MIP interface
+
+// fastest is 3 cycles (SUB + BNE) on a 3-stage pipeline or equivalent
+static inline void raspin(volatile uint32_t count) {
while (count--) (void) 0;
}
-
-static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
- EMAC->EMACMIIADDR &= (0xf << 2);
- EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
- EMAC->EMACMIIADDR |= BIT(0);
- while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
- return EMAC->EMACMIIDATA;
+// count to get the 200ns SMC semi-cycle period (2.5MHz) calling raspin():
+// SYS_FREQUENCY * 200ns / 3 = SYS_FREQUENCY / 15000000
+static uint32_t s_smispin;
+
+// Bit-banged SMI
+static void smi_preamble(void) {
+ unsigned int i = 32;
+ uint32_t pir = MG_BIT(1) | MG_BIT(2); // write, mdio = 1, mdc = 0
+ ETHERC->PIR = pir;
+ while (i--) {
+ pir &= ~MG_BIT(0); // mdc = 0
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ pir |= MG_BIT(0); // mdc = 1
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ }
}
-
-static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- EMAC->EMACMIIDATA = val;
- EMAC->EMACMIIADDR &= (0xf << 2);
- EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
- EMAC->EMACMIIADDR |= BIT(0);
- while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
+static void smi_wr(uint16_t header, uint16_t data) {
+ uint32_t word = (header << 16) | data;
+ smi_preamble();
+ unsigned int i = 32;
+ while (i--) {
+ uint32_t pir = MG_BIT(1) |
+ (word & 0x80000000 ? MG_BIT(2) : 0); // write, mdc = 0, data
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ pir |= MG_BIT(0); // mdc = 1
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ word <<= 1;
+ }
}
-
-static uint32_t get_sysclk(void) {
- struct sysctl {
- volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
- PLLFREQ1;
- } *sysctl = (struct sysctl *) 0x400FE000;
- uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
- if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
- uint32_t fin, vco, mdiv, n, q, psysdiv;
- uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
- if (pllsrc == 0) {
- clk = piosc;
- } else if (pllsrc == 3) {
- clk = mosc;
- } else {
- MG_ERROR(("Unsupported clock source"));
- }
- q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
- n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
- fin = clk / ((q + 1) * (n + 1));
- mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
- 0; // mint + (mfrac / 1024); MFRAC not supported
- psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
- vco = (uint32_t) ((uint64_t) fin * mdiv);
- return vco / (psysdiv + 1);
+static uint16_t smi_rd(uint16_t header) {
+ smi_preamble();
+ unsigned int i = 16; // 2 LSb as turnaround
+ uint32_t pir;
+ while (i--) {
+ pir = (i > 1 ? MG_BIT(1) : 0) |
+ (header & 0x8000
+ ? MG_BIT(2)
+ : 0); // mdc = 0, header, set read direction at turnaround
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ pir |= MG_BIT(0); // mdc = 1
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
+ header <<= 1;
}
- uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
- if (oscsrc == 0) {
- clk = piosc;
- } else if (oscsrc == 3) {
- clk = mosc;
- } else {
- MG_ERROR(("Unsupported clock source"));
+ i = 16;
+ uint16_t data = 0;
+ while (i--) {
+ data <<= 1;
+ pir = 0; // read, mdc = 0
+ ETHERC->PIR = pir;
+ raspin(s_smispin / 2); // 1/4 clock period, 300ns max access time
+ data |= (uint16_t)(ETHERC->PIR & MG_BIT(3) ? 1 : 0); // read mdio
+ raspin(s_smispin / 2); // 1/4 clock period
+ pir |= MG_BIT(0); // mdc = 1
+ ETHERC->PIR = pir;
+ raspin(s_smispin);
}
- uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
- return clk / (osysdiv + 1);
+ return data;
}
-// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
-// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
-// derived from the PIOSC (internal RC), and it can go above specs, the
-// datasheets specify a range of frequencies and activate one of a series of
-// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
-// setting based on SYSCLK with a +5% drift. If the user uses a different clock
-// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
-// (4.5% worst case drift)
-// The PHY receives the main oscillator (MOSC) (20.3.1)
-static int guess_mdc_cr(void) {
- uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
- uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
- uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
- int result = -1; // Invalid CR value
- if (sysclk < 25000000) {
- MG_ERROR(("SYSCLK too low"));
- } else {
- for (int i = 0; i < 4; i++) {
- if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
- result = crs[i];
- break;
- }
- }
- if (result < 0) MG_ERROR(("SYSCLK too high"));
- }
- MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
- return result;
+static uint16_t raeth_read_phy(uint8_t addr, uint8_t reg) {
+ return smi_rd((uint16_t)((1 << 14) | (2 << 12) | (addr << 7) | (reg << 2) | (2 << 0)));
}
-static bool mip_driver_tm4c_init(struct mip_if *ifp) {
- struct mip_driver_tm4c_data *d = (struct mip_driver_tm4c_data *) ifp->driver_data;
+static void raeth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ smi_wr((uint16_t)((1 << 14) | (1 << 12) | (addr << 7) | (reg << 2) | (2 << 0)), val);
+}
+
+// MDC clock is generated manually; as per 802.3, it must not exceed 2.5MHz
+static bool mg_tcpip_driver_ra_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_ra_data *d =
+ (struct mg_tcpip_driver_ra_data *) ifp->driver_data;
s_ifp = ifp;
+ // Init SMI clock timing. If user told us the clock value, use it.
+ // TODO(): Otherwise, guess
+ s_smispin = d->clock / 15000000;
+
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
- s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
- s_rxdesc[i][3] =
- (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
- // MG_DEBUG(("%d %p", i, s_rxdesc[i]));
+ s_rxdesc[i][0] = MG_BIT(31); // RACT
+ s_rxdesc[i][1] = ETH_PKT_SIZE << 16; // RBL
+ s_rxdesc[i][2] = (uint32_t) s_rxbuf[i]; // Point to data buffer
}
+ s_rxdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- s_txdesc[i][3] =
- (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ // TACT = 0
+ s_txdesc[i][2] = (uint32_t) s_txbuf[i];
}
-
- EMAC->EMACDMABUSMOD |= BIT(0); // Software reset
- while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done
-
- // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
- int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
- EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
-
- // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
- // hardware checksum. Therefore, descriptor size is 4, not 8
- // EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
- EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT
- EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause
- // EMAC->EMACFRAMEFLTR = BIT(31); // Receive all
- // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
- emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY)
- emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation
- EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
- EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
- EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE
- EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
- EMAC->EMACDMAOPMODE =
- BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
- EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
- EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
- ((uint32_t) ifp->mac[2] << 16) |
- ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
- // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
- // default. This also applies to the STM32 driver (at least for F7)
-
- if (ifp->queue.len == 0) ifp->queue.len = 8192;
+ s_txdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
+
+ EDMAC->EDMR = MG_BIT(0); // Software reset, wait 64 PCLKA clocks (27.2.1)
+ uint32_t sckdivcr = RASYSC[8]; // get divisors from SCKDIVCR (8.2.2)
+ uint32_t ick = 1 << ((sckdivcr >> 24) & 7); // sys_clock div
+ uint32_t pcka = 1 << ((sckdivcr >> 12) & 7); // pclka div
+ raspin((64U * pcka) / (3U * ick));
+ EDMAC->EDMR = MG_BIT(6); // Initialize, little-endian (27.2.1)
+
+ MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin));
+ struct mg_phy phy = {raeth_read_phy, raeth_write_phy};
+ mg_phy_init(&phy, d->phy_addr, 0); // MAC clocks PHY
+
+ // Select RMII mode,
+ ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC
+ // ETHERC->ECMR |= MG_BIT(0); // Receive all
+ ETHERC->RFLR = 1518; // Set max rx length
+
+ EDMAC->RDLAR = (uint32_t) (uintptr_t) s_rxdesc;
+ EDMAC->TDLAR = (uint32_t) (uintptr_t) s_txdesc;
+ // MAC address filtering (bytes in reversed order)
+ ETHERC->MAHR = (uint32_t) (ifp->mac[0] << 24U) |
+ ((uint32_t) ifp->mac[1] << 16U) |
+ ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
+ ETHERC->MALR = ((uint32_t) ifp->mac[4] << 8U) | ifp->mac[5];
+
+ EDMAC->TFTR = 0; // Store and forward (27.2.10)
+ EDMAC->FDR = 0x070f; // (27.2.11)
+ EDMAC->RMCR = MG_BIT(0); // (27.2.12)
+ ETHERC->ECMR |= MG_BIT(6) | MG_BIT(5); // TE RE
+ EDMAC->EESIPR = MG_BIT(18); // Enable Rx IRQ
+ EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
+ EDMAC->EDTRR = MG_BIT(0); // Transmit Descriptors have changed
return true;
}
-static uint32_t s_txno;
-static size_t mip_driver_tm4c_tx(const void *buf, size_t len, struct mip_if *ifp) {
- if (len > sizeof(s_txbuf[s_txno])) {
+// Transmit frame
+static size_t mg_tcpip_driver_ra_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ static int s_txno; // Current descriptor index
+ if (len > sizeof(s_txbuf[ETH_DESC_CNT])) {
MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // fail
- } else if ((s_txdesc[s_txno][0] & BIT(31))) {
+ len = (size_t) -1; // fail
+ } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
+ ifp->nerr++;
MG_ERROR(("No descriptors available"));
- // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
- // EMAC->EMACDMARIS);
- len = 0; // fail
+ len = 0; // retry later
} else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][0] =
- BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC
- s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = len << 16; // Set data len
+ s_txdesc[s_txno][0] |= MG_BIT(31) | 3 << 28; // (27.3.1.1) mark valid
+ EDMAC->EDTRR = MG_BIT(0); // Transmit request
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
- EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
- EMAC->EMACTXPOLLD = 0; // and resume
return len;
- (void) ifp;
}
-static bool mip_driver_tm4c_up(struct mip_if *ifp) {
- uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
- (void) ifp;
- return (bmsr & BIT(2)) ? 1 : 0;
+static bool mg_tcpip_driver_ra_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_ra_data *d =
+ (struct mg_tcpip_driver_ra_data *) ifp->driver_data;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {raeth_read_phy, raeth_write_phy};
+ up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ // tmp = reg with flags set to the most likely situation: 100M full-duplex
+ // if(link is slow or half) set flags otherwise
+ // reg = tmp
+ uint32_t ecmr = ETHERC->ECMR | MG_BIT(2) | MG_BIT(1); // 100M Full-duplex
+ if (speed == MG_PHY_SPEED_10M) ecmr &= ~MG_BIT(2); // 10M
+ if (full_duplex == false) ecmr &= ~MG_BIT(1); // Half-duplex
+ ETHERC->ECMR = ecmr; // IRQ handler does not fiddle with these registers
+ MG_DEBUG(("Link is %uM %s-duplex", ecmr & MG_BIT(2) ? 100 : 10,
+ ecmr & MG_BIT(1) ? "full" : "half"));
+ }
+ return up;
}
-void EMAC0_IRQHandler(void);
+void EDMAC_IRQHandler(void);
static uint32_t s_rxno;
-void EMAC0_IRQHandler(void) {
- qp_mark(QP_IRQTRIGGERED, 0);
- if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop
- EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
- // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
- // EMAC->EMACDMARIS);
- mip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][0] = BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+void EDMAC_IRQHandler(void) {
+ struct mg_tcpip_driver_ra_data *d =
+ (struct mg_tcpip_driver_ra_data *) s_ifp->driver_data;
+ EDMAC->EESR = MG_BIT(18); // Ack IRQ in EDMAC 1st
+ ICU_IELSR[d->irqno] &= ~MG_BIT(16); // Ack IRQ in ICU last
+ // Frame received, loop
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ uint32_t r = s_rxdesc[s_rxno][0];
+ if (r & MG_BIT(31)) break; // exit when done
+ // skip partial/errored frames (27.3.1.2)
+ if ((r & (MG_BIT(29) | MG_BIT(28)) && !(r & MG_BIT(27)))) {
+ size_t len = s_rxdesc[s_rxno][1] & 0xffff;
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); // CRC already stripped
}
+ s_rxdesc[s_rxno][0] |= MG_BIT(31);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
- EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing
- EMAC->EMACRXPOLLD = 0; // and resume RX
+ EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
+ // If b0 == 0, descriptors were exhausted and probably frames were dropped,
+ // (27.2.9 RMFCR counts them)
}
-struct mip_driver mip_driver_tm4c = {mip_driver_tm4c_init, mip_driver_tm4c_tx,
- mip_driver_rx, mip_driver_tm4c_up};
+struct mg_tcpip_driver mg_tcpip_driver_ra = {mg_tcpip_driver_ra_init,
+ mg_tcpip_driver_ra_tx, NULL,
+ mg_tcpip_driver_ra_up};
+
#endif
#ifdef MG_ENABLE_LINES
-#line 1 "mip/driver_w5500.c"
+#line 1 "src/drivers/same54.c"
#endif
-#if MG_ENABLE_MIP
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && MG_ENABLE_DRIVER_SAME54
-enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
+#include <sam.h>
-static void w5500_txn(struct mip_spi *s, uint8_t block, uint16_t addr, bool wr,
- void *buf, size_t len) {
- uint8_t *p = (uint8_t *) buf;
- uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
- (uint8_t) ((block << 3) | (wr ? 4 : 0))};
- s->begin(s->spi);
- for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
- for (size_t i = 0; i < len; i++) {
- uint8_t r = s->txn(s->spi, p[i]);
- if (!wr) p[i] = r;
- }
- s->end(s->spi);
+#define ETH_PKT_SIZE 1536 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 2 // Descriptor size (words)
+
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor
+
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
+
+#define MG_PHYREGBIT_BCR_DUPLEX_MODE MG_BIT(8)
+#define MG_PHYREGBIT_BCR_SPEED MG_BIT(13)
+#define MG_PHYREGBIT_BSR_LINK_STATUS MG_BIT(2)
+
+static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ GMAC_REGS->GMAC_MAN = GMAC_MAN_CLTTO_Msk |
+ GMAC_MAN_OP(2) | // Setting the read operation
+ GMAC_MAN_WTN(2) | GMAC_MAN_PHYA(addr) | // PHY address
+ GMAC_MAN_REGA(reg); // Setting the register
+ while (!(GMAC_REGS->GMAC_NSR & GMAC_NSR_IDLE_Msk)) (void) 0;
+ return GMAC_REGS->GMAC_MAN & GMAC_MAN_DATA_Msk; // Getting the read value
}
-// clang-format off
-static void w5500_wn(struct mip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
-static void w5500_w1(struct mip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
-static void w5500_w2(struct mip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
-static void w5500_rn(struct mip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
-static uint8_t w5500_r1(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
-static uint16_t w5500_r2(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
-// clang-format on
+#if 0
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ GMAC_REGS->GMAC_MAN = GMAC_MAN_CLTTO_Msk | GMAC_MAN_OP(1) | // Setting the write operation
+ GMAC_MAN_WTN(2) | GMAC_MAN_PHYA(addr) | // PHY address
+ GMAC_MAN_REGA(reg) | GMAC_MAN_DATA(val); // Setting the register
+ while (!(GMAC_REGS->GMAC_NSR & GMAC_NSR_IDLE_Msk)); // Waiting until the write op is complete
+}
+#endif
-static size_t w5500_rx(void *buf, size_t buflen, struct mip_if *ifp) {
- struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
- uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
- while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
- // printf("RSR: %d\n", (int) n);
- if (n > 0) {
- uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
- n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
- if (n <= len + 2 && n > 1) {
- r = (uint16_t) (n - 2);
- w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
+int get_clock_rate(struct mg_tcpip_driver_same54_data *d) {
+ if (d && d->mdc_cr >= 0 && d->mdc_cr <= 5) {
+ return d->mdc_cr;
+ } else {
+ // get MCLK from GCLK_GENERATOR 0
+ uint32_t div = 512;
+ uint32_t mclk;
+ if (!(GCLK_REGS->GCLK_GENCTRL[0] & GCLK_GENCTRL_DIVSEL_Msk)) {
+ div = ((GCLK_REGS->GCLK_GENCTRL[0] & 0x00FF0000) >> 16);
+ if (div == 0) div = 1;
}
- 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);
+ switch (GCLK_REGS->GCLK_GENCTRL[0] & GCLK_GENCTRL_SRC_Msk) {
+ case GCLK_GENCTRL_SRC_XOSC0_Val:
+ mclk = 32000000UL; /* 32MHz */
+ break;
+ case GCLK_GENCTRL_SRC_XOSC1_Val:
+ mclk = 32000000UL; /* 32MHz */
+ break;
+ case GCLK_GENCTRL_SRC_OSCULP32K_Val:
+ mclk = 32000UL;
+ break;
+ case GCLK_GENCTRL_SRC_XOSC32K_Val:
+ mclk = 32000UL;
+ break;
+ case GCLK_GENCTRL_SRC_DFLL_Val:
+ mclk = 48000000UL; /* 48MHz */
+ break;
+ case GCLK_GENCTRL_SRC_DPLL0_Val:
+ mclk = 200000000UL; /* 200MHz */
+ break;
+ case GCLK_GENCTRL_SRC_DPLL1_Val:
+ mclk = 200000000UL; /* 200MHz */
+ break;
+ default:
+ mclk = 200000000UL; /* 200MHz */
+ }
+
+ mclk /= div;
+ uint8_t crs[] = {0, 1, 2, 3, 4, 5}; // GMAC->NCFGR::CLK values
+ uint8_t dividers[] = {8, 16, 32, 48, 64, 96}; // Respective CLK dividers
+ for (int i = 0; i < 6; i++) {
+ if (mclk / dividers[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ return crs[i];
+ }
+ }
+
+ return 5;
}
- return r;
}
-static size_t w5500_tx(const void *buf, size_t buflen, struct mip_if *ifp) {
- struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
- uint16_t n = 0, len = (uint16_t) buflen;
- 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
- w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
- w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
- w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
- for (int i = 0; i < 40; i++) {
- uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
- if (ir == 0) continue;
- // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
- w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
- if (ir & 8) len = 0; // Timeout. Report error
- if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
+static bool mg_tcpip_driver_same54_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_same54_data *d =
+ (struct mg_tcpip_driver_same54_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_GMAC_Msk;
+ MCLK_REGS->MCLK_AHBMASK |= MCLK_AHBMASK_GMAC_Msk;
+ GMAC_REGS->GMAC_NCFGR = GMAC_NCFGR_CLK(get_clock_rate(d)); // Set MDC divider
+ GMAC_REGS->GMAC_NCR = 0; // Disable RX & TX
+ GMAC_REGS->GMAC_NCR |= GMAC_NCR_MPE_Msk; // Enable MDC & MDIO
+
+ for (int i = 0; i < ETH_DESC_CNT; i++) { // Init TX descriptors
+ s_txdesc[i][0] = (uint32_t) s_txbuf[i]; // Point to data buffer
+ s_txdesc[i][1] = MG_BIT(31); // OWN bit
}
- return len;
+ s_txdesc[ETH_DESC_CNT - 1][1] |= MG_BIT(30); // Last tx descriptor - wrap
+
+ GMAC_REGS->GMAC_DCFGR = GMAC_DCFGR_DRBS(0x18) // DMA recv buf 1536
+ | GMAC_DCFGR_RXBMS(GMAC_DCFGR_RXBMS_FULL_Val) |
+ GMAC_DCFGR_TXPBMS(1); // See #2487
+ for (int i = 0; i < ETH_DESC_CNT; i++) { // Init RX descriptors
+ s_rxdesc[i][0] = (uint32_t) s_rxbuf[i]; // Address of the data buffer
+ s_rxdesc[i][1] = 0; // Clear status
+ }
+ s_rxdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(1); // Last rx descriptor - wrap
+
+ GMAC_REGS->GMAC_TBQB = (uint32_t) s_txdesc; // about the descriptor addresses
+ GMAC_REGS->GMAC_RBQB = (uint32_t) s_rxdesc; // Let the controller know
+
+ GMAC_REGS->SA[0].GMAC_SAB =
+ MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]);
+ GMAC_REGS->SA[0].GMAC_SAT = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]);
+
+ GMAC_REGS->GMAC_UR &= ~GMAC_UR_MII_Msk; // Disable MII, use RMII
+ GMAC_REGS->GMAC_NCFGR |= GMAC_NCFGR_MAXFS_Msk | GMAC_NCFGR_MTIHEN_Msk |
+ GMAC_NCFGR_EFRHD_Msk | GMAC_NCFGR_CAF_Msk;
+ GMAC_REGS->GMAC_TSR = GMAC_TSR_HRESP_Msk | GMAC_TSR_UND_Msk |
+ GMAC_TSR_TXCOMP_Msk | GMAC_TSR_TFC_Msk |
+ GMAC_TSR_TXGO_Msk | GMAC_TSR_RLE_Msk |
+ GMAC_TSR_COL_Msk | GMAC_TSR_UBR_Msk;
+ GMAC_REGS->GMAC_RSR = GMAC_RSR_HNO_Msk | GMAC_RSR_RXOVR_Msk |
+ GMAC_RSR_REC_Msk | GMAC_RSR_BNA_Msk;
+ GMAC_REGS->GMAC_IDR = ~0U; // Disable interrupts, then enable required
+ GMAC_REGS->GMAC_IER = GMAC_IER_HRESP_Msk | GMAC_IER_ROVR_Msk |
+ GMAC_IER_TCOMP_Msk | GMAC_IER_TFC_Msk |
+ GMAC_IER_RLEX_Msk | GMAC_IER_TUR_Msk |
+ GMAC_IER_RXUBR_Msk | GMAC_IER_RCOMP_Msk;
+ GMAC_REGS->GMAC_NCR |= GMAC_NCR_TXEN_Msk | GMAC_NCR_RXEN_Msk;
+ NVIC_EnableIRQ(GMAC_IRQn);
+
+ return true;
}
-static bool w5500_init(struct mip_if *ifp) {
- struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
- s->end(s->spi);
- w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
- w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
- w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
- // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
- w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
- w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
- w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
- w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
- return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
+static size_t mg_tcpip_driver_same54_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][1] & MG_BIT(31)) == 0) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors"));
+ len = 0; // All descriptors are busy, fail
+ } else {
+ uint32_t status = len | MG_BIT(15); // Frame length, last chunk
+ if (s_txno == ETH_DESC_CNT - 1) status |= MG_BIT(30); // wrap
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = status;
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ __DSB(); // Ensure descriptors have been written
+ GMAC_REGS->GMAC_NCR |= GMAC_NCR_TSTART_Msk; // Enable transmission
+ return len;
}
-static bool w5500_up(struct mip_if *ifp) {
- struct mip_spi *spi = (struct mip_spi *) ifp->driver_data;
- uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
- return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
+static bool mg_tcpip_driver_same54_up(struct mg_tcpip_if *ifp) {
+ uint16_t bsr = eth_read_phy(MG_PHY_ADDR, MG_PHYREG_BSR);
+ bool up = bsr & MG_PHYREGBIT_BSR_LINK_STATUS ? 1 : 0;
+
+ // If PHY is ready, update NCFGR accordingly
+ if (ifp->state == MG_TCPIP_STATE_DOWN && up) {
+ uint16_t bcr = eth_read_phy(MG_PHY_ADDR, MG_PHYREG_BCR);
+ bool fd = bcr & MG_PHYREGBIT_BCR_DUPLEX_MODE ? 1 : 0;
+ bool spd = bcr & MG_PHYREGBIT_BCR_SPEED ? 1 : 0;
+ GMAC_REGS->GMAC_NCFGR = (GMAC_REGS->GMAC_NCFGR &
+ ~(GMAC_NCFGR_SPD_Msk | MG_PHYREGBIT_BCR_SPEED)) |
+ GMAC_NCFGR_SPD(spd) | GMAC_NCFGR_FD(fd);
+ }
+
+ return up;
+}
+
+void GMAC_Handler(void);
+void GMAC_Handler(void) {
+ uint32_t isr = GMAC_REGS->GMAC_ISR;
+ uint32_t rsr = GMAC_REGS->GMAC_RSR;
+ uint32_t tsr = GMAC_REGS->GMAC_TSR;
+ if (isr & GMAC_ISR_RCOMP_Msk) {
+ if (rsr & GMAC_ISR_RCOMP_Msk) {
+ for (uint8_t i = 0; i < ETH_DESC_CNT; i++) {
+ if ((s_rxdesc[s_rxno][0] & MG_BIT(0)) == 0) break;
+ size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1);
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
+ s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // Disown
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ }
+
+ if ((tsr & (GMAC_TSR_HRESP_Msk | GMAC_TSR_UND_Msk | GMAC_TSR_TXCOMP_Msk |
+ GMAC_TSR_TFC_Msk | GMAC_TSR_TXGO_Msk | GMAC_TSR_RLE_Msk |
+ GMAC_TSR_COL_Msk | GMAC_TSR_UBR_Msk)) != 0) {
+ // MG_INFO((" --> %#x %#x", s_txdesc[s_txno][1], tsr));
+ if (!(s_txdesc[s_txno][1] & MG_BIT(31))) s_txdesc[s_txno][1] |= MG_BIT(31);
+ }
+
+ GMAC_REGS->GMAC_RSR = rsr;
+ GMAC_REGS->GMAC_TSR = tsr;
}
-struct mip_driver mip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
+struct mg_tcpip_driver mg_tcpip_driver_same54 = {
+ mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL,
+ mg_tcpip_driver_same54_up};
#endif
#ifdef MG_ENABLE_LINES
-#line 1 "mip/mip.c"
+#line 1 "src/drivers/stm32f.c"
#endif
-#if MG_ENABLE_MIP
-
-#define MIP_ETHEMERAL_PORT 49152
-#define U16(ptr) ((((uint16_t) (ptr)[0]) << 8) | (ptr)[1])
-#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
-
-#ifndef MIP_QSIZE
-#define MIP_QSIZE (16 * 1024) // Queue size
-#endif
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
+ MG_ENABLE_DRIVER_STM32F
+struct stm32f_eth {
+ volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
+ MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
+ MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
+ MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
+ RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
+ RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
+ RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
+ PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
+ DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
+ DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
+ DMACHRBAR;
+};
+#undef ETH
+#define ETH ((struct stm32f_eth *) (uintptr_t) 0x40028000)
-#ifndef MIP_TCP_KEEPALIVE_MS
-#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
-#endif
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
-#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor
-struct connstate {
- uint32_t seq, ack; // TCP seq/ack counters
- uint64_t timer; // TCP keep-alive / ACK timer
- uint8_t mac[6]; // Peer MAC address
- 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
- uint8_t tmiss; // Number of keep-alive misses
- struct mg_iobuf raw; // For TLS only. Incoming raw data
-};
+static struct mg_tcpip_if *s_ifp; // MIP interface
-#pragma pack(push, 1)
+static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ ETH->MACMIIAR &= (7 << 2);
+ ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
+ ETH->MACMIIAR |= MG_BIT(0);
+ while (ETH->MACMIIAR & MG_BIT(0)) (void) 0;
+ return ETH->MACMIIDR & 0xffff;
+}
-struct lcp {
- uint8_t addr, ctrl, proto[2], code, id, len[2];
-};
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ ETH->MACMIIDR = val;
+ ETH->MACMIIAR &= (7 << 2);
+ ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1);
+ ETH->MACMIIAR |= MG_BIT(0);
+ while (ETH->MACMIIAR & MG_BIT(0)) (void) 0;
+}
-struct eth {
- uint8_t dst[6]; // Destination MAC address
- uint8_t src[6]; // Source MAC address
- uint16_t type; // Ethernet type
-};
+static uint32_t get_hclk(void) {
+ struct rcc {
+ volatile uint32_t CR, PLLCFGR, CFGR;
+ } *rcc = (struct rcc *) 0x40023800;
+ uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
-struct ip {
- uint8_t ver; // Version
- uint8_t tos; // Unused
- uint16_t len; // Length
- uint16_t id; // Unused
- uint16_t frag; // Fragmentation
- uint8_t ttl; // Time to live
- uint8_t proto; // Upper level protocol
- uint16_t csum; // Checksum
- uint32_t src; // Source IP
- uint32_t dst; // Destination IP
-};
+ if (rcc->CFGR & (1 << 2)) {
+ clk = hse;
+ } else if (rcc->CFGR & (1 << 3)) {
+ uint32_t vco, m, n, p;
+ m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
+ n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
+ p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
+ clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
+ vco = (uint32_t) ((uint64_t) clk * n / m);
+ clk = vco / p;
+ } else {
+ clk = hsi;
+ }
+ uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
+ if (hpre < 8) return clk;
-struct ip6 {
- uint8_t ver; // Version
- uint8_t opts[3]; // Options
- uint16_t len; // Length
- uint8_t proto; // Upper level protocol
- uint8_t ttl; // Time to live
- uint8_t src[16]; // Source IP
- uint8_t dst[16]; // Destination IP
-};
+ uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
+ return ((uint32_t) clk) >> ahbptab[hpre - 8];
+}
-struct icmp {
- uint8_t type;
- uint8_t code;
- uint16_t csum;
-};
+// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
+// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
+// from the HSI (internal RC), and it can go above specs, the datasheets
+// specify a range of frequencies and activate one of a series of dividers to
+// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
+// HCLK with a +5% drift. If the user uses a different clock from our
+// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
+// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
+static int guess_mdc_cr(void) {
+ uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
+ uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
+ uint32_t hclk = get_hclk(); // Guess system HCLK
+ int result = -1; // Invalid CR value
+ if (hclk < 25000000) {
+ MG_ERROR(("HCLK too low"));
+ } else {
+ for (int i = 0; i < 6; i++) {
+ if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
+ }
+ if (result < 0) MG_ERROR(("HCLK too high"));
+ }
+ MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
+ return result;
+}
-struct arp {
- uint16_t fmt; // Format of hardware address
- uint16_t pro; // Format of protocol address
- uint8_t hlen; // Length of hardware address
- uint8_t plen; // Length of protocol address
- uint16_t op; // Operation
- uint8_t sha[6]; // Sender hardware address
- uint32_t spa; // Sender protocol address
- uint8_t tha[6]; // Target hardware address
- uint32_t tpa; // Target protocol address
-};
+static bool mg_tcpip_driver_stm32f_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32f_data *d =
+ (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
+ uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
+ s_ifp = ifp;
-struct tcp {
- uint16_t sport; // Source port
- uint16_t dport; // Destination port
- uint32_t seq; // Sequence number
- uint32_t ack; // Acknowledgement number
- uint8_t off; // Data offset
- uint8_t flags; // TCP flags
-#define TH_FIN 0x01
-#define TH_SYN 0x02
-#define TH_RST 0x04
-#define TH_PUSH 0x08
-#define TH_ACK 0x10
-#define TH_URG 0x20
-#define TH_ECE 0x40
-#define TH_CWR 0x80
- uint16_t win; // Window
- uint16_t csum; // Checksum
- uint16_t urp; // Urgent pointer
-};
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = MG_BIT(31); // Own
+ s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained
+ s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] =
+ (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
-struct udp {
- uint16_t sport; // Source port
- uint16_t dport; // Destination port
- uint16_t len; // UDP length
- uint16_t csum; // UDP checksum
-};
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
+ s_txdesc[i][3] =
+ (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
-struct dhcp {
- uint8_t op, htype, hlen, hops;
- uint32_t xid;
- uint16_t secs, flags;
- uint32_t ciaddr, yiaddr, siaddr, giaddr;
- uint8_t hwaddr[208];
- uint32_t magic;
- uint8_t options[32];
-};
+ ETH->DMABMR |= MG_BIT(0); // Software reset
+ while ((ETH->DMABMR & MG_BIT(0)) != 0) (void) 0; // Wait until done
-#pragma pack(pop)
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
-struct pkt {
- struct mg_str raw; // Raw packet data
- struct mg_str pay; // Payload data
- struct eth *eth;
- struct llc *llc;
- struct arp *arp;
- struct ip *ip;
- struct ip6 *ip6;
- struct icmp *icmp;
- struct tcp *tcp;
- struct udp *udp;
- struct dhcp *dhcp;
-};
+ // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
+ // hardware checksum. Therefore, descriptor size is 4, not 8
+ // ETH->DMABMR = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) |
+ // MG_BIT(25);
+ ETH->MACIMR = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT
+ ETH->MACFCR = MG_BIT(7); // Disable zero quarta pause
+ // ETH->MACFFR = MG_BIT(31); // Receive all
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ mg_phy_init(&phy, phy_addr, MG_PHY_CLOCKS_MAC);
+ ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
+ ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
+ ETH->DMAIER = MG_BIT(6) | MG_BIT(16); // RIE, NISE
+ ETH->MACCR =
+ MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast
+ ETH->DMAOMR =
+ MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF
-static void q_copyin(struct queue *q, const uint8_t *buf, size_t len,
- size_t head) {
- size_t left = q->len - head;
- memcpy(&q->buf[head], buf, left < len ? left : len);
- if (left < len) memcpy(q->buf, &buf[left], len - left);
+ // MAC address filtering
+ ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ return true;
}
-static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) {
- size_t left = q->len - tail;
- memcpy(buf, &q->buf[tail], left < len ? left : len);
- if (left < len) memcpy(&buf[left], q->buf, len - left);
+static size_t mg_tcpip_driver_stm32f_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors"));
+ // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
+ len = 0; // All descriptors are busy, fail
+ } else {
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][0] = MG_BIT(20) | MG_BIT(28) | MG_BIT(29); // Chain,FS,LS
+ s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ MG_DSB(); // ensure descriptors have been written
+ ETH->DMASR = MG_BIT(2) | MG_BIT(5); // Clear any prior TBUS/TUS
+ ETH->DMATPDR = 0; // and resume
+ return len;
}
-static bool q_write(struct queue *q, const void *buf, size_t len) {
- bool success = false;
- size_t left = (q->len - q->head + q->tail - 1) % q->len;
- if (len + sizeof(size_t) <= left) {
- 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->head = (q->head + sizeof(len) + len) % q->len;
- success = true;
+static bool mg_tcpip_driver_stm32f_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32f_data *d =
+ (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
+ uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ // tmp = reg with flags set to the most likely situation: 100M full-duplex
+ // if(link is slow or half) set flags otherwise
+ // reg = tmp
+ uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex
+ if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
+ if (full_duplex == false) maccr &= ~MG_BIT(11); // Half-duplex
+ ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10,
+ maccr & MG_BIT(11) ? "full" : "half"));
}
- return success;
+ return up;
}
-#ifdef MIP_QPROFILE
-static inline size_t q_space(struct queue *q) {
- return q->tail > q->head ? q->tail - q->head : q->tail + (q->len - q->head);
+#ifdef __riscv
+__attribute__((interrupt())) // For RISCV CH32V307, which share the same MAC
+#endif
+void ETH_IRQHandler(void);
+void ETH_IRQHandler(void) {
+ if (ETH->DMASR & MG_BIT(6)) { // Frame received, loop
+ ETH->DMASR = MG_BIT(16) | MG_BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) ==
+ (MG_BIT(8) | MG_BIT(9))) &&
+ !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames
+ uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1));
+ // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
+ // ETH->DMASR);
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
+ }
+ s_rxdesc[s_rxno][0] = MG_BIT(31);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ // Cleanup flags
+ ETH->DMASR = MG_BIT(16) // NIS, normal interrupt summary
+ | MG_BIT(7); // Clear possible RBUS while processing
+ ETH->DMARPDR = 0; // and resume RX
}
+
+struct mg_tcpip_driver mg_tcpip_driver_stm32f = {
+ mg_tcpip_driver_stm32f_init, mg_tcpip_driver_stm32f_tx, NULL,
+ mg_tcpip_driver_stm32f_up};
#endif
-static inline size_t q_avail(struct queue *q) {
- size_t n = 0;
- if (q->tail != q->head) q_copyout(q, (uint8_t *) &n, sizeof(n), q->tail);
- return n;
-}
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/stm32h.c"
+#endif
-static size_t q_read(struct queue *q, void *buf) {
- size_t n = q_avail(q);
- if (n > 0) {
- q_copyout(q, (uint8_t *) buf, n, (q->tail + sizeof(n)) % q->len);
- q->tail = (q->tail + sizeof(n) + n) % q->len;
- }
- return n;
-}
-static struct mg_str mkstr(void *buf, size_t len) {
- struct mg_str str = {(char *) buf, len};
- return str;
-}
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
+ MG_ENABLE_DRIVER_STM32H
+struct stm32h_eth {
+ volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
+ RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
+ RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
+ MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
+ MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
+ MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
+ RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
+ MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
+ MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
+ RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
+ RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
+ MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
+ RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
+ RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
+ MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
+ MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
+ RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
+ MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
+ RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
+ MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
+ RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
+ MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
+ RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
+ DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
+ DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
+ DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
+ RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
+ DMACMFCR;
+};
+#undef ETH
+#define ETH \
+ ((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
-static void mkpay(struct pkt *pkt, void *p) {
- pkt->pay = mkstr(p, (size_t) (&pkt->raw.ptr[pkt->raw.len] - (char *) p));
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
+
+static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static struct mg_tcpip_if *s_ifp; // MIP interface
+
+static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ ETH->MACMDIOAR &= (0xF << 8);
+ ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
+ ETH->MACMDIOAR |= MG_BIT(0);
+ while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0;
+ return (uint16_t) ETH->MACMDIODR;
}
-static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
- const uint8_t *p = (const uint8_t *) buf;
- for (size_t i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8);
- return sum;
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ ETH->MACMDIODR = val;
+ ETH->MACMDIOAR &= (0xF << 8);
+ ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
+ ETH->MACMDIOAR |= MG_BIT(0);
+ while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0;
}
-static uint16_t csumfin(uint32_t sum) {
- while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
- return mg_htons(~sum & 0xffff);
+static uint32_t get_hclk(void) {
+ struct rcc {
+ volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
+ D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
+ PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
+ D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
+ RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
+ APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
+ RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
+ APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
+ AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
+ RESERVED13[4];
+ } *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
+ uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
+ csi = 4000000 /* 4MHz */;
+ unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
+
+ if (sel == 1) {
+ clk = csi;
+ } else if (sel == 2) {
+ clk = hse;
+ } else if (sel == 3) {
+ uint32_t vco, m, n, p;
+ unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
+ m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
+ n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
+ ((rcc->PLLCFGR & MG_BIT(0)) ? 1 : 0); // round-up in fractional mode
+ p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
+ if (src == 1) {
+ clk = csi;
+ } else if (src == 2) {
+ clk = hse;
+ } else {
+ clk = hsi;
+ clk >>= ((rcc->CR & 3) >> 3);
+ }
+ vco = (uint32_t) ((uint64_t) clk * n / m);
+ clk = vco / p;
+ } else {
+ clk = hsi;
+ clk >>= ((rcc->CR & 3) >> 3);
+ }
+ const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
+ uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
+ if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
+ MG_DEBUG(("D1 CLK: %u", clk));
+ uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
+ if (hpre < 8) return clk;
+ return ((uint32_t) clk) >> cptab[hpre - 8];
+}
+
+// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
+// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
+// be derived from HSI or CSI (internal RC) clocks, and those can go above
+// specs, the datasheets specify a range of frequencies and activate one of a
+// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
+// divider setting based on HCLK with some drift. If the user uses a different
+// clock from our defaults, needs to set the macros on top. Valid for
+// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
+// worst case drift @ max temp)
+static int guess_mdc_cr(void) {
+ const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
+ const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
+ uint32_t hclk = get_hclk(); // Guess system HCLK
+ int result = -1; // Invalid CR value
+ for (int i = 0; i < 6; i++) {
+ if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
+ }
+ if (result < 0) MG_ERROR(("HCLK too high"));
+ MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
+ return result;
}
-static uint16_t ipcsum(const void *buf, size_t len) {
- uint32_t sum = csumup(0, buf, len);
- return csumfin(sum);
-}
+static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32h_data *d =
+ (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
+ s_ifp = ifp;
+ uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
+ uint8_t phy_conf = d == NULL ? MG_PHY_CLOCKS_MAC : d->phy_conf;
-// ARP cache is organised as a doubly linked list. A successful cache lookup
-// moves an entry to the head of the list. New entries are added by replacing
-// the last entry in the list with a new IP/MAC.
-// ARP cache format: | prev | next | Entry0 | Entry1 | .... | EntryN |
-// ARP entry format: | prev | next | IP (4bytes) | MAC (6bytes) |
-// prev and next are 1-byte offsets in the cache, so cache size is max 256 bytes
-// ARP entry size is 12 bytes
-static void arp_cache_init(uint8_t *p, int n, int size) {
- for (int i = 0; i < n; i++) p[2 + i * size] = (uint8_t) (2 + (i - 1) * size);
- for (int i = 0; i < n; i++) p[3 + i * size] = (uint8_t) (2 + (i + 1) * size);
- p[0] = p[2] = (uint8_t) (2 + (n - 1) * size);
- p[1] = p[3 + (n - 1) * size] = 2;
-}
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
+ }
-#if 0
-static inline void arp_cache_dump(const uint8_t *p) {
- MG_INFO(("ARP cache:"));
- for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) {
- MG_INFO((" %I -> %A", 4, &p[j + 2], &p[j + 6]));
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
-}
-#endif
-static const uint8_t bcastmac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ ETH->DMAMR |= MG_BIT(0); // Software reset
+ while ((ETH->DMAMR & MG_BIT(0)) != 0) (void) 0; // Wait until done
-static uint8_t *arp_cache_find(struct mip_if *ifp, uint32_t ip) {
- uint8_t *p = ifp->arp_cache;
- if (ip == 0) return NULL;
- // use broadcast MAC for local and global broadcast IP
- if (ip == 0xffffffffU || ip == (ifp->ip | ~ifp->mask))
- return (uint8_t *) bcastmac;
- for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) {
- if (memcmp(p + j + 2, &ip, sizeof(ip)) == 0) {
- p[1] = j, p[0] = p[j]; // Found entry! Point list head to us
- // MG_DEBUG(("ARP find: %I @ %A", 4, &ip, &p[j + 6]));
- return p + j + 6; // And return MAC address
- }
- }
- return NULL;
-}
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
+
+ // NOTE(scaprile): We do not use timing facilities so the DMA engine does not
+ // re-write buffer address
+ ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
+ ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed
+ ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
+ ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause
+ // ETH->MACPFR = MG_BIT(31); // Receive all
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ mg_phy_init(&phy, phy_addr, phy_conf);
+ ETH->DMACRDLAR =
+ (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
+ ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
+ ETH->DMACRDTPR =
+ (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
+ 1]; // last valid descriptor address
+ ETH->DMACTDLAR =
+ (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
+ ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
+ ETH->DMACTDTPR =
+ (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
+ ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
+ ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE
+ ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) |
+ MG_BIT(15); // RE, TE, Duplex, Fast, Reserved
+ ETH->MTLTQOMR |= MG_BIT(1); // TSF
+ ETH->MTLRQOMR |= MG_BIT(5); // RSF
+ ETH->DMACTCR |= MG_BIT(0); // ST
+ ETH->DMACRCR |= MG_BIT(0); // SR
-static void arp_cache_add(struct mip_if *ifp, uint32_t ip, uint8_t mac[6]) {
- uint8_t *p = ifp->arp_cache;
- if (ip == 0 || ip == ~0U) return; // Bad IP
- if (arp_cache_find(ifp, ip) != NULL) return; // Already exists, do nothing
- memcpy(p + p[0] + 2, &ip, sizeof(ip)); // Replace last entry: IP address
- memcpy(p + p[0] + 6, mac, 6); // And MAC address
- p[1] = p[0], p[0] = p[p[1]]; // Point list head to us
- MG_DEBUG(("ARP cache: added %I @ %A", 4, &ip, mac));
+ // MAC address filtering
+ ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ return true;
}
-static size_t ether_output(struct mip_if *ifp, size_t len) {
- // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
- // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
- // mg_hexdump(ifp->tx.ptr, len);
- return ifp->driver->tx(ifp->tx.ptr, len, ifp);
+static uint32_t s_txno;
+static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][3] & MG_BIT(31))) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
+ s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
+ for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
+ len = 0; // All descriptors are busy, fail
+ } else {
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD
+ s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
+ ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS
+ ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
+ return len;
+ (void) ifp;
}
-static void arp_ask(struct mip_if *ifp, uint32_t ip) {
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct arp *arp = (struct arp *) (eth + 1);
- memset(eth->dst, 255, sizeof(eth->dst));
- memcpy(eth->src, ifp->mac, sizeof(eth->src));
- eth->type = mg_htons(0x806);
- memset(arp, 0, sizeof(*arp));
- arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6,
- arp->plen = 4;
- arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
- memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
- ether_output(ifp, PDIFF(eth, arp + 1));
+static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_stm32h_data *d =
+ (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
+ uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ // tmp = reg with flags set to the most likely situation: 100M full-duplex
+ // if(link is slow or half) set flags otherwise
+ // reg = tmp
+ uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex
+ if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
+ if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex
+ ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10,
+ maccr & MG_BIT(13) ? "full" : "half"));
+ }
+ return up;
}
-static void onstatechange(struct mip_if *ifp) {
- if (ifp->state == MIP_STATE_READY) {
- MG_INFO(("READY, IP: %I", 4, &ifp->ip));
- MG_INFO((" GW: %I", 4, &ifp->gw));
- if (ifp->lease_expire > ifp->now) {
- MG_INFO(
- (" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
+void ETH_IRQHandler(void);
+static uint32_t s_rxno;
+void ETH_IRQHandler(void) {
+ if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
+ ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) ==
+ (MG_BIT(28) | MG_BIT(29))) &&
+ !(s_rxdesc[s_rxno][3] & MG_BIT(15))) { // skip partial/errored frames
+ uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1);
+ // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
+ // ETH->DMACSR));
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
+ }
+ s_rxdesc[s_rxno][3] =
+ MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
- arp_ask(ifp, ifp->gw);
- } else if (ifp->state == MIP_STATE_UP) {
- MG_ERROR(("Link up"));
- } else if (ifp->state == MIP_STATE_DOWN) {
- MG_ERROR(("Link down"));
}
+ ETH->DMACSR =
+ MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
+ ETH->DMACRDTPR =
+ (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
}
-static struct ip *tx_ip(struct mip_if *ifp, uint8_t proto, uint32_t ip_src,
- uint32_t ip_dst, size_t plen) {
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct ip *ip = (struct ip *) (eth + 1);
- uint8_t *mac = arp_cache_find(ifp, ip_dst); // Dst IP in ARP cache ?
- if (!mac && ((ip_dst & ifp->mask) == (ifp->ip & ifp->mask)))
- arp_ask(ifp, ip_dst); // Same net, lookup
- if (!mac) mac = arp_cache_find(ifp, ifp->gw); // Use gateway MAC
- if (!mac) arp_ask(ifp, ifp->gw); // Not found? lookup
- if (mac) memcpy(eth->dst, mac, sizeof(eth->dst)); // Found? Use it
- if (!mac) memset(eth->dst, 255, sizeof(eth->dst)); // No? Use broadcast
- memcpy(eth->src, ifp->mac, sizeof(eth->src)); // TODO(cpq): ARP lookup
- eth->type = mg_htons(0x800);
- memset(ip, 0, sizeof(*ip));
- ip->ver = 0x45; // Version 4, header length 5 words
- ip->frag = 0x40; // Don't fragment
- ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
- ip->ttl = 64;
- ip->proto = proto;
- ip->src = ip_src;
- ip->dst = ip_dst;
- ip->csum = ipcsum(ip, sizeof(*ip));
- return ip;
-}
+struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
+ mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL,
+ mg_tcpip_driver_stm32h_up};
+#endif
-static void tx_udp(struct mip_if *ifp, uint32_t ip_src, uint16_t sport,
- uint32_t ip_dst, uint16_t dport, const void *buf,
- size_t len) {
- struct ip *ip = tx_ip(ifp, 17, ip_src, ip_dst, len + sizeof(struct udp));
- struct udp *udp = (struct udp *) (ip + 1);
- // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
- udp->sport = sport;
- udp->dport = dport;
- udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
- udp->csum = 0;
- uint32_t cs = csumup(0, udp, sizeof(*udp));
- cs = csumup(cs, buf, len);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
- udp->csum = csumfin(cs);
- memmove(udp + 1, buf, len);
- // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
- ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len);
-}
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/tm4c.c"
+#endif
-static void tx_dhcp(struct mip_if *ifp, uint32_t src, uint32_t dst,
- uint8_t *opts, size_t optslen) {
- struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
- dhcp.magic = mg_htonl(0x63825363);
- memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac));
- memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
- memcpy(&dhcp.options, opts, optslen);
- tx_udp(ifp, src, mg_htons(68), dst, mg_htons(67), &dhcp, sizeof(dhcp));
-}
-static void tx_dhcp_request(struct mip_if *ifp, uint32_t src, uint32_t dst) {
- 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
- };
- memcpy(opts + 14, &dst, sizeof(dst));
- memcpy(opts + 20, &src, sizeof(src));
- tx_dhcp(ifp, src, dst, opts, sizeof(opts));
-}
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
+struct tm4c_emac {
+ volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
+ EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
+ EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
+ EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
+ EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
+ EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
+ EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
+ EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
+ EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
+ EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
+ EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
+ EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
+ RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
+ EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
+ EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
+ RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
+ RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
+ EMACEPHYIM, EMACEPHYIMSC;
+};
+#undef EMAC
+#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
-static void tx_dhcp_discover(struct mip_if *ifp) {
- uint8_t opts[] = {
- 53, 1, 1, // Type: DHCP discover
- 55, 2, 1, 3, // Parameters: ip, mask
- 255 // End of options
- };
- tx_dhcp(ifp, 0, 0xffffffff, opts, sizeof(opts));
- MG_DEBUG(("DHCP discover sent"));
-}
+#define ETH_PKT_SIZE 1540 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
-static void rx_arp(struct mip_if *ifp, struct pkt *pkt) {
- if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
- // ARP request. Make a response, then send
- MG_DEBUG(("ARP op %d %I: %I?", mg_ntohs(pkt->arp->op), 4, &pkt->arp->spa, 4,
- &pkt->arp->tpa));
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct arp *arp = (struct arp *) (eth + 1);
- memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
- memcpy(eth->src, ifp->mac, sizeof(eth->src));
- eth->type = mg_htons(0x806);
- *arp = *pkt->arp;
- arp->op = mg_htons(2);
- memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha));
- memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha));
- arp->tpa = pkt->arp->spa;
- arp->spa = ifp->ip;
- MG_DEBUG(("ARP response: we're %I", 4, &ifp->ip));
- ether_output(ifp, PDIFF(eth, arp + 1));
- } else if (pkt->arp->op == mg_htons(2)) {
- if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
- // MG_INFO(("ARP RESPONSE"));
- arp_cache_add(ifp, pkt->arp->spa, pkt->arp->sha);
- }
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum {
+ EPHY_ADDR = 0,
+ EPHYBMCR = 0,
+ EPHYBMSR = 1,
+ EPHYSTS = 16
+}; // PHY constants
+
+static inline void tm4cspin(volatile uint32_t count) {
+ while (count--) (void) 0;
}
-static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("ICMP %d", (int) len));
- if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
- size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
- size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
- if (plen > space) plen = space;
- struct ip *ip =
- tx_ip(ifp, 1, ifp->ip, pkt->ip->src, sizeof(struct icmp) + plen);
- struct icmp *icmp = (struct icmp *) (ip + 1);
- memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
- memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX
- icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
- ether_output(ifp, hlen + plen);
- }
+static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
+ EMAC->EMACMIIADDR &= (0xf << 2);
+ EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
+ EMAC->EMACMIIADDR |= MG_BIT(0);
+ while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1);
+ return EMAC->EMACMIIDATA;
}
-static void rx_dhcp_client(struct mip_if *ifp, struct pkt *pkt) {
- uint32_t ip = 0, gw = 0, mask = 0;
- uint8_t *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
- if (end < (uint8_t *) (pkt->dhcp + 1)) return;
- while (p + 1 < end && p[0] != 255) { // Parse options
- if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
- memcpy(&mask, p + 2, sizeof(mask));
- } 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 (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
- uint32_t lease = 0;
- memcpy(&lease, p + 2, sizeof(lease));
- ifp->lease_expire = ifp->now + mg_ntohl(lease) * 1000;
- }
- p += p[1] + 2;
- }
- if (ip && mask && gw && ifp->ip == 0) {
- arp_cache_add(ifp, pkt->dhcp->siaddr, ((struct eth *) pkt->raw.ptr)->src);
- ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
- ifp->state = MIP_STATE_READY;
- onstatechange(ifp);
- tx_dhcp_request(ifp, ip, pkt->dhcp->siaddr);
- }
+static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
+ EMAC->EMACMIIDATA = val;
+ EMAC->EMACMIIADDR &= (0xf << 2);
+ EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1);
+ EMAC->EMACMIIADDR |= MG_BIT(0);
+ while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1);
}
-// Simple DHCP server that assigns a next IP address: ifp->ip + 1
-static void rx_dhcp_server(struct mip_if *ifp, struct pkt *pkt) {
- uint8_t op = 0, *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
- if (end < (uint8_t *) (pkt->dhcp + 1)) return;
- // struct dhcp *req = pkt->dhcp;
- struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
- res.yiaddr = ifp->ip;
- ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
- while (p + 1 < end && p[0] != 255) { // Parse options
- if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
- op = p[2];
+static uint32_t get_sysclk(void) {
+ struct sysctl {
+ volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
+ PLLFREQ1;
+ } *sysctl = (struct sysctl *) 0x400FE000;
+ uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
+ if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
+ uint32_t fin, vco, mdiv, n, q, psysdiv;
+ uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
+ if (pllsrc == 0) {
+ clk = piosc;
+ } else if (pllsrc == 3) {
+ clk = mosc;
+ } else {
+ MG_ERROR(("Unsupported clock source"));
}
- p += p[1] + 2;
- }
- if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
- uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
- uint8_t opts[] = {
- 53, 1, msg, // Message type
- 1, 4, 0, 0, 0, 0, // Subnet mask
- 54, 4, 0, 0, 0, 0, // Server ID
- 12, 3, 'm', 'i', 'p', // Host name: "mip"
- 51, 4, 255, 255, 255, 255, // Lease time
- 255 // End of options
- };
- memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
- memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
- memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
- memcpy(&res.options, opts, sizeof(opts));
- res.magic = pkt->dhcp->magic;
- res.xid = pkt->dhcp->xid;
- arp_cache_add(ifp, res.yiaddr, pkt->eth->src);
- tx_udp(ifp, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, mg_htons(68),
- &res, sizeof(res));
+ q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
+ n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
+ fin = clk / ((q + 1) * (n + 1));
+ mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
+ 0; // mint + (mfrac / 1024); MFRAC not supported
+ psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
+ vco = (uint32_t) ((uint64_t) fin * mdiv);
+ return vco / (psysdiv + 1);
}
-}
-
-static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
- bool lsn) {
- struct mg_connection *c = NULL;
- for (c = mgr->conns; c != NULL; c = c->next) {
- if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
- if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
- lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport))
- break;
+ uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
+ if (oscsrc == 0) {
+ clk = piosc;
+ } else if (oscsrc == 3) {
+ clk = mosc;
+ } else {
+ MG_ERROR(("Unsupported clock source"));
}
- return c;
+ uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
+ return clk / (osysdiv + 1);
}
-static void rx_udp(struct mip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
- if (c == NULL) {
- // No UDP listener on this port. Should send ICMP, but keep silent.
- } else if (c != NULL) {
- c->rem.port = pkt->udp->sport;
- c->rem.ip = pkt->ip->src;
- if (c->recv.len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "max_recv_buf_size reached");
- } else if (c->recv.size - c->recv.len < pkt->pay.len &&
- !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
- mg_error(c, "oom");
- } else {
- memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len);
- c->recv.len += pkt->pay.len;
- mg_call(c, MG_EV_READ, &pkt->pay.len);
+// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
+// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
+// derived from the PIOSC (internal RC), and it can go above specs, the
+// datasheets specify a range of frequencies and activate one of a series of
+// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
+// setting based on SYSCLK with a +5% drift. If the user uses a different clock
+// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
+// (4.5% worst case drift)
+// The PHY receives the main oscillator (MOSC) (20.3.1)
+static int guess_mdc_cr(void) {
+ uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
+ uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
+ uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
+ int result = -1; // Invalid CR value
+ if (sysclk < 25000000) {
+ MG_ERROR(("SYSCLK too low"));
+ } else {
+ for (int i = 0; i < 4; i++) {
+ if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
+ result = crs[i];
+ break;
+ }
}
+ if (result < 0) MG_ERROR(("SYSCLK too high"));
}
+ MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
+ return result;
}
-static size_t tx_tcp(struct mip_if *ifp, uint32_t dst_ip, uint8_t flags,
- uint16_t sport, uint16_t dport, uint32_t seq, uint32_t ack,
- const void *buf, size_t len) {
- struct ip *ip = tx_ip(ifp, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
- struct tcp *tcp = (struct tcp *) (ip + 1);
- memset(tcp, 0, sizeof(*tcp));
- if (buf != NULL && len) memmove(tcp + 1, buf, len);
- tcp->sport = sport;
- tcp->dport = dport;
- tcp->seq = seq;
- tcp->ack = ack;
- tcp->flags = flags;
- tcp->win = mg_htons(8192);
- tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4);
- uint32_t cs = 0;
- uint16_t n = (uint16_t) (sizeof(*tcp) + len);
- uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
- cs = csumup(cs, tcp, n);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs = csumup(cs, pseudo, sizeof(pseudo));
- tcp->csum = csumfin(cs);
- return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len);
-}
+static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_tm4c_data *d =
+ (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
+ s_ifp = ifp;
-static size_t tx_tcp_pkt(struct mip_if *ifp, struct pkt *pkt, uint8_t flags,
- uint32_t seq, const void *buf, size_t len) {
- uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
- return tx_tcp(ifp, pkt->ip->src, flags, pkt->tcp->dport, pkt->tcp->sport, seq,
- mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), buf, len);
-}
+ // Init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = MG_BIT(31); // Own
+ s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained
+ s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
+ s_rxdesc[i][3] =
+ (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ // MG_DEBUG(("%d %p", i, s_rxdesc[i]));
+ }
-static void settmout(struct mg_connection *c, uint8_t type) {
- struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
- 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));
-}
+ // Init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
+ s_txdesc[i][3] =
+ (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
+ }
-static struct mg_connection *accept_conn(struct mg_connection *lsn,
- struct pkt *pkt) {
- struct mg_connection *c = mg_alloc_conn(lsn->mgr);
- struct connstate *s = (struct connstate *) (c + 1);
- s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
- settmout(c, MIP_TTYPE_KEEPALIVE);
- c->rem.ip = pkt->ip->src;
- c->rem.port = pkt->tcp->sport;
- MG_DEBUG(
- ("%lu accepted %I:%hu", c->id, 4, &c->rem.ip, mg_ntohs(c->rem.port)));
- LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
- c->is_accepted = 1;
- c->is_hexdumping = lsn->is_hexdumping;
- c->pfn = lsn->pfn;
- c->loc = lsn->loc;
- c->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- mg_call(c, MG_EV_OPEN, NULL);
- mg_call(c, MG_EV_ACCEPT, NULL);
- return c;
+ EMAC->EMACDMABUSMOD |= MG_BIT(0); // Software reset
+ while ((EMAC->EMACDMABUSMOD & MG_BIT(0)) != 0) tm4cspin(1); // Wait until done
+
+ // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
+ int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
+ EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
+
+ // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
+ // hardware checksum. Therefore, descriptor size is 4, not 8
+ // EMAC->EMACDMABUSMOD = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) | MG_BIT(25);
+ EMAC->EMACIM = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT
+ EMAC->EMACFLOWCTL = MG_BIT(7); // Disable zero-quanta pause
+ // EMAC->EMACFRAMEFLTR = MG_BIT(31); // Receive all
+ // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
+ emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(15)); // Reset internal PHY (EPHY)
+ emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(12)); // Set autonegotiation
+ EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
+ EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
+ EMAC->EMACDMAIM = MG_BIT(6) | MG_BIT(16); // RIE, NIE
+ EMAC->EMACCFG = MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast
+ EMAC->EMACDMAOPMODE =
+ MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF
+ EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
+ EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
+ ((uint32_t) ifp->mac[2] << 16) |
+ ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
+ // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
+ // default. This also applies to the STM32 driver (at least for F7)
+ return true;
}
-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_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
+static uint32_t s_txno;
+static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // fail
+ } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
+ ifp->nerr++;
+ MG_ERROR(("No descriptors available"));
+ // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
+ // EMAC->EMACDMARIS);
+ len = 0; // fail
} else {
- return MG_IO_ERR;
+ memcpy(s_txbuf[s_txno], buf, len); // Copy data
+ s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
+ s_txdesc[s_txno][0] =
+ MG_BIT(20) | MG_BIT(28) | MG_BIT(29) | MG_BIT(30); // Chain,FS,LS,IC
+ s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
- return (long) len;
+ EMAC->EMACDMARIS = MG_BIT(2) | MG_BIT(5); // Clear any prior TU/UNF
+ EMAC->EMACTXPOLLD = 0; // and resume
+ return len;
+ (void) ifp;
}
-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 bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
+ uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
+ bool up = (bmsr & MG_BIT(2)) ? 1 : 0;
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
+ // tmp = reg with flags set to the most likely situation: 100M full-duplex
+ // if(link is slow or half) set flags otherwise
+ // reg = tmp
+ uint32_t emaccfg = EMAC->EMACCFG | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex
+ if (sts & MG_BIT(1)) emaccfg &= ~MG_BIT(14); // 10M
+ if ((sts & MG_BIT(2)) == 0) emaccfg &= ~MG_BIT(11); // Half-duplex
+ EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
+ MG_DEBUG(("Link is %uM %s-duplex", emaccfg & MG_BIT(14) ? 100 : 10,
+ emaccfg & MG_BIT(11) ? "full" : "half"));
+ }
+ return up;
}
-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;
- uint32_t seq = mg_ntohl(pkt->tcp->seq);
- s->raw.align = c->recv.align;
- if (pkt->tcp->flags & TH_FIN) {
- s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
- c->is_closing = 1;
- } else if (pkt->pay.len == 0) {
- // TODO(cpq): handle this peer's ACK
- } else if (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", 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 {
- // 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.ptr, pkt->pay.len);
- io->len += pkt->pay.len;
-
- MG_DEBUG(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
- // Advance ACK counter
- s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
-#if 0
- // Send ACK immediately
- MG_DEBUG((" imm ACK", c->id, mg_htonl(pkt->tcp->seq), s->ack));
- tx_tcp((struct mip_if *) c->mgr->priv, c->rem.ip, TH_ACK, c->loc.port,
- c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
-#else
- // if not already running, setup a timer to send an ACK later
- if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
-#endif
-
- if (c->is_tls) {
- // TLS connection. Make room for decrypted data in c->recv
- io = &c->recv;
- 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;
- mg_call(c, MG_EV_READ, &n);
- }
+void EMAC0_IRQHandler(void);
+static uint32_t s_rxno;
+void EMAC0_IRQHandler(void) {
+ if (EMAC->EMACDMARIS & MG_BIT(6)) { // Frame received, loop
+ EMAC->EMACDMARIS = MG_BIT(16) | MG_BIT(6); // Clear flag
+ for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
+ if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done
+ if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) == (MG_BIT(8) | MG_BIT(9))) &&
+ !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames
+ uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1));
+ // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
+ // EMAC->EMACDMARIS);
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
- } else {
- // Plain text connection, data is already in c->recv, trigger MG_EV_READ
- mg_call(c, MG_EV_READ, &pkt->pay.len);
+ s_rxdesc[s_rxno][0] = MG_BIT(31);
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
+ EMAC->EMACDMARIS = MG_BIT(7); // Clear possible RU while processing
+ EMAC->EMACRXPOLLD = 0; // and resume RX
}
-static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
- struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
-#if 0
- MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
+struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init,
+ mg_tcpip_driver_tm4c_tx, NULL,
+ mg_tcpip_driver_tm4c_up};
#endif
- 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;
- tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
- c->is_connecting = 0; // Client connected
- settmout(c, MIP_TTYPE_KEEPALIVE);
- mg_call(c, MG_EV_CONNECT, NULL); // Let user know
- } else if (c != NULL && c->is_connecting) {
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (c != NULL && pkt->tcp->flags & TH_RST) {
- mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- } else if (c != NULL) {
-#if 0
- MG_DEBUG(("%lu %d %I:%hu -> %I:%hu", c->id, (int) pkt->raw.len,
- 4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
- 4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
- mg_hexdump(pkt->pay.buf, pkt->pay.len);
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/w5500.c"
#endif
- s->tmiss = 0; // Reset missed keep-alive counter
- if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
- settmout(c, MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
- read_conn(c, pkt); // Override timer with ACK timeout if needed
- } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (pkt->tcp->flags & TH_RST) {
- if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- // ignore RST if not connected
- } else if (pkt->tcp->flags & TH_SYN) {
- // Use peer's source port as ISN, in order to recognise the handshake
- uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
- tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
- } else if (pkt->tcp->flags & TH_FIN) {
- tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
- accept_conn(c, pkt);
- } else if (!c->is_accepted ) { // no peer
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else {
- // MG_DEBUG(("dropped silently.."));
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500
+
+enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
+
+static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr,
+ bool wr, void *buf, size_t len) {
+ size_t i;
+ uint8_t *p = (uint8_t *) buf;
+ uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
+ (uint8_t) ((block << 3) | (wr ? 4 : 0))};
+ s->begin(s->spi);
+ for (i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
+ for (i = 0; i < len; i++) {
+ uint8_t r = s->txn(s->spi, p[i]);
+ if (!wr) p[i] = r;
}
+ s->end(s->spi);
}
-static void rx_ip(struct mip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("IP %d", (int) pkt->pay.len));
- if (pkt->ip->proto == 1) {
- pkt->icmp = (struct icmp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->icmp)) return;
- mkpay(pkt, pkt->icmp + 1);
- rx_icmp(ifp, pkt);
- } else if (pkt->ip->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
- mkpay(pkt, pkt->udp + 1);
- if (pkt->udp->dport == mg_htons(68)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp + 1);
- rx_dhcp_client(ifp, pkt);
- } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp + 1);
- rx_dhcp_server(ifp, pkt);
- } else {
- rx_udp(ifp, pkt);
+// clang-format off
+static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
+static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
+static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
+static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
+static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
+static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
+// clang-format on
+
+static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
+ while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
+ // printf("RSR: %d\n", (int) n);
+ if (n > 0) {
+ uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
+ n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
+ if (n <= len + 2 && n > 1) {
+ r = (uint16_t) (n - 2);
+ w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
}
- } else if (pkt->ip->proto == 6) {
- pkt->tcp = (struct tcp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->tcp)) return;
- mkpay(pkt, pkt->tcp + 1);
- uint16_t iplen = mg_ntohs(pkt->ip->len);
- uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
- if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
- rx_tcp(ifp, pkt);
+ 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);
}
+ return r;
}
-static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("IP %d", (int) len));
- if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
- pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
- if (pkt->pay.len < sizeof(*pkt->icmp)) return;
- mkpay(pkt, pkt->icmp + 1);
- rx_icmp(ifp, pkt);
- } else if (pkt->ip6->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip6 + 1);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
- // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
- // mg_htons(udp->dport)));
- mkpay(pkt, pkt->udp + 1);
+static size_t w5500_tx(const void *buf, size_t buflen,
+ struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint16_t i, ptr, n = 0, len = (uint16_t) buflen;
+ while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
+ ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
+ w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
+ w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
+ w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
+ for (i = 0; i < 40; i++) {
+ uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
+ if (ir == 0) continue;
+ // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
+ w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
+ if (ir & 8) len = 0; // Timeout. Report error
+ if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
}
+ return len;
}
-static void mip_rx(struct mip_if *ifp, void *buf, size_t len) {
- const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
- struct pkt pkt;
- memset(&pkt, 0, sizeof(pkt));
- pkt.raw.ptr = (char *) buf;
- pkt.raw.len = len;
- pkt.eth = (struct eth *) buf;
- if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
- if (memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
- memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) {
- // Not for us. Drop silently
- } else if (pkt.eth->type == mg_htons(0x806)) {
- pkt.arp = (struct arp *) (pkt.eth + 1);
- if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
- rx_arp(ifp, &pkt);
- } else if (pkt.eth->type == mg_htons(0x86dd)) {
- pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
- if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
- mkpay(&pkt, pkt.ip6 + 1);
- rx_ip6(ifp, &pkt);
- } else if (pkt.eth->type == mg_htons(0x800)) {
- pkt.ip = (struct ip *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- // Truncate frame to what IP header tells us
- if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
- pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
- }
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- if ((pkt.ip->ver >> 4) != 4) return; // Not IP
- mkpay(&pkt, pkt.ip + 1);
- rx_ip(ifp, &pkt);
- } else {
- MG_DEBUG((" Unknown eth type %x", mg_htons(pkt.eth->type)));
+static bool w5500_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
+ s->end(s->spi);
+ w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
+ w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
+ w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
+ // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
+ w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
+ w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
+ w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
+ w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
+ return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
+}
+
+static bool w5500_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
+ uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
+ return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
+}
+
+struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx,
+ w5500_up};
+#endif
+
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/xmc.c"
+#endif
+
+
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
+
+struct ETH_GLOBAL_TypeDef {
+ volatile uint32_t MAC_CONFIGURATION, MAC_FRAME_FILTER, HASH_TABLE_HIGH,
+ HASH_TABLE_LOW, GMII_ADDRESS, GMII_DATA, FLOW_CONTROL, VLAN_TAG, VERSION,
+ DEBUG, REMOTE_WAKE_UP_FRAME_FILTER, PMT_CONTROL_STATUS, RESERVED[2],
+ INTERRUPT_STATUS, INTERRUPT_MASK, MAC_ADDRESS0_HIGH, MAC_ADDRESS0_LOW,
+ MAC_ADDRESS1_HIGH, MAC_ADDRESS1_LOW, MAC_ADDRESS2_HIGH, MAC_ADDRESS2_LOW,
+ MAC_ADDRESS3_HIGH, MAC_ADDRESS3_LOW, RESERVED1[40], MMC_CONTROL,
+ MMC_RECEIVE_INTERRUPT, MMC_TRANSMIT_INTERRUPT, MMC_RECEIVE_INTERRUPT_MASK,
+ MMC_TRANSMIT_INTERRUPT_MASK, TX_STATISTICS[26], RESERVED2,
+ RX_STATISTICS_1[26], RESERVED3[6], MMC_IPC_RECEIVE_INTERRUPT_MASK,
+ RESERVED4, MMC_IPC_RECEIVE_INTERRUPT, RESERVED5, RX_STATISTICS_2[30],
+ RESERVED7[286], TIMESTAMP_CONTROL, SUB_SECOND_INCREMENT,
+ SYSTEM_TIME_SECONDS, SYSTEM_TIME_NANOSECONDS,
+ SYSTEM_TIME_SECONDS_UPDATE, SYSTEM_TIME_NANOSECONDS_UPDATE,
+ TIMESTAMP_ADDEND, TARGET_TIME_SECONDS, TARGET_TIME_NANOSECONDS,
+ SYSTEM_TIME_HIGHER_WORD_SECONDS, TIMESTAMP_STATUS,
+ PPS_CONTROL, RESERVED8[564], BUS_MODE, TRANSMIT_POLL_DEMAND,
+ RECEIVE_POLL_DEMAND, RECEIVE_DESCRIPTOR_LIST_ADDRESS,
+ TRANSMIT_DESCRIPTOR_LIST_ADDRESS, STATUS, OPERATION_MODE,
+ INTERRUPT_ENABLE, MISSED_FRAME_AND_BUFFER_OVERFLOW_COUNTER,
+ RECEIVE_INTERRUPT_WATCHDOG_TIMER, RESERVED9, AHB_STATUS,
+ RESERVED10[6], CURRENT_HOST_TRANSMIT_DESCRIPTOR,
+ CURRENT_HOST_RECEIVE_DESCRIPTOR, CURRENT_HOST_TRANSMIT_BUFFER_ADDRESS,
+ CURRENT_HOST_RECEIVE_BUFFER_ADDRESS, HW_FEATURE;
+};
+
+#undef ETH0
+#define ETH0 ((struct ETH_GLOBAL_TypeDef*) 0x5000C000UL)
+
+#define ETH_PKT_SIZE 1536 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 4 // Descriptor size (words)
+
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor
+
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
+
+static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) |
+ ((uint32_t)addr << 11) |
+ ((uint32_t)reg << 6) | 1;
+ while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0;
+ return (uint16_t)(ETH0->GMII_DATA & 0xffff);
+}
+
+static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ ETH0->GMII_DATA = val;
+ ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) |
+ ((uint32_t)addr << 11) |
+ ((uint32_t)reg << 6) | 3;
+ while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0;
+}
+
+static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc_data *d) {
+ if (d->mdc_cr == -1) {
+ // assume ETH clock is 60MHz by default
+ // then according to 13.2.8.1, we need to set value 3
+ return 3;
}
+
+ return d->mdc_cr;
}
-static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
- if (ifp == NULL || ifp->driver == NULL) return;
- bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms);
- ifp->now = uptime_ms;
+static bool mg_tcpip_driver_xmc_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_xmc_data *d =
+ (struct mg_tcpip_driver_xmc_data *) ifp->driver_data;
+ s_ifp = ifp;
- // Handle physical interface up/down status
- if (expired_1000ms && ifp->driver->up) {
- bool up = ifp->driver->up(ifp);
- bool current = ifp->state != MIP_STATE_DOWN;
- if (up != current) {
- ifp->state = up == false ? MIP_STATE_DOWN
- : ifp->enable_dhcp_client ? MIP_STATE_UP
- : MIP_STATE_READY;
- if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
- onstatechange(ifp);
+ // reset MAC
+ ETH0->BUS_MODE |= 1;
+ while (ETH0->BUS_MODE & 1) (void) 0;
+
+ // set clock rate
+ ETH0->GMII_ADDRESS = get_clock_rate(d) << 2;
+
+ // init phy
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
+
+ // configure MAC: DO, DM, FES, TC
+ ETH0->MAC_CONFIGURATION = MG_BIT(13) | MG_BIT(11) | MG_BIT(14) | MG_BIT(24);
+
+ // set the MAC address
+ ETH0->MAC_ADDRESS0_HIGH = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]);
+ ETH0->MAC_ADDRESS0_LOW =
+ MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]);
+
+ // Configure the receive filter
+ ETH0->MAC_FRAME_FILTER = MG_BIT(10) | MG_BIT(2); // HFP, HMC
+ // Disable flow control
+ ETH0->FLOW_CONTROL = 0;
+ // Enable store and forward mode
+ ETH0->OPERATION_MODE = MG_BIT(25) | MG_BIT(21); // RSF, TSF
+
+ // Configure DMA bus mode (AAL, USP, RPBL, PBL)
+ ETH0->BUS_MODE = MG_BIT(25) | MG_BIT(23) | (32 << 17) | (32 << 8);
+
+ // init RX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = MG_BIT(31); // OWN descriptor
+ s_rxdesc[i][1] = MG_BIT(14) | ETH_PKT_SIZE;
+ s_rxdesc[i][2] = (uint32_t) s_rxbuf[i];
+ if (i == ETH_DESC_CNT - 1) {
+ s_rxdesc[i][3] = (uint32_t) &s_rxdesc[0][0];
+ } else {
+ s_rxdesc[i][3] = (uint32_t) &s_rxdesc[i + 1][0];
}
}
- if (ifp->state == MIP_STATE_DOWN) return;
- // if (expired_1000ms) arp_cache_dump(ifp->arp_cache);
+ ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_rxdesc[0][0];
- if (ifp->ip == 0 && expired_1000ms) {
- tx_dhcp_discover(ifp); // If IP not configured, send DHCP
- } else if (ifp->enable_dhcp_client == false && expired_1000ms && ifp->gw &&
- arp_cache_find(ifp, ifp->gw) == NULL) {
- arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache
+ // init TX descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_txdesc[i][0] = MG_BIT(30) | MG_BIT(20);
+ s_txdesc[i][2] = (uint32_t) s_txbuf[i];
+ if (i == ETH_DESC_CNT - 1) {
+ s_txdesc[i][3] = (uint32_t) &s_txdesc[0][0];
+ } else {
+ s_txdesc[i][3] = (uint32_t) &s_txdesc[i + 1][0];
+ }
}
+ ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_txdesc[0][0];
- // Read data from the network
- size_t len = ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, ifp);
- mip_rx(ifp, (void *) ifp->rx.ptr, len);
- qp_mark(QP_FRAMEDONE, (int) q_space(&ifp->queue));
+ // Clear interrupts
+ ETH0->STATUS = 0xFFFFFFFF;
- // Process timeouts
- for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
- if (c->is_udp || c->is_listening) continue;
- if (c->is_connecting || c->is_resolving) continue;
- struct connstate *s = (struct connstate *) (c + 1);
- if (uptime_ms > s->timer) {
- if (s->ttype == MIP_TTYPE_ACK) {
- MG_DEBUG(("%lu ack %x %x", c->id, s->seq, s->ack));
- tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- } else {
- if (s->tmiss++ > 2) {
- mg_error(c, "keepalive");
- } else {
- MG_DEBUG(("%lu keepalive", c->id));
- tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
- }
- }
- settmout(c, MIP_TTYPE_KEEPALIVE);
- }
- }
-#ifdef MIP_QPROFILE
- qp_log();
-#endif
+ // Disable MAC interrupts
+ ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF;
+ ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+ ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+ ETH0->INTERRUPT_MASK = MG_BIT(9) | MG_BIT(3); // TSIM, PMTIM
+
+ //Enable interrupts (NIE, RIE, TIE)
+ ETH0->INTERRUPT_ENABLE = MG_BIT(16) | MG_BIT(6) | MG_BIT(0);
+
+ // Enable MAC transmission and reception (TE, RE)
+ ETH0->MAC_CONFIGURATION |= MG_BIT(3) | MG_BIT(2);
+ // Enable DMA transmission and reception (ST, SR)
+ ETH0->OPERATION_MODE |= MG_BIT(13) | MG_BIT(1);
+ return true;
}
-// This function executes in interrupt context, thus it should copy data
-// somewhere fast. Note that newlib's malloc is not thread safe, thus use
-// our lock-free queue with preallocated buffer to copy data and return asap
-void mip_qwrite(void *buf, size_t len, struct mip_if *ifp) {
- if (q_write(&ifp->queue, buf, len)) {
- qp_mark(QP_FRAMEPUSHED, (int) q_space(&ifp->queue));
+static size_t mg_tcpip_driver_xmc_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors"));
+ len = 0; // All descriptors are busy, fail
} else {
- ifp->dropped++;
- qp_mark(QP_FRAMEDROPPED, ifp->dropped);
- MG_ERROR(("dropped %d", (int) len));
+ memcpy(s_txbuf[s_txno], buf, len);
+ s_txdesc[s_txno][1] = len;
+ // Table 13-19 Transmit Descriptor Word 0 (IC, LS, FS, TCH)
+ s_txdesc[s_txno][0] = MG_BIT(30) | MG_BIT(29) | MG_BIT(28) | MG_BIT(20);
+ s_txdesc[s_txno][0] |= MG_BIT(31); // OWN bit: handle control to DMA
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
-}
-size_t mip_qread(void *buf, struct mip_if *ifp) {
- size_t len = q_read(&ifp->queue, buf);
- qp_mark(QP_FRAMEPOPPED, (int) q_space(&ifp->queue));
+ // Resume processing
+ ETH0->STATUS = MG_BIT(2); // clear Transmit unavailable
+ ETH0->TRANSMIT_POLL_DEMAND = 0;
return len;
}
-size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp) {
- return mip_qread((void *) ifp->rx.ptr, ifp);
- (void) len, (void) buf;
-}
+static bool mg_tcpip_driver_xmc_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_xmc_data *d =
+ (struct mg_tcpip_driver_xmc_data *) ifp->driver_data;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100,
+ full_duplex ? "full" : "half"));
+ }
+ return up;
+}
+
+void ETH0_IRQHandler(void);
+void ETH0_IRQHandler(void) {
+ uint32_t irq_status = ETH0->STATUS;
+
+ // check if a frame was received
+ if (irq_status & MG_BIT(6)) {
+ for (uint8_t i = 0; i < ETH_DESC_CNT; i++) {
+ if ((s_rxdesc[s_rxno][0] & MG_BIT(31)) == 0) {
+ size_t len = (s_rxdesc[s_rxno][0] & 0x3fff0000) >> 16;
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
+ s_rxdesc[s_rxno][0] = MG_BIT(31); // OWN bit: handle control to DMA
+ // Resume processing
+ ETH0->STATUS = MG_BIT(7) | MG_BIT(6); // clear RU and RI
+ ETH0->RECEIVE_POLL_DEMAND = 0;
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
+ ETH0->STATUS = MG_BIT(6);
+ }
-void mip_init(struct mg_mgr *mgr, struct mip_if *ifp) {
- if (ifp->driver->init && !ifp->driver->init(ifp)) {
- MG_ERROR(("driver init failed"));
- } else {
- size_t maxpktsize = 1540;
- ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize;
- ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize;
- if (ifp->queue.len) ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
- ifp->timer_1000ms = mg_millis();
- arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12);
- mgr->priv = ifp;
- ifp->mgr = mgr;
- mgr->extraconnsize = sizeof(struct connstate);
- if (ifp->ip == 0) ifp->enable_dhcp_client = true;
-#ifdef MIP_QPROFILE
- qp_init();
-#endif
+ // clear Successful transmission interrupt
+ if (irq_status & 1) {
+ ETH0->STATUS = 1;
}
-}
-void mip_free(struct mip_if *ifp) {
- free((char *) ifp->rx.ptr);
- free((char *) ifp->tx.ptr);
+ // clear normal interrupt
+ if (irq_status & MG_BIT(16)) {
+ ETH0->STATUS = MG_BIT(16);
+ }
}
-int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) {
- (void) m, (void) fn, (void) d, (void) udp;
- MG_ERROR(("Not implemented"));
- return -1;
-}
+struct mg_tcpip_driver mg_tcpip_driver_xmc = {
+ mg_tcpip_driver_xmc_init, mg_tcpip_driver_xmc_tx, NULL,
+ mg_tcpip_driver_xmc_up};
+#endif
-#if 0
-static uint16_t mkeport(void) {
- uint16_t a = 0, b = mg_millis() & 0xffffU, c = MIP_ETHEMERAL_PORT;
- mg_random(&a, sizeof(a));
- c += (a ^ b) % (0xffffU - MIP_ETHEMERAL_PORT);
- return c;
-}
+#ifdef MG_ENABLE_LINES
+#line 1 "src/drivers/xmc7.c"
#endif
-void mg_connect_resolved(struct mg_connection *c) {
- struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
- c->is_resolving = 0;
- if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT;
- c->loc.ip = ifp->ip;
- c->loc.port = mg_htons(ifp->eport++);
- MG_DEBUG(("%lu %I:%hu->%I:%hu", c->id, 4, &c->loc.ip, mg_ntohs(c->loc.port),
- 4, &c->rem.ip, mg_ntohs(c->rem.port)));
- mg_call(c, MG_EV_RESOLVE, NULL);
- if (c->is_udp) {
- mg_call(c, MG_EV_CONNECT, NULL);
- } else {
- uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
- tx_tcp(ifp, c->rem.ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL, 0);
- c->is_connecting = 1;
- }
-}
-bool mg_open_listener(struct mg_connection *c, const char *url) {
- c->loc.port = mg_htons(mg_url_port(url));
- return true;
-}
+#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
+
+struct ETH_Type {
+ volatile uint32_t CTL, STATUS, RESERVED[1022], NETWORK_CONTROL,
+ NETWORK_CONFIG, NETWORK_STATUS, USER_IO_REGISTER, DMA_CONFIG,
+ TRANSMIT_STATUS, RECEIVE_Q_PTR, TRANSMIT_Q_PTR, RECEIVE_STATUS,
+ INT_STATUS, INT_ENABLE, INT_DISABLE, INT_MASK, PHY_MANAGEMENT, PAUSE_TIME,
+ TX_PAUSE_QUANTUM, PBUF_TXCUTTHRU, PBUF_RXCUTTHRU, JUMBO_MAX_LENGTH,
+ EXTERNAL_FIFO_INTERFACE, RESERVED1, AXI_MAX_PIPELINE, RSC_CONTROL,
+ INT_MODERATION, SYS_WAKE_TIME, RESERVED2[7], HASH_BOTTOM, HASH_TOP,
+ SPEC_ADD1_BOTTOM, SPEC_ADD1_TOP, SPEC_ADD2_BOTTOM, SPEC_ADD2_TOP,
+ SPEC_ADD3_BOTTOM, SPEC_ADD3_TOP, SPEC_ADD4_BOTTOM, SPEC_ADD4_TOP,
+ SPEC_TYPE1, SPEC_TYPE2, SPEC_TYPE3, SPEC_TYPE4, WOL_REGISTER,
+ STRETCH_RATIO, STACKED_VLAN, TX_PFC_PAUSE, MASK_ADD1_BOTTOM,
+ MASK_ADD1_TOP, DMA_ADDR_OR_MASK, RX_PTP_UNICAST, TX_PTP_UNICAST,
+ TSU_NSEC_CMP, TSU_SEC_CMP, TSU_MSB_SEC_CMP, TSU_PTP_TX_MSB_SEC,
+ TSU_PTP_RX_MSB_SEC, TSU_PEER_TX_MSB_SEC, TSU_PEER_RX_MSB_SEC,
+ DPRAM_FILL_DBG, REVISION_REG, OCTETS_TXED_BOTTOM, OCTETS_TXED_TOP,
+ FRAMES_TXED_OK, BROADCAST_TXED, MULTICAST_TXED, PAUSE_FRAMES_TXED,
+ FRAMES_TXED_64, FRAMES_TXED_65, FRAMES_TXED_128, FRAMES_TXED_256,
+ FRAMES_TXED_512, FRAMES_TXED_1024, FRAMES_TXED_1519, TX_UNDERRUNS,
+ SINGLE_COLLISIONS, MULTIPLE_COLLISIONS, EXCESSIVE_COLLISIONS,
+ LATE_COLLISIONS, DEFERRED_FRAMES, CRS_ERRORS, OCTETS_RXED_BOTTOM,
+ OCTETS_RXED_TOP, FRAMES_RXED_OK, BROADCAST_RXED, MULTICAST_RXED,
+ PAUSE_FRAMES_RXED, FRAMES_RXED_64, FRAMES_RXED_65, FRAMES_RXED_128,
+ FRAMES_RXED_256, FRAMES_RXED_512, FRAMES_RXED_1024, FRAMES_RXED_1519,
+ UNDERSIZE_FRAMES, EXCESSIVE_RX_LENGTH, RX_JABBERS, FCS_ERRORS,
+ RX_LENGTH_ERRORS, RX_SYMBOL_ERRORS, ALIGNMENT_ERRORS, RX_RESOURCE_ERRORS,
+ RX_OVERRUNS, RX_IP_CK_ERRORS, RX_TCP_CK_ERRORS, RX_UDP_CK_ERRORS,
+ AUTO_FLUSHED_PKTS, RESERVED3, TSU_TIMER_INCR_SUB_NSEC, TSU_TIMER_MSB_SEC,
+ TSU_STROBE_MSB_SEC, TSU_STROBE_SEC, TSU_STROBE_NSEC, TSU_TIMER_SEC,
+ TSU_TIMER_NSEC, TSU_TIMER_ADJUST, TSU_TIMER_INCR, TSU_PTP_TX_SEC,
+ TSU_PTP_TX_NSEC, TSU_PTP_RX_SEC, TSU_PTP_RX_NSEC, TSU_PEER_TX_SEC,
+ TSU_PEER_TX_NSEC, TSU_PEER_RX_SEC, TSU_PEER_RX_NSEC, PCS_CONTROL,
+ PCS_STATUS, RESERVED4[2], PCS_AN_ADV, PCS_AN_LP_BASE, PCS_AN_EXP,
+ PCS_AN_NP_TX, PCS_AN_LP_NP, RESERVED5[6], PCS_AN_EXT_STATUS, RESERVED6[8],
+ TX_PAUSE_QUANTUM1, TX_PAUSE_QUANTUM2, TX_PAUSE_QUANTUM3, RESERVED7,
+ RX_LPI, RX_LPI_TIME, TX_LPI, TX_LPI_TIME, DESIGNCFG_DEBUG1,
+ DESIGNCFG_DEBUG2, DESIGNCFG_DEBUG3, DESIGNCFG_DEBUG4, DESIGNCFG_DEBUG5,
+ DESIGNCFG_DEBUG6, DESIGNCFG_DEBUG7, DESIGNCFG_DEBUG8, DESIGNCFG_DEBUG9,
+ DESIGNCFG_DEBUG10, RESERVED8[22], SPEC_ADD5_BOTTOM, SPEC_ADD5_TOP,
+ RESERVED9[60], SPEC_ADD36_BOTTOM, SPEC_ADD36_TOP, INT_Q1_STATUS,
+ INT_Q2_STATUS, INT_Q3_STATUS, RESERVED10[11], INT_Q15_STATUS, RESERVED11,
+ TRANSMIT_Q1_PTR, TRANSMIT_Q2_PTR, TRANSMIT_Q3_PTR, RESERVED12[11],
+ TRANSMIT_Q15_PTR, RESERVED13, RECEIVE_Q1_PTR, RECEIVE_Q2_PTR,
+ RECEIVE_Q3_PTR, RESERVED14[3], RECEIVE_Q7_PTR, RESERVED15,
+ DMA_RXBUF_SIZE_Q1, DMA_RXBUF_SIZE_Q2, DMA_RXBUF_SIZE_Q3, RESERVED16[3],
+ DMA_RXBUF_SIZE_Q7, CBS_CONTROL, CBS_IDLESLOPE_Q_A, CBS_IDLESLOPE_Q_B,
+ UPPER_TX_Q_BASE_ADDR, TX_BD_CONTROL, RX_BD_CONTROL, UPPER_RX_Q_BASE_ADDR,
+ RESERVED17[2], HIDDEN_REG0, HIDDEN_REG1, HIDDEN_REG2, HIDDEN_REG3,
+ RESERVED18[2], HIDDEN_REG4, HIDDEN_REG5;
+};
-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);
- if (len > 0) {
- mg_iobuf_del(&c->send, 0, (size_t) len);
- mg_call(c, MG_EV_WRITE, &len);
- }
+#define ETH0 ((struct ETH_Type *) 0x40490000)
+
+#define ETH_PKT_SIZE 1536 // Max frame size
+#define ETH_DESC_CNT 4 // Descriptors count
+#define ETH_DS 2 // Descriptor size (words)
+
+static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
+static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
+static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
+static uint8_t s_txno; // Current TX descriptor
+static uint8_t s_rxno; // Current RX descriptor
+
+static struct mg_tcpip_if *s_ifp; // MIP interface
+enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
+
+static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
+ // WRITE1, READ OPERATION, PHY, REG, WRITE10
+ ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(29) | ((addr & 0xf) << 24) |
+ ((reg & 0x1f) << 18) | MG_BIT(17);
+ while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0;
+ return ETH0->PHY_MANAGEMENT & 0xffff;
}
-static void close_conn(struct mg_connection *c) {
- struct connstate *s = (struct connstate *) (c + 1);
- mg_iobuf_free(&s->raw); // For TLS connections, release raw data
- 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 void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
+ ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(28) | ((addr & 0xf) << 24) |
+ ((reg & 0x1f) << 18) | MG_BIT(17) | val;
+ while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0;
}
-static bool can_write(struct mg_connection *c) {
- return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
- c->is_tls_hs == 0;
+static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc7_data *d) {
+ // see ETH0 -> NETWORK_CONFIG register
+ (void) d;
+ return 3;
}
-void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
- struct mg_connection *c, *tmp;
- uint64_t now = mg_millis();
- mip_poll((struct mip_if *) mgr->priv, now);
- mg_timer_poll(&mgr->timers, now);
- for (c = mgr->conns; c != NULL; c = tmp) {
- tmp = c->next;
- mg_call(c, MG_EV_POLL, &now);
- MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't',
- c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
- c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
- if (c->is_tls_hs) mg_tls_handshake(c);
- if (can_write(c)) write_conn(c);
- if (c->is_draining && c->send.len == 0) c->is_closing = 1;
- if (c->is_closing) close_conn(c);
+static bool mg_tcpip_driver_xmc7_init(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_xmc7_data *d =
+ (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data;
+ s_ifp = ifp;
+
+ // enable controller, set RGMII mode
+ ETH0->CTL = MG_BIT(31) | 2;
+
+ uint32_t cr = get_clock_rate(d);
+ // set NSP change, ignore RX FCS, data bus width, clock rate
+ // frame length 1536, full duplex, speed
+ ETH0->NETWORK_CONFIG = MG_BIT(29) | MG_BIT(26) | MG_BIT(21) |
+ ((cr & 7) << 18) | MG_BIT(8) | MG_BIT(4) |
+ MG_BIT(1) | MG_BIT(0);
+
+ // config DMA settings: Force TX burst, Discard on Error, set RX buffer size
+ // to 1536, TX_PBUF_SIZE, RX_PBUF_SIZE, AMBA_BURST_LENGTH
+ ETH0->DMA_CONFIG =
+ MG_BIT(26) | MG_BIT(24) | (0x18 << 16) | MG_BIT(10) | (3 << 8) | 4;
+
+ // initialize descriptors
+ for (int i = 0; i < ETH_DESC_CNT; i++) {
+ s_rxdesc[i][0] = (uint32_t) s_rxbuf[i];
+ if (i == ETH_DESC_CNT - 1) {
+ s_rxdesc[i][0] |= MG_BIT(1); // mark last descriptor
+ }
+
+ s_txdesc[i][0] = (uint32_t) s_txbuf[i];
+ s_txdesc[i][1] = MG_BIT(31); // OWN descriptor
+ if (i == ETH_DESC_CNT - 1) {
+ s_txdesc[i][1] |= MG_BIT(30); // mark last descriptor
+ }
}
- (void) ms;
+ ETH0->RECEIVE_Q_PTR = (uint32_t) s_rxdesc;
+ ETH0->TRANSMIT_Q_PTR = (uint32_t) s_txdesc;
+
+ // disable other queues
+ ETH0->TRANSMIT_Q2_PTR = 1;
+ ETH0->TRANSMIT_Q1_PTR = 1;
+ ETH0->RECEIVE_Q2_PTR = 1;
+ ETH0->RECEIVE_Q1_PTR = 1;
+
+ // enable interrupts (TX and RX complete)
+ ETH0->INT_ENABLE = MG_BIT(7) | MG_BIT(1);
+
+ // set MAC address
+ ETH0->SPEC_ADD1_BOTTOM =
+ ifp->mac[3] << 24 | ifp->mac[2] << 16 | ifp->mac[1] << 8 | ifp->mac[0];
+ ETH0->SPEC_ADD1_TOP = ifp->mac[5] << 8 | ifp->mac[4];
+
+ // enable MDIO, TX, RX
+ ETH0->NETWORK_CONTROL = MG_BIT(4) | MG_BIT(3) | MG_BIT(2);
+
+ // start transmission
+ ETH0->NETWORK_CONTROL |= MG_BIT(9);
+
+ // init phy
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
+
+ (void) d;
+ return true;
}
-bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mip_if *ifp = (struct mip_if *) c->mgr->priv;
- bool res = false;
- if (ifp->ip == 0 || ifp->state != MIP_STATE_READY) {
- mg_error(c, "net down");
- } else if (c->is_udp) {
- tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
- res = true;
+static size_t mg_tcpip_driver_xmc7_tx(const void *buf, size_t len,
+ struct mg_tcpip_if *ifp) {
+ if (len > sizeof(s_txbuf[s_txno])) {
+ MG_ERROR(("Frame too big, %ld", (long) len));
+ len = 0; // Frame is too big
+ } else if (((s_txdesc[s_txno][1] & MG_BIT(31)) == 0)) {
+ ifp->nerr++;
+ MG_ERROR(("No free descriptors"));
+ len = 0; // All descriptors are busy, fail
} else {
- res = mg_iobuf_add(&c->send, c->send.len, buf, len);
- }
- return res;
-}
+ memcpy(s_txbuf[s_txno], buf, len);
+ s_txdesc[s_txno][1] = (s_txno == ETH_DESC_CNT - 1 ? MG_BIT(30) : 0) |
+ MG_BIT(15) | len; // Last buffer and length
-#ifdef MIP_QPROFILE
+ ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission
+ if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
+ }
-#pragma pack(push, 1)
-struct qpentry {
- uint32_t timestamp;
- uint16_t type;
- uint16_t len;
-};
-#pragma pack(pop)
+ MG_DSB();
+ ETH0->TRANSMIT_STATUS = ETH0->TRANSMIT_STATUS;
+ ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission
-static struct queue qp;
+ return len;
+}
-// This is called from IRQ and main contexts; two producers, single consumer
-// TODO(scaprile): avoid concurrency issues (2 queues ?)
-void qp_mark(unsigned int type, int len) {
- static bool ovf = false;
- static uint16_t irq_ctr = 0, drop_ctr = 0;
- struct qpentry e = {.timestamp = (uint32_t) mg_millis(),
- .type = (uint16_t) type,
- .len = (uint16_t) len};
- if (type == QP_IRQTRIGGERED) e.len = ++irq_ctr;
- if (ovf) {
- e.type = (uint16_t) QP_QUEUEOVF;
- e.len = drop_ctr;
+static bool mg_tcpip_driver_xmc7_up(struct mg_tcpip_if *ifp) {
+ struct mg_tcpip_driver_xmc7_data *d =
+ (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data;
+ uint8_t speed = MG_PHY_SPEED_10M;
+ bool up = false, full_duplex = false;
+ struct mg_phy phy = {eth_read_phy, eth_write_phy};
+ up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
+ if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
+ if (speed == MG_PHY_SPEED_1000M) {
+ ETH0->NETWORK_CONFIG |= MG_BIT(10);
+ }
+ MG_DEBUG(("Link is %uM %s-duplex",
+ speed == MG_PHY_SPEED_10M ? 10 :
+ (speed == MG_PHY_SPEED_100M ? 100 : 1000),
+ full_duplex ? "full" : "half"));
}
- ovf = !q_write(&qp, &e, sizeof(e));
+ (void) d;
+ return up;
}
-void qp_log(void) {
- struct qpentry e;
- const char *titles[] = {"IRQ ", "PUSH", "POP ", "DONE", "DROP", "OVFL"};
- for (int i = 0; i < 10 && q_read(&qp, &e); i++) {
- MG_INFO(("%lx %s %u", e.timestamp, titles[e.type], e.len));
+void ETH_IRQHandler(void) {
+ uint32_t irq_status = ETH0->INT_STATUS;
+ if (irq_status & MG_BIT(1)) {
+ for (uint8_t i = 0; i < ETH_DESC_CNT; i++) {
+ if (s_rxdesc[s_rxno][0] & MG_BIT(0)) {
+ size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1);
+ //MG_INFO(("Receive complete: %ld bytes", len));
+ mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
+ s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // OWN bit: handle control to DMA
+ if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
+ }
+ }
}
-}
-void qp_init(void) {
- qp.len = 500 * (sizeof(size_t) + sizeof(struct qpentry));
- qp.buf = calloc(1, qp.len); // THERE IS NO FREE
+ ETH0->INT_STATUS = irq_status;
}
-#endif // MIP_QPROFILE
-#endif // MG_ENABLE_MIP
+struct mg_tcpip_driver mg_tcpip_driver_xmc7 = {mg_tcpip_driver_xmc7_init,
+ mg_tcpip_driver_xmc7_tx, NULL,
+ mg_tcpip_driver_xmc7_up};
+#endif
@@ -1,5 +1,5 @@
// Copyright (c) 2004-2013 Sergey Lyubka
-// Copyright (c) 2013-2022 Cesanta Software Limited
+// Copyright (c) 2013-2024 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
@@ -15,58 +15,48 @@
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0-only or commercial
#ifndef MONGOOSE_H
#define MONGOOSE_H
-#define MG_VERSION "7.8"
+#define MG_VERSION "7.14"
#ifdef __cplusplus
extern "C" {
#endif
-#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h
-#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
-#define MG_ARCH_WIN32 2 // Windows
-#define MG_ARCH_ESP32 3 // ESP32
-#define MG_ARCH_ESP8266 4 // ESP8266
-#define MG_ARCH_FREERTOS 5 // FreeRTOS
-#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
-#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
-#define MG_ARCH_NEWLIB 8 // Bare metal ARM
-#define MG_ARCH_RTX 9 // Keil MDK RTX
-#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
-#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_config.h
+#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
+#define MG_ARCH_WIN32 2 // Windows
+#define MG_ARCH_ESP32 3 // ESP32
+#define MG_ARCH_ESP8266 4 // ESP8266
+#define MG_ARCH_FREERTOS 5 // FreeRTOS
+#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS
+#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
+#define MG_ARCH_NEWLIB 8 // Bare metal ARM
+#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
+#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
+#define MG_ARCH_RP2040 11 // Raspberry Pi RP2040
+#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
+#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
+#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__)
#define MG_ARCH MG_ARCH_UNIX
#elif defined(_WIN32)
#define MG_ARCH MG_ARCH_WIN32
-#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
-#define MG_ARCH MG_ARCH_ESP8266
-#elif defined(__ZEPHYR__)
-#define MG_ARCH MG_ARCH_ZEPHYR
-#elif defined(ESP_PLATFORM)
-#define MG_ARCH MG_ARCH_ESP32
-#elif defined(FREERTOS_IP_H)
-#define MG_ARCH MG_ARCH_FREERTOS
-#define MG_ENABLE_FREERTOS_TCP 1
-#elif defined(AZURE_RTOS_THREADX)
-#define MG_ARCH MG_ARCH_AZURERTOS
-#elif defined(PICO_TARGET_NAME)
-#define MG_ARCH MG_ARCH_RP2040
#endif
#endif // !defined(MG_ARCH)
#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
-#include "mongoose_custom.h" // keep this include
+#include "mongoose_config.h" // keep this include
#endif
#if !defined(MG_ARCH)
-#error "MG_ARCH is not specified and we couldn't guess it. Set -D MG_ARCH=..."
+#error "MG_ARCH is not specified and we couldn't guess it. Define MG_ARCH=... in your compiler"
#endif
// http://esr.ibiblio.org/?p=5095
@@ -130,7 +120,8 @@ extern "C" {
#include <sys/types.h>
#include <time.h>
-#include <esp_timer.h>
+#include <esp_ota_ops.h> // Use angle brackets to avoid
+#include <esp_timer.h> // amalgamation ditching them
#define MG_PATH_MAX 128
@@ -166,15 +157,24 @@ extern "C" {
#if MG_ARCH == MG_ARCH_FREERTOS
#include <ctype.h>
-// #include <errno.h> // Cannot include errno - might conflict with lwip!
+#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
+#include <errno.h>
+#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h> // rand(), strtol(), atoi()
+#include <stdlib.h> // rand(), strtol(), atoi()
#include <string.h>
+#if defined(__ARMCC_VERSION)
+#define mode_t size_t
+#include <alloca.h>
+#include <time.h>
+#elif defined(__CCRH__)
+#else
#include <sys/stat.h>
+#endif
#include <FreeRTOS.h>
#include <task.h>
@@ -186,7 +186,7 @@ extern "C" {
#define calloc(a, b) mg_calloc(a, b)
#define free(a) vPortFree(a)
#define malloc(a) pvPortMalloc(a)
-#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr)
+#define strdup(s) mg_mprintf("%s", s)
// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(size_t cnt, size_t size) {
@@ -240,9 +240,34 @@ static inline int mg_mkdir(const char *path, mode_t mode) {
#include <pico/stdlib.h>
int mkdir(const char *, mode_t);
#endif
-
-
-#if MG_ARCH == MG_ARCH_RTX
+
+
+#if MG_ARCH == MG_ARCH_RTTHREAD
+
+#include <rtthread.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
mongoose rev-id: 6bb40e6ec96f95bfd36a816b430ea2726fac9d05 Signed-off-by: Michael Glembotzki <Michael.Glembotzki@iris-sensing.com> --- mongoose/mongoose.c | 21614 +++++++++++++++++++++++++++++++----------- mongoose/mongoose.h | 2003 +++- 2 files changed, 17594 insertions(+), 6023 deletions(-) +#include <sys/types.h> +#include <time.h> + +#ifndef MG_IO_SIZE +#define MG_IO_SIZE 1460 +#endif + +#endif // MG_ARCH == MG_ARCH_RTTHREAD + + +#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \ + MG_ARCH == MG_ARCH_CMSIS_RTOS2 #include <ctype.h> #include <errno.h> @@ -252,11 +277,35 @@ int mkdir(const char *, mode_t); #include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <alloca.h> #include <string.h> #include <time.h> +#if MG_ARCH == MG_ARCH_CMSIS_RTOS1 +#include "cmsis_os.h" // keep this include +// https://developer.arm.com/documentation/ka003821/latest +extern uint32_t rt_time_get(void); +#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2 +#include "cmsis_os2.h" // keep this include +#endif + +#define strdup(s) mg_mprintf("%s", s) + +#if defined(__ARMCC_VERSION) +#define mode_t size_t +#define mkdir(a, b) mg_mkdir(a, b) +static inline int mg_mkdir(const char *path, mode_t mode) { + (void) path, (void) mode; + return -1; +} +#endif -#if !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) +#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \ + !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \ + (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) #define MG_ENABLE_RL 1 +#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE +#define MG_SOCK_LISTEN_BACKLOG_SIZE 3 +#endif #endif #endif @@ -403,7 +452,6 @@ typedef enum { false = 0, true = 1 } bool; #define MG_INVALID_SOCKET INVALID_SOCKET #define MG_SOCKET_TYPE SOCKET typedef unsigned long nfds_t; -#define MG_SOCKET_ERRNO WSAGetLastError() #if defined(_MSC_VER) #pragma comment(lib, "ws2_32.lib") #ifndef alloca @@ -411,9 +459,6 @@ typedef unsigned long nfds_t; #endif #endif #define poll(a, b, c) WSAPoll((a), (b), (c)) -#ifndef SO_EXCLUSIVEADDRUSE -#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR)) -#endif #define closesocket(x) closesocket(x) typedef int socklen_t; @@ -423,16 +468,24 @@ typedef int socklen_t; #define MG_PATH_MAX FILENAME_MAX #endif -#ifndef EINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK +#ifndef SO_EXCLUSIVEADDRUSE +#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR)) #endif +#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0) + +#define MG_SOCK_PENDING(errcode) \ + (((errcode) < 0) && \ + (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \ + WSAGetLastError() == WSAEWOULDBLOCK)) + +#define MG_SOCK_RESET(errcode) \ + (((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET)) + #define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX) -#define sleep(x) Sleep(x) +#define sleep(x) Sleep((x) *1000) #define mkdir(a, b) _mkdir(a) +#define timegm(x) _mkgmtime(x) #ifndef S_ISDIR #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) @@ -442,6 +495,10 @@ typedef int socklen_t; #define MG_ENABLE_DIRLIST 1 #endif +#ifndef SIGPIPE +#define SIGPIPE 0 +#endif + #endif @@ -451,8 +508,9 @@ typedef int socklen_t; #include <ctype.h> #include <errno.h> -#include <fcntl.h> #include <zephyr/net/socket.h> +#include <zephyr/posix/fcntl.h> +#include <zephyr/posix/sys/select.h> #include <stdarg.h> #include <stdbool.h> #include <stdint.h> @@ -464,11 +522,18 @@ typedef int socklen_t; #define MG_PUTCHAR(x) printk("%c", x) #ifndef strdup -#define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr) +#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) #endif #define strerror(x) zsock_gai_strerror(x) + +#ifndef FD_CLOEXEC #define FD_CLOEXEC 0 +#endif + +#ifndef F_SETFD #define F_SETFD 0 +#endif + #define MG_ENABLE_SSI 0 int rand(void); @@ -479,24 +544,12 @@ int sscanf(const char *, const char *, ...); #if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP -#include <ctype.h> -#include <errno.h> #include <limits.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <time.h> - -#include <FreeRTOS.h> #include <list.h> -#include <task.h> #include <FreeRTOS_IP.h> #include <FreeRTOS_Sockets.h> +#include <FreeRTOS_errno_TCP.h> // contents to be moved and file removed, some day #define MG_SOCKET_TYPE Socket_t #define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET @@ -512,6 +565,20 @@ int sscanf(const char *, const char *, ...); #define SO_ERROR 0 #define SOL_SOCKET 0 #define SO_REUSEADDR 0 + +#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) + +#define MG_SOCK_PENDING(errcode) \ + ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \ + (errcode) == -pdFREERTOS_ERRNO_EISCONN || \ + (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \ + (errcode) == -pdFREERTOS_ERRNO_EAGAIN) + +#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN) + +// actually only if optional timeout is enabled +#define MG_SOCK_INTR(fd) (fd == NULL) + #define sockaddr_in freertos_sockaddr #define sockaddr freertos_sockaddr #define accept(a, b, c) FreeRTOS_accept((a), (b), (c)) @@ -543,8 +610,17 @@ static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) { #if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP -#if defined(__GNUC__) + +#if defined(__GNUC__) && !defined(__ARMCC_VERSION) #include <sys/stat.h> +#endif + +struct timeval; + +#include <lwip/sockets.h> + +#if !LWIP_TIMEVAL_PRIVATE +#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both #include <sys/time.h> #else struct timeval { @@ -552,8 +628,7 @@ struct timeval { long tv_usec; }; #endif - -#include <lwip/sockets.h> +#endif #if LWIP_SOCKET != 1 // Sockets support disabled in LWIP by default @@ -565,16 +640,25 @@ struct timeval { #if defined(MG_ENABLE_RL) && MG_ENABLE_RL #include <rl_net.h> -#define MG_ENABLE_CUSTOM_MILLIS 1 #define closesocket(x) closesocket(x) -#define mkdir(a, b) (-1) -#define EWOULDBLOCK BSD_EWOULDBLOCK -#define EAGAIN BSD_EWOULDBLOCK -#define EINPROGRESS BSD_EWOULDBLOCK -#define EINTR BSD_EWOULDBLOCK -#define ECONNRESET BSD_ECONNRESET -#define EPIPE BSD_ECONNRESET + #define TCP_NODELAY SO_KEEPALIVE + +#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) + +#define MG_SOCK_PENDING(errcode) \ + ((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \ + (errcode) == BSD_EINPROGRESS) + +#define MG_SOCK_RESET(errcode) \ + ((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET) + +// In blocking mode, which is enabled by default, accept() waits for a +// connection request. In non blocking mode, you must call accept() +// again if the error code BSD_EWOULDBLOCK is returned. +#define MG_SOCK_INTR(fd) (fd == BSD_EWOULDBLOCK) + +#define socklen_t int #endif @@ -582,8 +666,12 @@ struct timeval { #define MG_ENABLE_LOG 1 #endif -#ifndef MG_ENABLE_MIP -#define MG_ENABLE_MIP 0 // Mongoose built-in network stack +#ifndef MG_ENABLE_CUSTOM_LOG +#define MG_ENABLE_CUSTOM_LOG 0 // Let user define their own MG_LOG +#endif + +#ifndef MG_ENABLE_TCPIP +#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack #endif #ifndef MG_ENABLE_LWIP @@ -599,7 +687,7 @@ struct timeval { #endif #ifndef MG_ENABLE_SOCKET -#define MG_ENABLE_SOCKET !MG_ENABLE_MIP +#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP #endif #ifndef MG_ENABLE_POLL @@ -614,18 +702,6 @@ struct timeval { #define MG_ENABLE_FATFS 0 #endif -#ifndef MG_ENABLE_MBEDTLS -#define MG_ENABLE_MBEDTLS 0 -#endif - -#ifndef MG_ENABLE_OPENSSL -#define MG_ENABLE_OPENSSL 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_TLS -#define MG_ENABLE_CUSTOM_TLS 0 -#endif - #ifndef MG_ENABLE_SSI #define MG_ENABLE_SSI 0 #endif @@ -634,6 +710,10 @@ struct timeval { #define MG_ENABLE_IPV6 0 #endif +#ifndef MG_IPV6_V6ONLY +#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address +#endif + #ifndef MG_ENABLE_MD5 #define MG_ENABLE_MD5 1 #endif @@ -659,12 +739,16 @@ struct timeval { #define MG_ENABLE_PACKED_FS 0 #endif +#ifndef MG_ENABLE_ASSERT +#define MG_ENABLE_ASSERT 0 +#endif + #ifndef MG_IO_SIZE #define MG_IO_SIZE 2048 // Granularity of the send/recv IO buffer growth #endif #ifndef MG_MAX_RECV_SIZE -#define MG_MAX_RECV_SIZE (3 * 1024 * 1024) // Maximum recv IO buffer size +#define MG_MAX_RECV_SIZE (3UL * 1024UL * 1024UL) // Maximum recv IO buffer size #endif #ifndef MG_DATA_SIZE @@ -688,18 +772,18 @@ struct timeval { #endif #ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 3 +#define MG_SOCK_LISTEN_BACKLOG_SIZE 128 #endif #ifndef MG_DIRSEP #define MG_DIRSEP '/' #endif -#ifndef MG_ENABLE_FILE +#ifndef MG_ENABLE_POSIX_FS #if defined(FOPEN_MAX) -#define MG_ENABLE_FILE 1 +#define MG_ENABLE_POSIX_FS 1 #else -#define MG_ENABLE_FILE 0 +#define MG_ENABLE_POSIX_FS 0 #endif #endif @@ -732,60 +816,112 @@ struct timeval { #define MG_EPOLL_MOD(c, wr) #endif +#ifndef MG_ENABLE_PROFILE +#define MG_ENABLE_PROFILE 0 +#endif +#ifndef MG_ENABLE_TCPIP_DRIVER_INIT // mg_mgr_init() will also initialize +#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for +#endif // Mongoose built-in network stack +#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223) +#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) +#endif -struct mg_str { - const char *ptr; // Pointer to string data - size_t len; // String len -}; +#ifndef MG_TCPIP_MASK +#define MG_TCPIP_MASK MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) +#endif + +#ifndef MG_TCPIP_GW +#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) +#endif + +#ifndef MG_SET_MAC_ADDRESS +#define MG_SET_MAC_ADDRESS(mac) +#endif -#define MG_NULL_STR \ - { NULL, 0 } +#ifndef MG_ENABLE_TCPIP_PRINT_DEBUG_STATS +#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 +#endif -#define MG_C_STR(a) \ - { (a), sizeof(a) - 1 } + + + +// Describes an arbitrary chunk of memory +struct mg_str { + char *buf; // String data + size_t len; // String length +}; // Using macro to avoid shadowing C++ struct constructor, see #1298 #define mg_str(s) mg_str_s(s) struct mg_str mg_str(const char *s); struct mg_str mg_str_n(const char *s, size_t n); -int mg_lower(const char *s); -int mg_ncasecmp(const char *s1, const char *s2, size_t len); int mg_casecmp(const char *s1, const char *s2); -int mg_vcmp(const struct mg_str *s1, const char *s2); -int mg_vcasecmp(const struct mg_str *str1, const char *str2); int mg_strcmp(const struct mg_str str1, const struct mg_str str2); -struct mg_str mg_strstrip(struct mg_str s); -struct mg_str mg_strdup(const struct mg_str s); -const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle); +int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps); -bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n); -bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); -bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char delim); -char *mg_hex(const void *buf, size_t len, char *dst); -void mg_unhex(const char *buf, size_t len, unsigned char *to); -unsigned long mg_unhexn(const char *s, size_t len); -int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip); -int64_t mg_to64(struct mg_str str); -uint64_t mg_tou64(struct mg_str str); -char *mg_remove_double_dots(char *s); +bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim); + +bool mg_str_to_num(struct mg_str, int base, void *val, size_t val_len); + +// Single producer, single consumer non-blocking queue + +struct mg_queue { + char *buf; + size_t size; + volatile size_t tail; + volatile size_t head; +}; + +void mg_queue_init(struct mg_queue *, char *, size_t); // Init queue +size_t mg_queue_book(struct mg_queue *, char **buf, size_t); // Reserve space +void mg_queue_add(struct mg_queue *, size_t); // Add new message +size_t mg_queue_next(struct mg_queue *, char **); // Get oldest message +void mg_queue_del(struct mg_queue *, size_t); // Delete oldest message -typedef void (*mg_pfn_t)(char, void *); // Custom putchar + + +typedef void (*mg_pfn_t)(char, void *); // Output function typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer -void mg_pfn_iobuf(char ch, void *param); // iobuf printer size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *); size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...); + + + + + + +// Convenience wrappers around mg_xprintf size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap); size_t mg_snprintf(char *, size_t, const char *fmt, ...); char *mg_vmprintf(const char *fmt, va_list *ap); char *mg_mprintf(const char *fmt, ...); +size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *); +size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...); + +// %M print helper functions +size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap); +size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap); + +// Various output functions +void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * +void mg_pfn_stdout(char c, void *param); // param: ignored + +// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) +#define MG_ESC(str) mg_print_esc, 0, (str) @@ -793,16 +929,23 @@ char *mg_mprintf(const char *fmt, ...); enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE }; +extern int mg_log_level; // Current log level, one of MG_LL_* + void mg_log(const char *fmt, ...); -bool mg_log_prefix(int ll, const char *file, int line, const char *fname); -void mg_log_set(int log_level); +void mg_log_prefix(int ll, const char *file, int line, const char *fname); +// bool mg_log2(int ll, const char *file, int line, const char *fmt, ...); void mg_hexdump(const void *buf, size_t len); void mg_log_set_fn(mg_pfn_t fn, void *param); +#define mg_log_set(level_) mg_log_level = (level_) + #if MG_ENABLE_LOG -#define MG_LOG(level, args) \ - do { \ - if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \ +#define MG_LOG(level, args) \ + do { \ + if ((level) <= mg_log_level) { \ + mg_log_prefix((level), __FILE__, __LINE__, __func__); \ + mg_log args; \ + } \ } while (0) #else #define MG_LOG(level, args) \ @@ -854,7 +997,9 @@ enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; // stat(), write(), read() calls. struct mg_fs { int (*st)(const char *path, size_t *size, time_t *mtime); // stat file - void (*ls)(const char *path, void (*fn)(const char *, void *), void *); + void (*ls)(const char *path, void (*fn)(const char *, void *), + void *); // List directory entries: call fn(file_name, fn_data) + // for each directory entry void *(*op)(const char *path, int flags); // Open file void (*cl)(void *fd); // Close file size_t (*rd)(void *fd, void *buf, size_t len); // Read file @@ -877,28 +1022,84 @@ struct mg_fd { struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags); void mg_fs_close(struct mg_fd *fd); -char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size); +bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len); +struct mg_str mg_file_read(struct mg_fs *fs, const char *path); bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t); bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); +// Packed API +const char *mg_unpack(const char *path, size_t *size, time_t *mtime); +const char *mg_unlist(size_t no); // Get no'th packed filename +struct mg_str mg_unpacked(const char *path); // Packed file as mg_str + + +#if MG_ENABLE_ASSERT +#include <assert.h> +#elif !defined(assert) +#define assert(x) +#endif + +void mg_bzero(volatile unsigned char *buf, size_t len); void mg_random(void *buf, size_t len); char *mg_random_str(char *buf, size_t len); uint16_t mg_ntohs(uint16_t net); uint32_t mg_ntohl(uint32_t net); uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); -uint64_t mg_millis(void); +uint64_t mg_millis(void); // Return milliseconds since boot +uint64_t mg_now(void); // Return milliseconds since Epoch +bool mg_path_is_sane(const struct mg_str path); #define mg_htons(x) mg_ntohs(x) #define mg_htonl(x) mg_ntohl(x) -#define MG_U32(a, b, c, d) \ - (((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \ - ((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255)) +#define MG_U32(a, b, c, d) \ + (((uint32_t) ((a) & 255) << 24) | ((uint32_t) ((b) & 255) << 16) | \ + ((uint32_t) ((c) & 255) << 8) | (uint32_t) ((d) & 255)) + +#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d)) + +// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip)) +#define MG_U8P(ADDR) ((uint8_t *) (ADDR)) +#define MG_IPADDR_PARTS(ADDR) \ + MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3] + +#define MG_REG(x) ((volatile uint32_t *) (x))[0] +#define MG_BIT(x) (((uint32_t) 1U) << (x)) +#define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK) + +#define MG_ROUND_UP(x, a) ((a) == 0 ? (x) : ((((x) + (a) -1) / (a)) * (a))) +#define MG_ROUND_DOWN(x, a) ((a) == 0 ? (x) : (((x) / (a)) * (a))) + +#if defined(__GNUC__) +#define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory") +#define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory") +#elif defined(__CCRH__) +#define MG_RH850_DISABLE_IRQ() __DI() +#define MG_RH850_ENABLE_IRQ() __EI() +#else +#define MG_ARM_DISABLE_IRQ() +#define MG_ARM_ENABLE_IRQ() +#endif + +#if defined(__CC_ARM) +#define MG_DSB() __dsb(0xf) +#elif defined(__ARMCC_VERSION) +#define MG_DSB() __builtin_arm_dsb(0xf) +#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) +#define MG_DSB() asm("DSB 0xf") +#elif defined(__ICCARM__) +#define MG_DSB() __iar_builtin_DSB() +#else +#define MG_DSB() +#endif + +struct mg_addr; +int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip); // Linked list management macros #define LIST_ADD_HEAD(type_, head_, elem_) \ @@ -946,10 +1147,11 @@ void mg_iobuf_free(struct mg_iobuf *); size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t); size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len); -int mg_base64_update(unsigned char p, char *to, int len); -int mg_base64_final(char *to, int len); -int mg_base64_encode(const unsigned char *p, int n, char *to); -int mg_base64_decode(const char *src, int n, char *dst); + +size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len); +size_t mg_base64_final(char *buf, size_t len); +size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t); +size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t); @@ -976,35 +1178,793 @@ typedef struct { void mg_sha1_init(mg_sha1_ctx *); void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len); void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *); +// https://github.com/B-Con/crypto-algorithms +// Author: Brad Conte (brad AT bradconte.com) +// Disclaimer: This code is presented "as is" without any guarantees. +// Details: Defines the API for the corresponding SHA1 implementation. +// Copyright: public domain + + + + + +typedef struct { + uint32_t state[8]; + uint64_t bits; + uint32_t len; + unsigned char buffer[64]; +} mg_sha256_ctx; + +void mg_sha256_init(mg_sha256_ctx *); +void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len); +void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *); +void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, + size_t datasz); +#ifndef TLS_X15519_H +#define TLS_X15519_H + + + +#define X25519_BYTES 32 +extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; + +int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], + const uint8_t x1[X25519_BYTES], int clamp); + + +#endif /* TLS_X15519_H */ +/****************************************************************************** + * + * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL + * + * This is a simple and straightforward implementation of AES-GCM authenticated + * encryption. The focus of this work was correctness & accuracy. It is written + * in straight 'C' without any particular focus upon optimization or speed. It + * should be endian (memory byte order) neutral since the few places that care + * are handled explicitly. + * + * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. + * + * It is intended for general purpose use, but was written in support of GRC's + * reference implementation of the SQRL (Secure Quick Reliable Login) client. + * + * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf + * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \ + * gcm/gcm-revised-spec.pdf + * + * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE + * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. + * + *******************************************************************************/ +#ifndef TLS_AES128_H +#define TLS_AES128_H + +typedef unsigned char uchar; // add some convienent shorter types +typedef unsigned int uint; + +/****************************************************************************** + * AES_CONTEXT : cipher context / holds inter-call data + ******************************************************************************/ +typedef struct { + int mode; // 1 for Encryption, 0 for Decryption + int rounds; // keysize-based rounds count + uint32_t *rk; // pointer to current round key + uint32_t buf[68]; // key expansion buffer +} aes_context; + + +#define GCM_AUTH_FAILURE 0x55555555 // authentication failure + +/****************************************************************************** + * GCM_CONTEXT : MUST be called once before ANY use of this library + ******************************************************************************/ +int mg_gcm_initialize(void); + +// +// aes-gcm.h +// MKo +// +// Created by Markus Kosmal on 20/11/14. +// +// +int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input, + size_t input_length, const unsigned char *key, + const size_t key_len, const unsigned char *iv, + const size_t iv_len, unsigned char *aead, + size_t aead_len, unsigned char *tag, + const size_t tag_len); + +int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, + size_t input_length, const unsigned char *key, + const size_t key_len, const unsigned char *iv, + const size_t iv_len); + +#endif /* TLS_AES128_H */ + +// End of aes128 PD + + + +#define MG_UECC_SUPPORTS_secp256r1 1 +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_H_ +#define _UECC_H_ + +/* Platform selection options. +If MG_UECC_PLATFORM is not defined, the code will try to guess it based on +compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */ +#define mg_uecc_arch_other 0 +#define mg_uecc_x86 1 +#define mg_uecc_x86_64 2 +#define mg_uecc_arm 3 +#define mg_uecc_arm_thumb 4 +#define mg_uecc_arm_thumb2 5 +#define mg_uecc_arm64 6 +#define mg_uecc_avr 7 + +/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform +(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will +be automatically set based on your platform. */ + +/* Optimization level; trade speed for code size. + Larger values produce code that is faster but larger. + Currently supported values are 0 - 4; 0 is unusably slow for most + applications. Optimization level 4 currently only has an effect ARM platforms + where more than one curve is enabled. */ +#ifndef MG_UECC_OPTIMIZATION_LEVEL +#define MG_UECC_OPTIMIZATION_LEVEL 2 +#endif + +/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a +specific function to be used for (scalar) squaring instead of the generic +multiplication function. This can make things faster somewhat faster, but +increases the code size. */ +#ifndef MG_UECC_SQUARE_FUNC +#define MG_UECC_SQUARE_FUNC 0 +#endif + +/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will +switch to native little-endian format for *all* arrays passed in and out of the +public API. This includes public and private keys, shared secrets, signatures +and message hashes. Using this switch reduces the amount of call stack memory +used by uECC, since less intermediate translations are required. Note that this +will *only* work on native little-endian processors and it will treat the +uint8_t arrays passed into the public API as word arrays, therefore requiring +the provided byte arrays to be word aligned on architectures that do not support +unaligned accesses. IMPORTANT: Keys and signatures generated with +MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures +generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same +endianness. */ +#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN +#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0 +#endif + +/* Curve support selection. Set to 0 to remove that curve. */ +#ifndef MG_UECC_SUPPORTS_secp160r1 +#define MG_UECC_SUPPORTS_secp160r1 0 +#endif +#ifndef MG_UECC_SUPPORTS_secp192r1 +#define MG_UECC_SUPPORTS_secp192r1 0 +#endif +#ifndef MG_UECC_SUPPORTS_secp224r1 +#define MG_UECC_SUPPORTS_secp224r1 0 +#endif +#ifndef MG_UECC_SUPPORTS_secp256r1 +#define MG_UECC_SUPPORTS_secp256r1 1 +#endif +#ifndef MG_UECC_SUPPORTS_secp256k1 +#define MG_UECC_SUPPORTS_secp256k1 0 +#endif + +/* Specifies whether compressed point format is supported. + Set to 0 to disable point compression/decompression functions. */ +#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT +#define MG_UECC_SUPPORT_COMPRESSED_POINT 1 +#endif + +struct MG_UECC_Curve_t; +typedef const struct MG_UECC_Curve_t *MG_UECC_Curve; + +#ifdef __cplusplus +extern "C" { +#endif + +#if MG_UECC_SUPPORTS_secp160r1 +MG_UECC_Curve mg_uecc_secp160r1(void); +#endif +#if MG_UECC_SUPPORTS_secp192r1 +MG_UECC_Curve mg_uecc_secp192r1(void); +#endif +#if MG_UECC_SUPPORTS_secp224r1 +MG_UECC_Curve mg_uecc_secp224r1(void); +#endif +#if MG_UECC_SUPPORTS_secp256r1 +MG_UECC_Curve mg_uecc_secp256r1(void); +#endif +#if MG_UECC_SUPPORTS_secp256k1 +MG_UECC_Curve mg_uecc_secp256k1(void); +#endif + +/* MG_UECC_RNG_Function type +The RNG function should fill 'size' random bytes into 'dest'. It should return 1 +if 'dest' was filled with random data, or 0 if the random data could not be +generated. The filled-in values should be either truly random, or from a +cryptographically-secure PRNG. + +A correctly functioning RNG function must be set (using mg_uecc_set_rng()) +before calling mg_uecc_make_key() or mg_uecc_sign(). + +Setting a correctly functioning RNG function improves the resistance to +side-channel attacks for mg_uecc_shared_secret() and +mg_uecc_sign_deterministic(). + +A correct RNG function is set by default when building for Windows, Linux, or OS +X. If you are building on another POSIX-compliant system that supports +/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined +RNG. For embedded platforms there is no predefined RNG function; you must +provide your own. +*/ +typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size); + +/* mg_uecc_set_rng() function. +Set the function that will be used to generate random bytes. The RNG function +should return 1 if the random data was generated, or 0 if the random data could +not be generated. + +On platforms where there is no predefined RNG function (eg embedded platforms), +this must be called before mg_uecc_make_key() or mg_uecc_sign() are used. + +Inputs: + rng_function - The function that will be used to generate random bytes. +*/ +void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function); + +/* mg_uecc_get_rng() function. + +Returns the function that will be used to generate random bytes. +*/ +MG_UECC_RNG_Function mg_uecc_get_rng(void); + +/* mg_uecc_curve_private_key_size() function. + +Returns the size of a private key for the curve in bytes. +*/ +int mg_uecc_curve_private_key_size(MG_UECC_Curve curve); + +/* mg_uecc_curve_public_key_size() function. + +Returns the size of a public key for the curve in bytes. +*/ +int mg_uecc_curve_public_key_size(MG_UECC_Curve curve); + +/* mg_uecc_make_key() function. +Create a public/private key pair. + +Outputs: + public_key - Will be filled in with the public key. Must be at least 2 * +the curve size (in bytes) long. For example, if the curve is secp256r1, +public_key must be 64 bytes long. private_key - Will be filled in with the +private key. Must be as long as the curve order; this is typically the same as +the curve size, except for secp160r1. For example, if the curve is secp256r1, +private_key must be 32 bytes long. + + For secp160r1, private_key must be 21 bytes long! Note that +the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it +being non-zero). + +Returns 1 if the key pair was generated successfully, 0 if an error occurred. +*/ +int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, + MG_UECC_Curve curve); + +/* mg_uecc_shared_secret() function. +Compute a shared secret given your secret key and someone else's public key. If +the public key is not from a trusted source and has not been previously +verified, you should verify it first using mg_uecc_valid_public_key(). Note: It +is recommended that you hash the result of mg_uecc_shared_secret() before using +it for symmetric encryption or HMAC. + +Inputs: + public_key - The public key of the remote party. + private_key - Your private key. + +Outputs: + secret - Will be filled in with the shared secret value. Must be the same +size as the curve size; for example, if the curve is secp256r1, secret must be +32 bytes long. + +Returns 1 if the shared secret was generated successfully, 0 if an error +occurred. +*/ +int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, + uint8_t *secret, MG_UECC_Curve curve); + +#if MG_UECC_SUPPORT_COMPRESSED_POINT +/* mg_uecc_compress() function. +Compress a public key. + +Inputs: + public_key - The public key to compress. + +Outputs: + compressed - Will be filled in with the compressed public key. Must be at +least (curve size + 1) bytes long; for example, if the curve is secp256r1, + compressed must be 33 bytes long. +*/ +void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, + MG_UECC_Curve curve); + +/* mg_uecc_decompress() function. +Decompress a compressed public key. + +Inputs: + compressed - The compressed public key. + +Outputs: + public_key - Will be filled in with the decompressed public key. +*/ +void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, + MG_UECC_Curve curve); +#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ + +/* mg_uecc_valid_public_key() function. +Check to see if a public key is valid. + +Note that you are not required to check for a valid public key before using any +other uECC functions. However, you may wish to avoid spending CPU time computing +a shared secret or verifying a signature using an invalid public key. + +Inputs: + public_key - The public key to check. + +Returns 1 if the public key is valid, 0 if it is invalid. +*/ +int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve); + +/* mg_uecc_compute_public_key() function. +Compute the corresponding public key for a private key. + +Inputs: + private_key - The private key to compute the public key for + +Outputs: + public_key - Will be filled in with the corresponding public key + +Returns 1 if the key was computed successfully, 0 if an error occurred. +*/ +int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, + MG_UECC_Curve curve); + +/* mg_uecc_sign() function. +Generate an ECDSA signature for a given hash value. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and +pass it in to this function along with your private key. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + +Outputs: + signature - Will be filled in with the signature value. Must be at least 2 * +curve size long. For example, if the curve is secp256r1, signature must be 64 +bytes long. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve); + +/* MG_UECC_HashContext structure. +This is used to pass in an arbitrary hash function to +mg_uecc_sign_deterministic(). The structure will be used for multiple hash +computations; each time a new hash is computed, init_hash() will be called, +followed by one or more calls to update_hash(), and finally a call to +finish_hash() to produce the resulting hash. + +The intention is that you will create a structure that includes +MG_UECC_HashContext followed by any hash-specific data. For example: + +typedef struct SHA256_HashContext { + MG_UECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +void init_SHA256(MG_UECC_HashContext *base) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Init(&context->ctx); +} + +void update_SHA256(MG_UECC_HashContext *base, + const uint8_t *message, + unsigned message_size) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Update(&context->ctx, message, message_size); +} + +void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Final(hash_result, &context->ctx); +} + +... when signing ... +{ + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, +32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature); +} +*/ +typedef struct MG_UECC_HashContext { + void (*init_hash)(const struct MG_UECC_HashContext *context); + void (*update_hash)(const struct MG_UECC_HashContext *context, + const uint8_t *message, unsigned message_size); + void (*finish_hash)(const struct MG_UECC_HashContext *context, + uint8_t *hash_result); + unsigned + block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ + unsigned + result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ + uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + + block_size) bytes. */ +} MG_UECC_HashContext; + +/* mg_uecc_sign_deterministic() function. +Generate an ECDSA signature for a given hash value, using a deterministic +algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng() +before calling this function; however, if the RNG is defined it will improve +resistance to side-channel attacks. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and +pass it to this function along with your private key and a hash context. Note +that the message_hash does not need to be computed with the same hash function +used by hash_context. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + hash_context - A hash context to use. + +Outputs: + signature - Will be filled in with the signature value. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int mg_uecc_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, unsigned hash_size, + const MG_UECC_HashContext *hash_context, + uint8_t *signature, MG_UECC_Curve curve); + +/* mg_uecc_verify() function. +Verify an ECDSA signature. + +Usage: Compute the hash of the signed data using the same hash as the signer and +pass it to this function along with the signer's public key and the signature +values (r and s). + +Inputs: + public_key - The signer's public key. + message_hash - The hash of the signed data. + hash_size - The size of message_hash in bytes. + signature - The signature value. + +Returns 1 if the signature is valid, 0 if it is invalid. +*/ +int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, + unsigned hash_size, const uint8_t *signature, + MG_UECC_Curve curve); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_H_ */ + +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_VLI_H_ +#define _UECC_VLI_H_ + +// +// + +/* Functions for raw large-integer manipulation. These are only available + if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */ +#ifndef MG_UECC_ENABLE_VLI_API +#define MG_UECC_ENABLE_VLI_API 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if MG_UECC_ENABLE_VLI_API + +void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words); + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, + wordcount_t num_words); + +/* Returns nonzero if bit 'bit' of vli is set. */ +mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit); + +/* Counts the number of bits required to represent vli. */ +bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, + const wordcount_t max_words); + +/* Sets dest = src. */ +void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src, + wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise */ +mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, + const mg_uecc_word_t *right, + wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns sign of left - right, in constant time. */ +cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, + const mg_uecc_word_t *right, wordcount_t num_words); + +/* Computes vli = vli >> 1. */ +void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words); + +/* Computes result = left + right, returning carry. Can modify in place. */ +mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, + const mg_uecc_word_t *left, + const mg_uecc_word_t *right, + wordcount_t num_words); + +/* Computes result = left - right, returning borrow. Can modify in place. */ +mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, + const mg_uecc_word_t *left, + const mg_uecc_word_t *right, + wordcount_t num_words); + +/* Computes result = left * right. Result must be 2 * num_words long. */ +void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left, + const mg_uecc_word_t *right, wordcount_t num_words); + +/* Computes result = left^2. Result must be 2 * num_words long. */ +void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left, + wordcount_t num_words); + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap + mod. */ +void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left, + const mg_uecc_word_t *right, const mg_uecc_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap + mod. */ +void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left, + const mg_uecc_word_t *right, const mg_uecc_word_t *mod, + wordcount_t num_words); + +/* Computes result = product % mod, where product is 2N words long. + Currently only designed to work for mod == curve->p or curve_n. */ +void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product, + const mg_uecc_word_t *mod, wordcount_t num_words); + +/* Calculates result = product (mod curve->p), where product is up to + 2 * curve->num_words long. */ +void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, + MG_UECC_Curve curve); + +/* Computes result = (left * right) % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left, + const mg_uecc_word_t *right, const mg_uecc_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left * right) % curve->p. */ +void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, + const mg_uecc_word_t *left, + const mg_uecc_word_t *right, MG_UECC_Curve curve); + +/* Computes result = left^2 % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left, + const mg_uecc_word_t *mod, wordcount_t num_words); + +/* Computes result = left^2 % curve->p. */ +void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, + const mg_uecc_word_t *left, + MG_UECC_Curve curve); + +/* Computes result = (1 / input) % mod.*/ +void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input, + const mg_uecc_word_t *mod, wordcount_t num_words); + +#if MG_UECC_SUPPORT_COMPRESSED_POINT +/* Calculates a = sqrt(a) (mod curve->p) */ +void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve); +#endif + +/* Converts an integer in uECC native format to big-endian bytes. */ +void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const mg_uecc_word_t *native); +/* Converts big-endian bytes to an integer in uECC native format. */ +void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes, + int num_bytes); + +unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve); +unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve); +unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve); +unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve); +unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve); +unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve); + +const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve); +const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve); +const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve); +const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve); + +int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve); + +/* Multiplies a point by a scalar. Points are represented by the X coordinate + followed by the Y coordinate in the same array, both coordinates are + curve->num_words long. Note that scalar must be curve->num_n_words long (NOT + curve->num_words). */ +void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, + const mg_uecc_word_t *scalar, MG_UECC_Curve curve); + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +int mg_uecc_generate_random_int(mg_uecc_word_t *random, + const mg_uecc_word_t *top, + wordcount_t num_words); + +#endif /* MG_UECC_ENABLE_VLI_API */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_VLI_H_ */ + +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_TYPES_H_ +#define _UECC_TYPES_H_ + +#ifndef MG_UECC_PLATFORM +#if defined(__AVR__) && __AVR__ +#define MG_UECC_PLATFORM mg_uecc_avr +#elif defined(__thumb2__) || \ + defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ +#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 +#elif defined(__thumb__) +#define MG_UECC_PLATFORM mg_uecc_arm_thumb +#elif defined(__arm__) || defined(_M_ARM) +#define MG_UECC_PLATFORM mg_uecc_arm +#elif defined(__aarch64__) +#define MG_UECC_PLATFORM mg_uecc_arm64 +#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ + defined(__I86__) +#define MG_UECC_PLATFORM mg_uecc_x86 +#elif defined(__amd64__) || defined(_M_X64) +#define MG_UECC_PLATFORM mg_uecc_x86_64 +#else +#define MG_UECC_PLATFORM mg_uecc_arch_other +#endif +#endif + +#ifndef MG_UECC_ARM_USE_UMAAL +#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) +#define MG_UECC_ARM_USE_UMAAL 1 +#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ + (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) +#define MG_UECC_ARM_USE_UMAAL 1 +#else +#define MG_UECC_ARM_USE_UMAAL 0 +#endif +#endif + +#ifndef MG_UECC_WORD_SIZE +#if MG_UECC_PLATFORM == mg_uecc_avr +#define MG_UECC_WORD_SIZE 1 +#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) +#define MG_UECC_WORD_SIZE 8 +#else +#define MG_UECC_WORD_SIZE 4 +#endif +#endif + +#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ + (MG_UECC_WORD_SIZE != 8) +#error "Unsupported value for MG_UECC_WORD_SIZE" +#endif + +#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) +#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") +#undef MG_UECC_WORD_SIZE +#define MG_UECC_WORD_SIZE 1 +#endif + +#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ + MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ + MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ + (MG_UECC_WORD_SIZE != 4)) +#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") +#undef MG_UECC_WORD_SIZE +#define MG_UECC_WORD_SIZE 4 +#endif + +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +typedef int8_t cmpresult_t; + +#if (MG_UECC_WORD_SIZE == 1) + +typedef uint8_t mg_uecc_word_t; +typedef uint16_t mg_uecc_dword_t; + +#define HIGH_BIT_SET 0x80 +#define MG_UECC_WORD_BITS 8 +#define MG_UECC_WORD_BITS_SHIFT 3 +#define MG_UECC_WORD_BITS_MASK 0x07 + +#elif (MG_UECC_WORD_SIZE == 4) + +typedef uint32_t mg_uecc_word_t; +typedef uint64_t mg_uecc_dword_t; + +#define HIGH_BIT_SET 0x80000000 +#define MG_UECC_WORD_BITS 32 +#define MG_UECC_WORD_BITS_SHIFT 5 +#define MG_UECC_WORD_BITS_MASK 0x01F + +#elif (MG_UECC_WORD_SIZE == 8) + +typedef uint64_t mg_uecc_word_t; + +#define HIGH_BIT_SET 0x8000000000000000U +#define MG_UECC_WORD_BITS 64 +#define MG_UECC_WORD_BITS_SHIFT 6 +#define MG_UECC_WORD_BITS_MASK 0x03F + +#endif /* MG_UECC_WORD_SIZE */ + +#endif /* _UECC_TYPES_H_ */ +// End of uecc BSD-2 struct mg_connection; typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, - void *ev_data, void *fn_data); + void *ev_data); void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_error(struct mg_connection *c, const char *fmt, ...); enum { - MG_EV_ERROR, // Error char *error_message - MG_EV_OPEN, // Connection created NULL - MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis - MG_EV_RESOLVE, // Host name is resolved NULL - MG_EV_CONNECT, // Connection established NULL - MG_EV_ACCEPT, // Connection accepted NULL - MG_EV_TLS_HS, // TLS handshake succeeded NULL - MG_EV_READ, // Data received from socket long *bytes_read - MG_EV_WRITE, // Data written to socket long *bytes_written - MG_EV_CLOSE, // Connection closed NULL - MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * - MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message * - MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * - MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * - MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * - MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * - MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * - MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code - MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis - MG_EV_USER // Starting ID for user events + MG_EV_ERROR, // Error char *error_message + MG_EV_OPEN, // Connection created NULL + MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis + MG_EV_RESOLVE, // Host name is resolved NULL + MG_EV_CONNECT, // Connection established NULL + MG_EV_ACCEPT, // Connection accepted NULL + MG_EV_TLS_HS, // TLS handshake succeeded NULL + MG_EV_READ, // Data received from socket long *bytes_read + MG_EV_WRITE, // Data written to socket long *bytes_written + MG_EV_CLOSE, // Connection closed NULL + MG_EV_HTTP_HDRS, // HTTP headers struct mg_http_message * + MG_EV_HTTP_MSG, // Full HTTP request/response struct mg_http_message * + MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * + MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * + MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * + MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * + MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * + MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code + MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis + MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data + MG_EV_USER // Starting ID for user events }; @@ -1021,10 +1981,10 @@ struct mg_dns { }; struct mg_addr { - uint16_t port; // TCP or UDP port in network byte order - uint32_t ip; // IP address in network byte order - uint8_t ip6[16]; // IPv6 address - bool is_ip6; // True when address is IPv6 address + uint8_t ip[16]; // Holds IPv4 or IPv6 address, in network byte order + uint16_t port; // TCP or UDP port in network byte order + uint8_t scope_id; // IPv6 scope ID + bool is_ip6; // True when address is IPv6 address }; struct mg_mgr { @@ -1036,12 +1996,14 @@ struct mg_mgr { unsigned long nextid; // Next connection ID unsigned long timerid; // Next timer ID void *userdata; // Arbitrary user data pointer + void *tls_ctx; // TLS context shared by all TLS sessions uint16_t mqtt_id; // MQTT IDs for pub/sub void *active_dns_requests; // DNS requests in progress struct mg_timer *timers; // Active timers int epoll_fd; // Used when MG_EPOLL_ENABLE=1 void *priv; // Used by the MIP stack size_t extraconnsize; // Used by the MIP stack + MG_SOCKET_TYPE pipe; // Socketpair end for mg_wakeup() #if MG_ENABLE_FREERTOS_TCP SocketSet_t ss; // NOTE(lsm): referenced from socket struct #endif @@ -1056,6 +2018,8 @@ struct mg_connection { unsigned long id; // Auto-incrementing unique connection ID struct mg_iobuf recv; // Incoming data struct mg_iobuf send; // Outgoing data + struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE + struct mg_iobuf rtls; // TLS only. Incoming encrypted data mg_event_handler_t fn; // User-specified event handler function void *fn_data; // User-specified function parameter mg_event_handler_t pfn; // Protocol-specific handler function @@ -1066,6 +2030,7 @@ struct mg_connection { unsigned is_client : 1; // Outbound (client) connection unsigned is_accepted : 1; // Accepted (server) connection unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress + unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress unsigned is_connecting : 1; // Non-blocking connect is in progress unsigned is_tls : 1; // TLS-enabled connection unsigned is_tls_hs : 1; // TLS handshake is in progress @@ -1096,20 +2061,18 @@ bool mg_send(struct mg_connection *, const void *, size_t); size_t mg_printf(struct mg_connection *, const char *fmt, ...); size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap); bool mg_aton(struct mg_str str, struct mg_addr *addr); -int mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *, bool udp); // These functions are used to integrate with custom network stacks struct mg_connection *mg_alloc_conn(struct mg_mgr *); void mg_close_conn(struct mg_connection *c); bool mg_open_listener(struct mg_connection *c, const char *url); + +// Utility functions +bool mg_wakeup(struct mg_mgr *, unsigned long id, const void *buf, size_t len); +bool mg_wakeup_init(struct mg_mgr *); struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, 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); - @@ -1127,7 +2090,6 @@ struct mg_http_message { struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers struct mg_str body; // Body struct mg_str head; // Request + headers - struct mg_str chunk; // Chunk for chunked encoding, or partial body struct mg_str message; // Request + headers + body }; @@ -1169,9 +2131,8 @@ int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t); int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); -bool mg_http_match_uri(const struct mg_http_message *, const char *glob); long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *path, size_t max_size); + struct mg_fs *fs, const char *dir, size_t max_size); void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); @@ -1183,60 +2144,84 @@ void mg_http_serve_ssi(struct mg_connection *c, const char *root, const char *fullpath); +#define MG_TLS_NONE 0 // No TLS support +#define MG_TLS_MBED 1 // mbedTLS +#define MG_TLS_OPENSSL 2 // OpenSSL +#define MG_TLS_WOLFSSL 5 // WolfSSL (based on OpenSSL) +#define MG_TLS_BUILTIN 3 // Built-in +#define MG_TLS_CUSTOM 4 // Custom implementation + +#ifndef MG_TLS +#define MG_TLS MG_TLS_NONE +#endif + struct mg_tls_opts { - const char *ca; // CA certificate file. For both listeners and clients - const char *crl; // Certificate Revocation List. For clients - const char *cert; // Certificate - const char *certkey; // Certificate key - const char *ciphers; // Cipher list - struct mg_str srvname; // If not empty, enables server name verification - struct mg_fs *fs; // FS API for reading certificate files + struct mg_str ca; // PEM or DER + struct mg_str cert; // PEM or DER + struct mg_str key; // PEM or DER + struct mg_str name; // If not empty, enable host name verification + int skip_verification; // Skip certificate and host name verification }; -void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *); +void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *opts); void mg_tls_free(struct mg_connection *); long mg_tls_send(struct mg_connection *, const void *buf, size_t len); long mg_tls_recv(struct mg_connection *, void *buf, size_t len); size_t mg_tls_pending(struct mg_connection *); void mg_tls_handshake(struct mg_connection *); +// Private +void mg_tls_ctx_init(struct mg_mgr *); +void mg_tls_ctx_free(struct mg_mgr *); + +// 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); -#if MG_ENABLE_MBEDTLS + +#if MG_TLS == MG_TLS_MBED #include <mbedtls/debug.h> #include <mbedtls/net_sockets.h> #include <mbedtls/ssl.h> +#include <mbedtls/ssl_ticket.h> + +struct mg_tls_ctx { + int dummy; +#ifdef MBEDTLS_SSL_SESSION_TICKETS + mbedtls_ssl_ticket_context tickets; +#endif +}; struct mg_tls { - char *cafile; // CA certificate path mbedtls_x509_crt ca; // Parsed CA certificate mbedtls_x509_crt cert; // Parsed certificate + mbedtls_pk_context pk; // Private key context mbedtls_ssl_context ssl; // SSL/TLS context mbedtls_ssl_config conf; // SSL-TLS config - mbedtls_pk_context pk; // Private key context +#ifdef MBEDTLS_SSL_SESSION_TICKETS + mbedtls_ssl_ticket_context ticket; // Session tickets context +#endif }; #endif -#if MG_ENABLE_OPENSSL +#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL -#ifdef CONFIG_SSL_IMPL_WOLFSSL -#include <wolfssl/openssl/err.h> -#include <wolfssl/openssl/ssl.h> -#else #include <openssl/err.h> #include <openssl/ssl.h> -#endif struct mg_tls { + BIO_METHOD *bm; SSL_CTX *ctx; SSL *ssl; }; @@ -1296,29 +2281,81 @@ int64_t mg_sntp_parse(const unsigned char *buf, size_t len); #define MQTT_CMD_DISCONNECT 14 #define MQTT_CMD_AUTH 15 +#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01 +#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02 +#define MQTT_PROP_CONTENT_TYPE 0x03 +#define MQTT_PROP_RESPONSE_TOPIC 0x08 +#define MQTT_PROP_CORRELATION_DATA 0x09 +#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B +#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11 +#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12 +#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13 +#define MQTT_PROP_AUTHENTICATION_METHOD 0x15 +#define MQTT_PROP_AUTHENTICATION_DATA 0x16 +#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17 +#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18 +#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19 +#define MQTT_PROP_RESPONSE_INFORMATION 0x1A +#define MQTT_PROP_SERVER_REFERENCE 0x1C +#define MQTT_PROP_REASON_STRING 0x1F +#define MQTT_PROP_RECEIVE_MAXIMUM 0x21 +#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22 +#define MQTT_PROP_TOPIC_ALIAS 0x23 +#define MQTT_PROP_MAXIMUM_QOS 0x24 +#define MQTT_PROP_RETAIN_AVAILABLE 0x25 +#define MQTT_PROP_USER_PROPERTY 0x26 +#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27 +#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28 +#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29 +#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A + +enum { + MQTT_PROP_TYPE_BYTE, + MQTT_PROP_TYPE_STRING, + MQTT_PROP_TYPE_STRING_PAIR, + MQTT_PROP_TYPE_BINARY_DATA, + MQTT_PROP_TYPE_VARIABLE_INT, + MQTT_PROP_TYPE_INT, + MQTT_PROP_TYPE_SHORT +}; + enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; +struct mg_mqtt_prop { + uint8_t id; // Enumerated at MQTT5 Reference + uint32_t iv; // Integer value for 8-, 16-, 32-bit integers types + struct mg_str key; // Non-NULL only for user property type + struct mg_str val; // Non-NULL only for UTF-8 types and user properties +}; + struct mg_mqtt_opts { - struct mg_str user; // Username, can be empty - struct mg_str pass; // Password, can be empty - struct mg_str client_id; // Client ID - struct mg_str will_topic; // Will topic - struct mg_str will_message; // Will message - uint8_t will_qos; // Will message quality of service - uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4. - uint16_t keepalive; // Keep-alive timer in seconds - bool will_retain; // Retain last will - bool clean; // Use clean session, 0 or 1 + struct mg_str user; // Username, can be empty + struct mg_str pass; // Password, can be empty + struct mg_str client_id; // Client ID + struct mg_str topic; // message/subscription topic + struct mg_str message; // message content + uint8_t qos; // message quality of service + uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4 + uint16_t keepalive; // Keep-alive timer in seconds + uint16_t retransmit_id; // For PUBLISH, init to 0 + bool retain; // Retain flag + bool clean; // Clean session flag + struct mg_mqtt_prop *props; // MQTT5 props array + size_t num_props; // number of props + struct mg_mqtt_prop *will_props; // Valid only for CONNECT packet (MQTT5) + size_t num_will_props; // Number of will props }; struct mg_mqtt_message { - struct mg_str topic; // Parsed topic - struct mg_str data; // Parsed message - struct mg_str dgram; // Whole MQTT datagram, including headers - uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH - uint8_t cmd; // MQTT command, one of MQTT_CMD_* - uint8_t qos; // Quality of service - uint8_t ack; // Connack return code. 0 - success + struct mg_str topic; // Parsed topic for PUBLISH + struct mg_str data; // Parsed message for PUBLISH + struct mg_str dgram; // Whole MQTT packet, including headers + uint16_t id; // For PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH + uint8_t cmd; // MQTT command, one of MQTT_CMD_* + uint8_t qos; // Quality of service + uint8_t ack; // CONNACK return code, 0 = success + size_t props_start; // Offset to the start of the properties (MQTT5) + size_t props_size; // Length of the properties }; struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, @@ -1327,15 +2364,16 @@ struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data); void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); -void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic, - struct mg_str data, int qos, bool retain); -void mg_mqtt_sub(struct mg_connection *, struct mg_str topic, int qos); +uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts); +void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts); int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, uint32_t len); void mg_mqtt_ping(struct mg_connection *); void mg_mqtt_pong(struct mg_connection *); -void mg_mqtt_disconnect(struct mg_connection *); +void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *); +size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *, + size_t ofs); @@ -1387,6 +2425,7 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; int mg_json_get(struct mg_str json, const char *path, int *toklen); +struct mg_str mg_json_get_tok(struct mg_str json, const char *path); bool mg_json_get_num(struct mg_str json, const char *path, double *v); bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); long mg_json_get_long(struct mg_str json, const char *path, long dflt); @@ -1394,6 +2433,10 @@ char *mg_json_get_str(struct mg_str json, const char *path); char *mg_json_get_hex(struct mg_str json, const char *path, int *len); char *mg_json_get_b64(struct mg_str json, const char *path, int *len); +bool mg_json_unescape(struct mg_str str, char *buf, size_t len); +size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, + struct mg_str *val); + @@ -1426,96 +2469,342 @@ void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap); void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...); void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *); void mg_rpc_list(struct mg_rpc_req *r); +// Copyright (c) 2023 Cesanta Software Limited +// All rights reserved -struct mip_if; // MIP network interface +#define MG_OTA_NONE 0 // No OTA support +#define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_ESP32 2 // ESP32 OTA implementation +#define MG_OTA_CUSTOM 100 // Custom implementation -struct mip_driver { - bool (*init)(struct mip_if *); // Initialise driver - size_t (*tx)(const void *, size_t, struct mip_if *); // Transmit frame - size_t (*rx)(void *buf, size_t len, struct mip_if *); // Receive frame (poll) - bool (*up)(struct mip_if *); // Up/down status -}; +#ifndef MG_OTA +#define MG_OTA MG_OTA_NONE +#endif + +#if defined(__GNUC__) && !defined(__APPLE__) +#define MG_IRAM __attribute__((section(".iram"))) +#else +#define MG_IRAM +#endif -// Receive queue - single producer, single consumer queue. Interrupt-based -// drivers copy received frames to the queue in interrupt context. mip_poll() -// function runs in event loop context, reads from the queue -struct queue { - uint8_t *buf; - size_t len; - volatile size_t tail, head; +// Firmware update API +bool mg_ota_begin(size_t new_firmware_size); // Start writing +bool mg_ota_write(const void *buf, size_t len); // Write chunk, aligned to 1k +bool mg_ota_end(void); // Stop writing + +enum { + MG_OTA_UNAVAILABLE = 0, // No OTA information is present + MG_OTA_FIRST_BOOT = 1, // Device booting the first time after the OTA + MG_OTA_UNCOMMITTED = 2, // Ditto, but marking us for the rollback + MG_OTA_COMMITTED = 3 // The firmware is good }; +enum { MG_FIRMWARE_CURRENT = 0, MG_FIRMWARE_PREVIOUS = 1 }; + +int mg_ota_status(int firmware); // Return firmware status MG_OTA_* +uint32_t mg_ota_crc32(int firmware); // Return firmware checksum +uint32_t mg_ota_timestamp(int firmware); // Firmware timestamp, UNIX UTC epoch +size_t mg_ota_size(int firmware); // Firmware size + +bool mg_ota_commit(void); // Commit current firmware +bool mg_ota_rollback(void); // Rollback to the previous firmware +MG_IRAM void mg_ota_boot(void); // Bootloader function +// Copyright (c) 2023 Cesanta Software Limited +// All rights reserved -#define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 -#define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size + + + + +#define MG_DEVICE_NONE 0 // Dummy system + +#define MG_DEVICE_STM32H5 1 // STM32 H5 +#define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_CH32V307 100 // WCH CH32V307 +#define MG_DEVICE_U2A 200 // Renesas U2A16, U2A8, U2A6 +#define MG_DEVICE_RT1020 300 // IMXRT1020 +#define MG_DEVICE_RT1060 301 // IMXRT1060 +#define MG_DEVICE_CUSTOM 1000 // Custom implementation + +#ifndef MG_DEVICE +#define MG_DEVICE MG_DEVICE_NONE +#endif + +// Flash information +void *mg_flash_start(void); // Return flash start address +size_t mg_flash_size(void); // Return flash size +size_t mg_flash_sector_size(void); // Return flash sector size +size_t mg_flash_write_align(void); // Return flash write align, minimum 4 +int mg_flash_bank(void); // 0: not dual bank, 1: bank1, 2: bank2 + +// Write, erase, swap bank +bool mg_flash_write(void *addr, const void *buf, size_t len); +bool mg_flash_erase(void *sector); +bool mg_flash_swap_bank(void); + +// Convenience functions to store data on a flash sector with wear levelling +// If `sector` is NULL, then the last sector of flash is used +bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len); +bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len); + +void mg_device_reset(void); // Reboot device immediately + + + + + + +#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP +struct mg_tcpip_if; // Mongoose TCP/IP network interface + +struct mg_tcpip_driver { + bool (*init)(struct mg_tcpip_if *); // Init driver + size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit frame + size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Receive frame + bool (*up)(struct mg_tcpip_if *); // Up/down status +}; // Network interface -struct mip_if { - uint8_t mac[6]; // MAC address. Must be set to a valid MAC - uint32_t ip, mask, gw; // IP address, mask, default gateway - struct mg_str rx; // Output (TX) buffer - struct mg_str tx; // Input (RX) buffer - bool enable_dhcp_client; // Enable DCHP client - bool enable_dhcp_server; // Enable DCHP server - struct mip_driver *driver; // Low level driver - void *driver_data; // Driver-specific data - struct mg_mgr *mgr; // Mongoose event manager - struct queue queue; // Set queue.len for interrupt based drivers +struct mg_tcpip_if { + uint8_t mac[6]; // MAC address. Must be set to a valid MAC + uint32_t ip, mask, gw; // IP address, mask, default gateway + struct mg_str tx; // Output (TX) buffer + bool enable_dhcp_client; // Enable DCHP client + bool enable_dhcp_server; // Enable DCHP server + bool enable_get_gateway; // DCHP server sets client as gateway + bool enable_crc32_check; // Do a CRC check on RX frames and strip it + bool enable_mac_check; // Do a MAC check on RX frames + struct mg_tcpip_driver *driver; // Low level driver + void *driver_data; // Driver-specific data + struct mg_mgr *mgr; // Mongoose event manager + struct mg_queue recv_queue; // Receive queue + uint16_t mtu; // Interface MTU +#define MG_TCPIP_MTU_DEFAULT 1500 // Internal state, user can use it but should not change it - uint64_t now; // Current time - uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state - uint64_t lease_expire; // Lease expiration time - uint8_t arp_cache[MIP_ARP_CS]; // Each entry is 12 bytes - uint16_t eport; // Next ephemeral port - uint16_t dropped; // Number of dropped frames - uint8_t state; // Current state -#define MIP_STATE_DOWN 0 // Interface is down -#define MIP_STATE_UP 1 // Interface is up -#define MIP_STATE_READY 2 // Interface is up and has IP + uint8_t gwmac[6]; // Router's MAC + uint64_t now; // Current time + uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state + uint64_t lease_expire; // Lease expiration time, in ms + uint16_t eport; // Next ephemeral port + volatile uint32_t ndrop; // Number of received, but dropped frames + volatile uint32_t nrecv; // Number of received frames + volatile uint32_t nsent; // Number of transmitted frames + volatile uint32_t nerr; // Number of driver errors + uint8_t state; // Current state +#define MG_TCPIP_STATE_DOWN 0 // Interface is down +#define MG_TCPIP_STATE_UP 1 // Interface is up +#define MG_TCPIP_STATE_REQ 2 // Interface is up and has requested an IP +#define MG_TCPIP_STATE_READY 3 // Interface is up and has an IP assigned }; -void mip_init(struct mg_mgr *, struct mip_if *); -void mip_free(struct mip_if *); -void mip_qwrite(void *buf, size_t len, struct mip_if *ifp); -size_t mip_qread(void *buf, struct mip_if *ifp); -// conveniency rx function for IRQ-driven drivers -size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp); - -extern struct mip_driver mip_driver_stm32; -extern struct mip_driver mip_driver_w5500; -extern struct mip_driver mip_driver_tm4c; +void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); +void mg_tcpip_free(struct mg_tcpip_if *); +void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp); + +extern struct mg_tcpip_driver mg_tcpip_driver_stm32f; +extern struct mg_tcpip_driver mg_tcpip_driver_w5500; +extern struct mg_tcpip_driver mg_tcpip_driver_tm4c; +extern struct mg_tcpip_driver mg_tcpip_driver_stm32h; +extern struct mg_tcpip_driver mg_tcpip_driver_imxrt; +extern struct mg_tcpip_driver mg_tcpip_driver_same54; +extern struct mg_tcpip_driver mg_tcpip_driver_cmsis; +extern struct mg_tcpip_driver mg_tcpip_driver_ra; +extern struct mg_tcpip_driver mg_tcpip_driver_xmc; +extern struct mg_tcpip_driver mg_tcpip_driver_xmc7; // Drivers that require SPI, can use this SPI abstraction -struct mip_spi { +struct mg_tcpip_spi { void *spi; // Opaque SPI bus descriptor void (*begin)(void *); // SPI begin: slave select low void (*end)(void *); // SPI end: slave select high uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply }; +#endif -#ifdef MIP_QPROFILE -enum { - QP_IRQTRIGGERED = 0, // payload is number of interrupts so far - QP_FRAMEPUSHED, // available space in the frame queue - QP_FRAMEPOPPED, // available space in the frame queue - QP_FRAMEDONE, // available space in the frame queue - QP_FRAMEDROPPED, // number of dropped frames - QP_QUEUEOVF // profiling queue is full, payload is number of frame drops + + +// Macros to record timestamped events that happens with a connection. +// They are saved into a c->prof IO buffer, each event is a name and a 32-bit +// timestamp in milliseconds since connection init time. +// +// Test (run in two separate terminals): +// make -C examples/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1 +// curl localhost:8000 +// Output: +// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile: +// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init +// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN +// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT +// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ +// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG +// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE +// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE +// +// Usage: +// Enable profiling by setting MG_ENABLE_PROFILE=1 +// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure + +#if MG_ENABLE_PROFILE +struct mg_profitem { + const char *name; // Event name + uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN) }; -void qp_mark(unsigned int type, int len); -void qp_log(void); // timestamp, type, payload -void qp_init(void); +#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc + +// Adding a profile item to the c->prof. Must be as fast as possible. +// Reallocation of the c->prof iobuf is not desirable here, that's why we +// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY. +// This macro just inits and copies 8 bytes, and calls mg_millis(), +// which should be fast enough. +#define MG_PROF_ADD(c, name_) \ + do { \ + struct mg_iobuf *io = &c->prof; \ + uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \ + struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \ + mg_iobuf_add(io, io->len, &item, sizeof(item)); \ + } while (0) + +// Initialising profile for a new connection. Not time sensitive +#define MG_PROF_INIT(c) \ + do { \ + struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \ + mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \ + mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \ + } while (0) + +#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof) + +// Dumping the profile. Not time sensitive +#define MG_PROF_DUMP(c) \ + do { \ + struct mg_iobuf *io = &c->prof; \ + struct mg_profitem *p = (struct mg_profitem *) io->buf; \ + struct mg_profitem *e = &p[io->len / sizeof(*p)]; \ + MG_INFO(("%lu profile:", c->id)); \ + while (p < e) { \ + MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \ + p++; \ + } \ + } while (0) + #else -#define qp_mark(a, b) +#define MG_PROF_INIT(c) +#define MG_PROF_FREE(c) +#define MG_PROF_ADD(c, name) +#define MG_PROF_DUMP(c) +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS + +#include "Driver_ETH_MAC.h" // keep this include +#include "Driver_ETH_PHY.h" // keep this include + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT) && MG_ENABLE_DRIVER_IMXRT + +struct mg_tcpip_driver_imxrt_data { + // MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk), + // must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz + // 37.5.1.8.2, Table 37-46 : f = ipg_clk / (2(mdc_cr + 1)) + // ipg_clk mdc_cr VALUE + // -------------------------- + // -1 <-- TODO() tell driver to guess the value + // 25 MHz 4 + // 33 MHz 6 + // 40 MHz 7 + // 50 MHz 9 + // 66 MHz 13 + int mdc_cr; // Valid values: -1 to 63 + + uint8_t phy_addr; // PHY address +}; + +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 2 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 24 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_imxrt_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_imxrt; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: imxrt, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + #endif -struct mip_driver_stm32_data { + + +struct mg_phy { + uint16_t (*read_reg)(uint8_t addr, uint8_t reg); + void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value); +}; + +// PHY configuration settings, bitmask +enum { + MG_PHY_LEDS_ACTIVE_HIGH = + (1 << 0), // Set if PHY LEDs are connected to ground + MG_PHY_CLOCKS_MAC = + (1 << 1) // Set when PHY clocks MAC. Otherwise, MAC clocks PHY +}; + +enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M }; + +void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t config); +bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex, + uint8_t *speed); + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RA) && MG_ENABLE_DRIVER_RA + +struct mg_tcpip_driver_ra_data { + // MDC clock "divider". MDC clock is software generated, + uint32_t clock; // core clock frequency in Hz + uint16_t irqno; // IRQn, R_ICU->IELSR[irqno] + uint8_t phy_addr; // PHY address +}; + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && MG_ENABLE_DRIVER_SAME54 + +struct mg_tcpip_driver_same54_data { + int mdc_cr; +}; + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 5 +#endif + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ + MG_ENABLE_DRIVER_STM32F + +struct mg_tcpip_driver_stm32f_data { // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz // HCLK range DIVIDER mdc_cr VALUE // ------------------------------------- @@ -1528,10 +2817,88 @@ struct mip_driver_stm32_data { // 216-310 MHz HCLK/124 5 // 110, 111 Reserved int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 + + uint8_t phy_addr; // PHY address +}; + +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 0 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 4 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_stm32f_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_stm32f; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: stm32f, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \ + MG_ENABLE_DRIVER_STM32H + +struct mg_tcpip_driver_stm32h_data { + // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz + // HCLK range DIVIDER mdc_cr VALUE + // ------------------------------------- + // -1 <-- tell driver to guess the value + // 60-100 MHz HCLK/42 0 + // 100-150 MHz HCLK/62 1 + // 20-35 MHz HCLK/16 2 + // 35-60 MHz HCLK/26 3 + // 150-250 MHz HCLK/102 4 <-- value for max speed HSI + // 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI + // 110, 111 Reserved + int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 + + uint8_t phy_addr; // PHY address + uint8_t phy_conf; // PHY config }; +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 0 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 4 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_stm32h_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_stm32h; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + +#endif + -struct mip_driver_tm4c_data { +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C + +struct mg_tcpip_driver_tm4c_data { // MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz // SYSCLK range DIVIDER mdc_cr VALUE // ------------------------------------- @@ -1544,6 +2911,102 @@ struct mip_driver_tm4c_data { int mdc_cr; // Valid values: -1, 0, 1, 2, 3 }; +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 1 +#endif + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500 + +#undef MG_ENABLE_TCPIP_DRIVER_INIT +#define MG_ENABLE_TCPIP_DRIVER_INIT 0 + +#endif + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 + +struct mg_tcpip_driver_xmc7_data { + int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 + uint8_t phy_addr; +}; + +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 0 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 3 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_xmc7_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_xmc7; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + +#endif + + + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC + +struct mg_tcpip_driver_xmc_data { + // 13.2.8.1 Station Management Functions + // MDC clock divider (). MDC clock is derived from ETH MAC clock + // It must not exceed 2.5MHz + // ETH Clock range DIVIDER mdc_cr VALUE + // -------------------------------------------- + // -1 <-- tell driver to guess the value + // 60-100 MHz ETH Clock/42 0 + // 100-150 MHz ETH Clock/62 1 + // 20-35 MHz ETH Clock/16 2 + // 35-60 MHz ETH Clock/26 3 + // 150-250 MHz ETH Clock/102 4 + // 250-300 MHz ETH Clock/124 5 + // 110, 111 Reserved + int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 + uint8_t phy_addr; +}; + +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 0 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 4 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_xmc_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_xmc; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + +#endif + #ifdef __cplusplus } #endif