assorted patches

This commit is contained in:
Sergio R. Caprile 2026-06-17 11:07:05 -03:00 committed by Sergio R. Caprile
parent 07a49a7a2d
commit a9df523f76
34 changed files with 1240 additions and 592 deletions

View file

@ -262,6 +262,7 @@ jobs:
name: tutorials_win ${{ matrix.ssl }} name: tutorials_win ${{ matrix.ssl }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 }
# - uses: egor-tensin/setup-mingw@v2 # - uses: egor-tensin/setup-mingw@v2
# with: # with:
# platform: x64 # platform: x64

View file

@ -114,6 +114,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: { fetch-depth: 2 }
# - uses: egor-tensin/setup-mingw@v2 # - uses: egor-tensin/setup-mingw@v2
# with: # with:
# platform: x64 # platform: x64

File diff suppressed because it is too large Load diff

View file

@ -1529,6 +1529,8 @@ size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
// Output functions for use as the fn argument to mg_xprintf/mg_vxprintf. // Output functions for use as the fn argument to mg_xprintf/mg_vxprintf.
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed) void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed)
@ -1632,7 +1634,7 @@ void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
// Flags returned by mg_fs.st() and passed to mg_fs.open(). // Flags returned by mg_fs.st() and passed to mg_fs.open().
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4, MG_FS_EXCL = 8 };
// Filesystem abstraction. Implement all function pointers to plug in a custom // Filesystem abstraction. Implement all function pointers to plug in a custom
// filesystem. Short UNIX-style names are used deliberately to avoid conflicts // filesystem. Short UNIX-style names are used deliberately to avoid conflicts
@ -3476,6 +3478,7 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *q, size_t q_len, const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len, const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len); uint8_t *signature, size_t sig_len);
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
#endif // TLS_RSA_H #endif // TLS_RSA_H
@ -4743,6 +4746,7 @@ struct mg_tcpip_if {
char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Hostname sent in DHCP requests; defaults to "mip" char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Hostname sent in DHCP requests; defaults to "mip"
uint16_t mtu; // IP MTU (max payload size at the IP layer) uint16_t mtu; // IP MTU (max payload size at the IP layer)
uint16_t framesize; // Maximum L2 frame size in bytes uint16_t framesize; // Maximum L2 frame size in bytes
uint16_t l2mtu; // L2 frame payload, default net MTU
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses
@ -5651,6 +5655,41 @@ struct mg_tcpip_driver_tms570_data {
#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 #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
struct mg_tcpip_driver_xmc_data { struct mg_tcpip_driver_xmc_data {
@ -5697,41 +5736,6 @@ struct mg_tcpip_driver_xmc_data {
#endif #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
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -280,8 +280,8 @@ static void sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests; struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
uint16_t id; uint16_t id;
mg_random(&id, sizeof(uint16_t)); mg_random(&id, sizeof(uint16_t));
// TODO(): traverse reqs and check id != reqs->txnid; repeat otherwise if (reqs != NULL) // no seq, no collision for upto 256 in-flight requests
if (reqs != NULL) id = (uint16_t) (reqs->txnid + 1); // no collision id = (uint16_t) (reqs->txnid + (id & 0xFF) + 1);
d->txnid = id; d->txnid = id;
d->next = reqs; d->next = reqs;
c->mgr->active_dns_requests = d; c->mgr->active_dns_requests = d;

View file

@ -1054,7 +1054,7 @@ static size_t cyw_spi_poll(uint8_t *response) {
} }
static size_t cyw_spi_tx(uint32_t *data, uint16_t len) { static size_t cyw_spi_tx(uint32_t *data, uint16_t len) {
while (len & 3) data[len++] = 0; // SPI 32-bit padding while (len & 3) ((uint8_t *)data)[len++] = 0; // SPI 32-bit padding
return cyw_spi_write(CYW_SPID_FUNC_WLAN, 0, data, len) ? len: 0; return cyw_spi_write(CYW_SPID_FUNC_WLAN, 0, data, len) ? len: 0;
} }

View file

@ -182,11 +182,11 @@ void ENET_IRQHandler(void) {
(MG_BIT(21) | MG_BIT(20) | MG_BIT(18) | MG_BIT(17) | MG_BIT(16)))) { (MG_BIT(21) | MG_BIT(20) | MG_BIT(18) | MG_BIT(17) | MG_BIT(16)))) {
size_t len = s_rxdesc[s_rxno][0] & 0xffff; size_t len = s_rxdesc[s_rxno][0] & 0xffff;
mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
s_rxdesc[s_rxno][0] |= MG_BIT(31); // OWN bit: handle control to DMA
MG_DSB();
ENET->RDAR = 0;
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
} }
s_rxdesc[s_rxno][0] |= MG_BIT(31); // OWN bit: handle control to DMA
MG_DSB();
ENET->RDAR = 0;
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
} }
} }
} }

View file

@ -155,7 +155,10 @@ static size_t mg_tcpip_driver_tms570_tx(const void *buf, size_t len,
len = 0; // fail len = 0; // fail
} else { } else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data memcpy(s_txbuf[s_txno], buf, len); // Copy data
if (len < 128) len = 128; if (len < 128) {
memset(s_txbuf[s_txno] + len, 0, 128 - len);
len = 128;
}
s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len
s_txdesc[s_txno][3] = s_txdesc[s_txno][3] =
SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length

View file

@ -39,7 +39,7 @@ static size_t w5100_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
} else { } else {
uint16_t remaining_len = rxbuf_size - ptr_ofs; uint16_t remaining_len = rxbuf_size - ptr_ofs;
w5100_rn(s, rxbuf_addr + ptr_ofs, buf, remaining_len); w5100_rn(s, rxbuf_addr + ptr_ofs, buf, remaining_len);
w5100_rn(s, rxbuf_addr, buf + remaining_len, n - remaining_len); w5100_rn(s, rxbuf_addr, buf + remaining_len, r - remaining_len);
} }
w5100_w2(s, 0x428, (uint16_t) (ptr + n)); w5100_w2(s, 0x428, (uint16_t) (ptr + n));
w5100_w1(s, 0x401, 0x40); // Sock0 CR -> RECV w5100_w1(s, 0x401, 0x40); // Sock0 CR -> RECV

View file

@ -68,16 +68,17 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) {
while (d < 1.0 && d / mul < 1.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); // printf(" --> %g %d %g %g\n", saved, e, t, mul);
if (tz && e >= width && width > 1) { if (tz && (e >= width || e <= -width) && width > 1) {
n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); char exp[6];
int ne;
n = (int) mg_dtoa(buf + s, sizeof(buf) - (size_t) s, saved / mul, width, tz);
// printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
n += addexp(buf + s + n, e, '+'); ne = addexp(exp, e < 0 ? -e : e, e < 0 ? '-' : '+');
return mg_snprintf(dst, dstlen, "%.*s", n, buf); if (s + n + ne >= (int) sizeof(buf))
} else if (tz && e <= -width && width > 1) { n = (int) sizeof(buf) - s - ne - 1;
n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); memcpy(buf + s + n, exp, (size_t) ne);
// printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); n += ne;
n += addexp(buf + s + n, -e, '-'); return mg_snprintf(dst, dstlen, "%.*s", s + n, buf);
return mg_snprintf(dst, dstlen, "%.*s", n, buf);
} else { } else {
int targ_width = width; int targ_width = width;
for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {

View file

@ -44,17 +44,18 @@ bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
size_t len) { size_t len) {
bool result = false; bool result = false;
struct mg_fd *fd; struct mg_fd *fd;
char tmp[MG_PATH_MAX]; char tmp[MG_PATH_MAX], rnd[10];
mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand()); size_t path_len = mg_snprintf(tmp, sizeof(tmp), "%s..%s", path,
if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) { mg_random_str(rnd, sizeof(rnd)));
if (path_len < sizeof(tmp) &&
(fd = mg_fs_open(fs, tmp, MG_FS_WRITE | MG_FS_EXCL)) != NULL) {
result = fs->wr(fd->fd, buf, len) == len; result = fs->wr(fd->fd, buf, len) == len;
mg_fs_close(fd); mg_fs_close(fd);
if (result) { if (result) {
fs->rm(path); fs->rm(path);
fs->mv(tmp, path); result = fs->mv(tmp, path);
} else {
fs->rm(tmp);
} }
fs->rm(tmp);
} }
return result; return result;
} }

View file

@ -4,7 +4,7 @@
#include "config.h" #include "config.h"
// Flags returned by mg_fs.st() and passed to mg_fs.open(). // Flags returned by mg_fs.st() and passed to mg_fs.open().
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4, MG_FS_EXCL = 8 };
// Filesystem abstraction. Implement all function pointers to plug in a custom // Filesystem abstraction. Implement all function pointers to plug in a custom
// filesystem. Short UNIX-style names are used deliberately to avoid conflicts // filesystem. Short UNIX-style names are used deliberately to avoid conflicts

View file

@ -1,4 +1,5 @@
#include "arch.h" #include "arch.h"
#include "event.h"
#include "fs.h" #include "fs.h"
#if MG_ENABLE_FATFS #if MG_ENABLE_FATFS
@ -69,17 +70,22 @@ static void ff_list(const char *dir, void (*fn)(const char *, void *),
} }
static void *ff_open(const char *path, int flags) { static void *ff_open(const char *path, int flags) {
FIL f; FIL *fp = NULL;
unsigned char mode = FA_READ; unsigned char mode = FA_READ;
if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND; if (flags & MG_FS_WRITE) {
if (f_open(&f, path, mode) == 0) { mode |= FA_WRITE;
FIL *fp; if (flags & MG_FS_EXCL) {
if ((fp = mg_calloc(1, sizeof(*fp))) != NULL) { mode |= FA_OPEN_ALWAYS | FA_OPEN_APPEND;
memcpy(fp, &f, sizeof(*fp)); } else {
return fp; mode |= FA_CREATE_NEW;
} }
} }
return NULL; if ((fp = mg_calloc(1, sizeof(*fp))) != NULL &&
f_open(fp, path, mode) != FR_OK) {
mg_free(fp);
fp = NULL;
}
return fp;
} }
static void ff_close(void *fp) { static void ff_close(void *fp) {

View file

@ -96,6 +96,7 @@ DIR *opendir(const char *name) {
DIR *d = NULL; DIR *d = NULL;
wchar_t wpath[MAX_PATH]; wchar_t wpath[MAX_PATH];
DWORD attrs; DWORD attrs;
size_t n;
if (name == NULL) { if (name == NULL) {
SetLastError(ERROR_BAD_ARGUMENTS); SetLastError(ERROR_BAD_ARGUMENTS);
@ -105,9 +106,16 @@ DIR *opendir(const char *name) {
to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
attrs = GetFileAttributesW(wpath); attrs = GetFileAttributesW(wpath);
if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
(void) wcscat(wpath, L"\\*"); n = wcslen(wpath);
d->handle = FindFirstFileW(wpath, &d->info); if (n <= (sizeof(wpath) / sizeof(wpath[0])) - 3) {
d->result.d_name[0] = '\0'; (void) wcscat(wpath, L"\\*");
d->handle = FindFirstFileW(wpath, &d->info);
d->result.d_name[0] = '\0';
} else {
mg_free(d);
d = NULL;
SetLastError(ERROR_BUFFER_OVERFLOW);
}
} else { } else {
mg_free(d); mg_free(d);
d = NULL; d = NULL;
@ -169,13 +177,17 @@ static void p_list(const char *dir, void (*fn)(const char *, void *),
static void *p_open(const char *path, int flags) { static void *p_open(const char *path, int flags) {
#if MG_ARCH == MG_ARCH_WIN32 #if MG_ARCH == MG_ARCH_WIN32
const char *mode = flags == MG_FS_READ ? "rb" : "a+b"; const char *mode = flags == MG_FS_READ ? "rb"
: (flags & MG_FS_EXCL) ? "wxb"
: "a+b";
wchar_t b1[MG_PATH_MAX], b2[10]; wchar_t b1[MG_PATH_MAX], b2[10];
MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0])); MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0])); MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
return (void *) _wfopen(b1, b2); return (void *) _wfopen(b1, b2);
#else #else
const char *mode = flags == MG_FS_READ ? "rbe" : "a+be"; // e for CLOEXEC const char *mode = flags == MG_FS_READ ? "rbe"
: (flags & MG_FS_EXCL) ? "wxbe"
: "a+be"; // e for CLOSEXEC
return (void *) fopen(path, mode); return (void *) fopen(path, mode);
#endif #endif
} }

View file

@ -65,14 +65,14 @@ size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0); if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
// Skip boundary // Skip boundary
while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++; while (b + 2 < max && !(s[b] == '\r' && s[b + 1] == '\n')) b++;
if (b <= ofs || b + 2 >= max) return 0; if (b <= ofs || b + 2 >= max) return 0;
// MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s)); // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
// Skip headers // Skip headers
h1 = h2 = b + 2; h1 = h2 = b + 2;
for (;;) { for (;;) {
while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++; while (h2 + 2 < max && !(s[h2] == '\r' && s[h2 + 1] == '\n')) h2++;
if (h2 == h1) break; if (h2 == h1) break;
if (h2 + 2 >= max) return 0; if (h2 + 2 >= max) return 0;
// MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1])); // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
@ -320,7 +320,10 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
if (!mg_http_parse_headers(s, end, hm->headers, if (!mg_http_parse_headers(s, end, hm->headers,
sizeof(hm->headers) / sizeof(hm->headers[0]))) sizeof(hm->headers) / sizeof(hm->headers[0])))
return -1; // error when parsing return -1; // error when parsing
if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) { cl = mg_http_get_header(hm, "Content-Length");
if (cl != NULL && mg_http_get_header(hm, "Transfer-Encoding") != NULL)
return -1; // cannot contain both CL and TE
if (cl != NULL) {
if (mg_to_size_t(*cl, &hm->body.len) == false) return -1; if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
hm->message.len = (size_t) req_len + hm->body.len; hm->message.len = (size_t) req_len + hm->body.len;
} }
@ -676,6 +679,24 @@ struct printdirentrydata {
}; };
#if MG_ENABLE_DIRLIST #if MG_ENABLE_DIRLIST
// Print file name, escaping HTML chars
static size_t html_esc(void (*fn)(char, void *), void *arg, va_list *ap) {
const char *s = va_arg(*ap, const char *);
size_t i, len = 0;
for (i = 0; s[i] != '\0'; i++) {
if (s[i] == '<') {
len += mg_xprintf(fn, arg, "%s", "&lt;");
} else if (s[i] == '>') {
len += mg_xprintf(fn, arg, "%s", "&gt;");
} else if (s[i] == '&') {
len += mg_xprintf(fn, arg, "%s", "&amp;");
} else {
len += mg_xprintf(fn, arg, "%c", s[i]);
}
}
return len;
}
static void printdirentry(const char *name, void *userdata) { static void printdirentry(const char *name, void *userdata) {
struct printdirentrydata *d = (struct printdirentrydata *) userdata; struct printdirentrydata *d = (struct printdirentrydata *) userdata;
struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
@ -709,9 +730,9 @@ static void printdirentry(const char *name, void *userdata) {
#endif #endif
n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
mg_printf(d->c, mg_printf(d->c,
" <tr><td><a href=\"%.*s%s\">%s%s</a></td>" " <tr><td><a href=\"%.*s%s\">%M%s</a></td>"
"<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n", "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
n, path, slash, name, slash, (unsigned long) t, mod, n, path, slash, html_esc, name, slash, (unsigned long) t, mod,
flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
} }
} }
@ -756,22 +777,21 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
opts->extra_headers == NULL ? "" : opts->extra_headers); opts->extra_headers == NULL ? "" : opts->extra_headers);
off = c->send.len; // Start of body off = c->send.len; // Start of body
mg_printf(c, mg_printf(c,
"<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s" "<!DOCTYPE html><html><head><title>Index of %M</title>%s%s"
"<style>th,td {text-align: left; padding-right: 1em; " "<style>th,td {text-align: left; padding-right: 1em; "
"font-family: monospace; }</style></head>" "font-family: monospace; }</style></head>"
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>" "<body><h1>Index of %M</h1><table cellpadding=\"0\"><thead>"
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>" "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
"<a href=\"#\" rel=\"1\">Modified</a></th>" "<a href=\"#\" rel=\"1\">Modified</a></th>"
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>" "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>" "<tr><td colspan=\"3\"><hr></td></tr>"
"</thead>" "</thead>"
"<tbody id=\"tb\">\n", "<tbody id=\"tb\">\n",
(int) uri.len, uri.buf, sort_js_code, sort_js_code2, (int) uri.len, mg_print_html_esc, (int) uri.len, uri.buf, sort_js_code, sort_js_code2,
uri.buf); mg_print_html_esc, (int) uri.len, uri.buf);
mg_printf(c, "%s", mg_printf(c, "%s",
" <tr><td><a href=\"..\">..</a></td>" " <tr><td><a href=\"..\">..</a></td>"
"<td name=-1></td><td name=-1>[DIR]</td></tr>\n"); "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
fs->ls(dir, printdirentry, &d); fs->ls(dir, printdirentry, &d);
mg_printf(c, mg_printf(c,
"</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>" "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
@ -941,7 +961,8 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
p++; p++;
// MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.buf, (int) v.len, // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.buf, (int) v.len,
// v.buf, (int) (p - b), b)); // v.buf, (int) (p - b), b));
return stripquotes(mg_str_n(b, (size_t) (p - b + q))); return stripquotes(mg_str_n(b,
(size_t) (p - b + (q && p < x && *p == '"' ? 1 : 0))));
} }
} }
return mg_str_n(NULL, 0); return mg_str_n(NULL, 0);
@ -1098,7 +1119,7 @@ static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
while (i < len && is_hex_digit(buf[i])) i++; while (i < len && is_hex_digit(buf[i])) i++;
if (i == 0) return -1; // Error, no length specified if (i == 0) return -1; // Error, no length specified
if (i > (int) sizeof(int) * 2) return -1; // Chunk length is too big 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 (len < i + 2 || 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) if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(int)) == false)
return -1; // Decode chunk length, overflow return -1; // Decode chunk length, overflow
if (n < 0) return -1; // Error. TODO(): some checks now redundant if (n < 0) return -1; // Error. TODO(): some checks now redundant
@ -1142,7 +1163,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf); hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
} }
is_http_1_0 = is_http_1_0 =
hm.proto.len > 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0; hm.proto.len == 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0;
// HTTP/1.0 does not use "Transfer-Encoding: chunked" // HTTP/1.0 does not use "Transfer-Encoding: chunked"
if (!is_http_1_0 && if (!is_http_1_0 &&
(te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) { (te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {

View file

@ -51,7 +51,7 @@ void mg_l2_eth_init(struct mg_tcpip_if *ifp) {
MG_INFO( MG_INFO(
("MAC not set. Generated random: %M", mg_print_mac, l2addr->addr.mac)); ("MAC not set. Generated random: %M", mg_print_mac, l2addr->addr.mac));
} }
ifp->mtu = 1500; ifp->l2mtu = 1500;
ifp->framesize = 1540; ifp->framesize = 1540;
} }

View file

@ -100,7 +100,7 @@ static uint8_t s_state = MG_PPPoE_ST_DISC;
static uint16_t s_id; static uint16_t s_id;
void mg_l2_ppp_init(struct mg_tcpip_if *ifp) { void mg_l2_ppp_init(struct mg_tcpip_if *ifp) {
ifp->mtu = 1500; ifp->l2mtu = 1500;
ifp->framesize = 1500 + sizeof(struct ppp) + sizeof(struct hdlc_); ifp->framesize = 1500 + sizeof(struct ppp) + sizeof(struct hdlc_);
} }
@ -108,7 +108,7 @@ extern void mg_l2_eth_init(struct mg_tcpip_if *);
void mg_l2_pppoe_init(struct mg_tcpip_if *ifp) { void mg_l2_pppoe_init(struct mg_tcpip_if *ifp) {
mg_l2_eth_init(ifp); mg_l2_eth_init(ifp);
ifp->mtu = ifp->mtu - (uint16_t) (sizeof(struct pppoe) + ifp->l2mtu = ifp->l2mtu - (uint16_t) (sizeof(struct pppoe) +
sizeof(struct ppp)); // 1500 --> 1492 sizeof(struct ppp)); // 1500 --> 1492
} }
@ -277,8 +277,8 @@ static void ppp_handle_lcp(struct mg_tcpip_if *ifp, uint8_t *lcpp,
static bool find_opt(const uint8_t opt, const uint8_t optlen, static bool find_opt(const uint8_t opt, const uint8_t optlen,
const uint8_t *opts, size_t optslen, uint8_t *dest) { const uint8_t *opts, size_t optslen, uint8_t *dest) {
uint8_t *p = (uint8_t *) opts; uint8_t *p = (uint8_t *) opts;
while (optslen >= 2) { // parse options for requested one while (optslen >= 2) { // parse options for requested one
if (p[1] > optslen) return false; // truncated / malformed if (p[1] > optslen || p[1] < 2) return false; // truncated / malformed
if (p[0] == opt && p[1] == optlen) { if (p[0] == opt && p[1] == optlen) {
memcpy(dest, p + 2, optlen - 2); memcpy(dest, p + 2, optlen - 2);
return true; return true;
@ -299,6 +299,7 @@ static void ppp_handle_ipcp(struct mg_tcpip_if *ifp, uint8_t *ipcpp,
if (ipcpsz < sizeof(*ipcp)) return; if (ipcpsz < sizeof(*ipcp)) return;
id = ipcp->id; id = ipcp->id;
len = mg_ntohs(ipcp->len); len = mg_ntohs(ipcp->len);
if (len > ipcpsz) return;
switch (ipcp->code) { switch (ipcp->code) {
case MG_PPP_IPCP_CFG_REQ: case MG_PPP_IPCP_CFG_REQ:
MG_VERBOSE(("got IPCP config request, acknowledging...")); MG_VERBOSE(("got IPCP config request, acknowledging..."));
@ -449,8 +450,9 @@ static bool ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
size_t msglen; size_t msglen;
MG_DEBUG(("unknown %u-byte PPP frame with proto 0x%04x:", MG_DEBUG(("unknown %u-byte PPP frame with proto 0x%04x:",
pay->len + sizeof(*ppp), mg_ntohs(ppp->proto))); pay->len + sizeof(*ppp), mg_ntohs(ppp->proto)));
if (mg_log_level >= MG_LL_DEBUG) mg_hexdump(ppp, sizeof(*ppp) + 20); if (mg_log_level >= MG_LL_DEBUG)
if (!s_lcpup) return false; // RFC-1661 5.7: must reject on link up mg_hexdump(ppp, pay->len > 14 ? 16 : pay->len + sizeof(*ppp));
if (!s_lcpup) return false; // RFC-1661 5.7: must reject on link down
if (pay->len > (size_t) (ifp->mtu - 20)) if (pay->len > (size_t) (ifp->mtu - 20))
pay->len = (size_t) (ifp->mtu - 20); // truncate to some safe limit pay->len = (size_t) (ifp->mtu - 20); // truncate to some safe limit
rej.code = MG_PPP_LCP_REJECT; rej.code = MG_PPP_LCP_REJECT;

View file

@ -198,12 +198,15 @@ static bool mg_send_mqtt_properties(struct mg_connection *c,
size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop, size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
size_t ofs) { size_t ofs) {
uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs; uint8_t *props = (uint8_t *) msg->dgram.buf + msg->props_start;
uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len; uint8_t *props_end = props + msg->props_size;
uint8_t *i = props + ofs;
size_t new_pos = ofs, len; size_t new_pos = ofs, len;
if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size || (i + 1) >= end) if (msg->props_start > msg->dgram.len ||
return 0; msg->props_size > msg->dgram.len - msg->props_start ||
ofs >= msg->props_size)
return 0;
memset(prop, 0, sizeof(struct mg_mqtt_prop)); memset(prop, 0, sizeof(struct mg_mqtt_prop));
prop->id = i[0]; prop->id = i[0];
@ -211,49 +214,50 @@ size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
switch (mqtt_prop_type_by_id(prop->id)) { switch (mqtt_prop_type_by_id(prop->id)) {
case MQTT_PROP_TYPE_STRING_PAIR: case MQTT_PROP_TYPE_STRING_PAIR:
if (i + 2 >= end) return 0; if (i + 2 > props_end) return 0;
prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
if (i + 2 + prop->key.len > props_end) return 0;
prop->key.buf = (char *) i + 2; prop->key.buf = (char *) i + 2;
i += 2 + prop->key.len; i += 2 + prop->key.len;
if (i + 2 >= end) return 0; if (i + 2 > props_end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0; if (i + 2 + prop->val.len > props_end) return 0;
new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len;
break; break;
case MQTT_PROP_TYPE_BYTE: case MQTT_PROP_TYPE_BYTE:
if (i + 1 >= end) return 0; if (i + 1 > props_end) return 0;
prop->iv = (uint8_t) i[0]; prop->iv = (uint8_t) i[0];
new_pos++; new_pos++;
break; break;
case MQTT_PROP_TYPE_SHORT: case MQTT_PROP_TYPE_SHORT:
if (i + 2 >= end) return 0; if (i + 2 > props_end) return 0;
prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
new_pos += sizeof(uint16_t); new_pos += sizeof(uint16_t);
break; break;
case MQTT_PROP_TYPE_INT: case MQTT_PROP_TYPE_INT:
if (i + 4 >= end) return 0; if (i + 4 > props_end) return 0;
prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) | prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) |
((uint32_t) i[2] << 8) | i[3]; ((uint32_t) i[2] << 8) | i[3];
new_pos += sizeof(uint32_t); new_pos += sizeof(uint32_t);
break; break;
case MQTT_PROP_TYPE_STRING: case MQTT_PROP_TYPE_STRING:
if (i + 2 >= end) return 0; if (i + 2 > props_end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0; if (i + 2 + prop->val.len > props_end) return 0;
new_pos += 2 + prop->val.len; new_pos += 2 + prop->val.len;
break; break;
case MQTT_PROP_TYPE_BINARY_DATA: case MQTT_PROP_TYPE_BINARY_DATA:
if (i + 2 >= end) return 0; if (i + 2 > props_end) return 0;
prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
prop->val.buf = (char *) i + 2; prop->val.buf = (char *) i + 2;
if (i + 2 + prop->val.len >= end) return 0; if (i + 2 + prop->val.len > props_end) return 0;
new_pos += 2 + prop->val.len; new_pos += 2 + prop->val.len;
break; break;
case MQTT_PROP_TYPE_VARIABLE_INT: case MQTT_PROP_TYPE_VARIABLE_INT:
len = decode_varint(i, (size_t) (end - i), &prop->iv); len = decode_varint(i, (size_t) (props_end - i), &prop->iv);
if (i + len >= end) return 0; if (i + len > props_end) return 0;
new_pos = (len == 0) ? 0 : new_pos + len; new_pos = (len == 0) ? 0 : new_pos + len;
break; break;
default: default:

View file

@ -630,7 +630,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
size_t plen = pkt->pay.len; size_t plen = pkt->pay.len;
if (!icmpcsum_ok(pkt->icmp, sizeof(struct icmp) + plen)) return; if (!icmpcsum_ok(pkt->icmp, sizeof(struct icmp) + plen)) return;
if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
size_t l2_max_overhead = ifp->framesize - ifp->mtu; size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
size_t hlen = sizeof(struct ip) + sizeof(struct icmp); size_t hlen = sizeof(struct ip) + sizeof(struct icmp);
size_t room = ifp->tx.len - hlen - l2_max_overhead; size_t room = ifp->tx.len - hlen - l2_max_overhead;
uint8_t *l2addr; uint8_t *l2addr;
@ -662,9 +662,9 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0, owner = 0; uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0, owner = 0;
uint8_t msgtype = 0, state = ifp->state; uint8_t msgtype = 0, state = ifp->state;
// perform size check first, then access fields // perform size check first, then access fields
uint8_t *p = pkt->dhcp->options, uint8_t *p = (uint8_t *) pkt->pay.buf,
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len]; *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
if (end < p) return; // options are optional, check min header length // min header length checked at payload calculation, options are optional
if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return; if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
while (p + 1 < end && p[0] != 255) { // Parse options, get #1; RFC-2132 9 while (p + 1 < end && p[0] != 255) { // Parse options, get #1; RFC-2132 9
if (p[0] == 1 && p[1] == 4 && p + 6 < end) { // Mask, 3.3 if (p[0] == 1 && p[1] == 4 && p + 6 < end) { // Mask, 3.3
@ -704,7 +704,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
} else if (msgtype == 5) { // DHCPACK } else if (msgtype == 5) { // DHCPACK
if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
uint64_t rand; uint64_t rand;
ifp->lease_expire = ifp->now + lease * 1000; ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
// assume DHCP server = router until ARP resolves // assume DHCP server = router until ARP resolves
memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac)); memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac));
@ -721,7 +721,7 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (ifp->enable_req_sntp && sntp != 0) if (ifp->enable_req_sntp && sntp != 0)
mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp); mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp);
} else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew
ifp->lease_expire = ifp->now + lease * 1000; ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 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) } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4)
} }
@ -731,11 +731,11 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
// Simple DHCP server that assigns a next IP address: ifp->ip + 1 // 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) { static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint8_t *mac; uint8_t *mac;
uint8_t op = 0, *p = pkt->dhcp->options, uint8_t op = 0, *p = (uint8_t *) pkt->pay.buf,
*end = (uint8_t *) &pkt->pay.buf[pkt->pay.len]; *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
// NOTE(): assumes Ethernet: htype=1 hlen=6, copy 6 bytes // NOTE(): assumes Ethernet: htype=1 hlen=6, copy 6 bytes
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
if (end < p) return; // options are optional, check min header length // min header length checked at payload calculation, options are optional
res.yiaddr = ifp->ip; res.yiaddr = ifp->ip;
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
while (p + 1 < end && p[0] != 255) { // Parse options while (p + 1 < end && p[0] != 255) { // Parse options
@ -827,14 +827,16 @@ static void tx_ndp_na(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
static void onstate6change(struct mg_tcpip_if *ifp); static void onstate6change(struct mg_tcpip_if *ifp);
static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct ndp_na *na = (struct ndp_na *) (pkt->icmp6 + 1); struct ndp_na *na = (struct ndp_na *) pkt->pay.buf;
uint8_t *opts = (uint8_t *) (na + 1); uint8_t *opts = (uint8_t *) (na + 1), *endp = opts + pkt->pay.len - sizeof(*na);
if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts
if ((na->res[0] & 0x40) == 0) return; // not "solicited" if ((na->res[0] & 0x40) == 0) return; // not "solicited"
if (*opts++ != 2) return; // no target hwaddr if (*opts++ != 2) return; // no target hwaddr, must have
MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr)); MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr));
if (MG_IP6MATCH(na->addr, ifp->gw6)) { if (MG_IP6MATCH(na->addr, ifp->gw6)) {
// Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY // Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
uint8_t len = *opts++; // check valid hwaddr and get it uint8_t len = *opts++; // check valid hwaddr and get it
if ((opts + 8 * len - 2) > endp) return; // truncated
if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return; if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return;
ifp->gw6_ready = true; ifp->gw6_ready = true;
if (ifp->state6 == MG_TCPIP_STATE_IP) { if (ifp->state6 == MG_TCPIP_STATE_IP) {
@ -846,6 +848,7 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (c != NULL && c->is_arplooking) { if (c != NULL && c->is_arplooking) {
struct connstate *s = (struct connstate *) (c + 1); struct connstate *s = (struct connstate *) (c + 1);
uint8_t len = *opts++; // check valid hwaddr and get it uint8_t len = *opts++; // check valid hwaddr and get it
if ((opts + 8 * len - 2) > endp) return; // truncated
if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return; if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return;
MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6, MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6,
&c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac)); &c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac));
@ -857,15 +860,17 @@ static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
// Neighbor Solicitation, 4.3 // Neighbor Solicitation, 4.3
static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na
uint64_t target[2]; uint64_t target[2];
if (pkt->pay.len < sizeof(target)) return; if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts
memcpy(target, pkt->pay.buf + 4, sizeof(target)); memcpy(target, ns->addr, sizeof(target));
if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) { if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
uint64_t req[2]; // requester address uint64_t req[2]; // requester address
uint8_t l2[sizeof(struct mg_l2addr)]; uint8_t l2[sizeof(struct mg_l2addr)];
uint8_t len, *opts = (uint8_t *) pkt->pay.buf + 20; uint8_t len, *opts = (uint8_t *) (ns + 1), *endp = opts + pkt->pay.len - sizeof(*ns);
if (*opts++ != 1) return; // no requester hwaddr (source) if (*opts++ != 1) return; // no requester hwaddr (source)
len = *opts++; // check valid hwaddr and get it len = *opts++; // check valid hwaddr and get it
if ((opts + 8 * len - 2) > endp) return; // truncated
if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return; if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return;
req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit
tx_ndp_na(ifp, l2, target, req, true, ifp->mac); tx_ndp_na(ifp, l2, target, req, true, ifp->mac);
@ -961,15 +966,15 @@ static bool fill_global(struct mg_tcpip_if *ifp, uint8_t *prefix,
// Router Advertisement, 4.2 // Router Advertisement, 4.2
static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (pkt->pay.len < 12) return; struct ndp_ra *ra = (struct ndp_ra *) pkt->pay.buf;
struct ndp_ra *ra = (struct ndp_ra *) (pkt->icmp6 + 1);
uint8_t *opts = (uint8_t *) (ra + 1); uint8_t *opts = (uint8_t *) (ra + 1);
size_t opt_left = pkt->pay.len - 12; size_t opt_left = pkt->pay.len - sizeof(*ra);
bool gotl2addr = false, gotprefix = false, changed = false; bool gotl2addr = false, gotprefix = false, changed = false;
uint8_t l2[sizeof(struct mg_l2addr)]; uint8_t l2[sizeof(struct mg_l2addr)];
uint32_t mtu = 0; uint32_t mtu = 0;
uint8_t *prefix, prefix_len; uint8_t *prefix, prefix_len;
if (pkt->pay.len < sizeof(*ra)) return;
if (ifp->state6 == MG_TCPIP_STATE_UP) { if (ifp->state6 == MG_TCPIP_STATE_UP) {
MG_DEBUG(("Received NDP RA")); // fill gw6 address MG_DEBUG(("Received NDP RA")); // fill gw6 address
// parse options // parse options
@ -982,8 +987,9 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (!mg_l2_ip6get(ifp->l2type, l2, opts + 2, len)) break; if (!mg_l2_ip6get(ifp->l2type, l2, opts + 2, len)) break;
gotl2addr = true; gotl2addr = true;
} else if (type == 5 && length >= 8) { } else if (type == 5 && length >= 8) {
// process MTU if available // process MTU if available, ignore if it smells
mtu = MG_LOAD_BE32(opts + 4); mtu = MG_LOAD_BE32(opts + 4);
if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU
} else if (type == 3 && length >= 32) { } else if (type == 3 && length >= 32) {
// process prefix, 4.6.2 // process prefix, 4.6.2
uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40 uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40
@ -1037,7 +1043,7 @@ static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint64_t target[2]; uint64_t target[2];
target[0] = pkt->ip6->dst[0], target[1] = pkt->ip6->dst[1]; target[0] = pkt->ip6->dst[0], target[1] = pkt->ip6->dst[1];
if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) { if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
size_t l2_max_overhead = ifp->framesize - ifp->mtu; size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
size_t hlen = sizeof(struct ip6) + sizeof(struct icmp6); size_t hlen = sizeof(struct ip6) + sizeof(struct icmp6);
size_t room = ifp->tx.len - hlen - l2_max_overhead, plen = pkt->pay.len; size_t room = ifp->tx.len - hlen - l2_max_overhead, plen = pkt->pay.len;
struct mg_addr ips; struct mg_addr ips;
@ -1328,7 +1334,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
static size_t trim_len(struct mg_connection *c, size_t len) { static size_t trim_len(struct mg_connection *c, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp; struct mg_tcpip_if *ifp = c->mgr->ifp;
size_t l2_max_overhead = ifp->framesize - ifp->mtu; size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
size_t ip_max_h_len = c->rem.is_ip6 ? 40 : 24; // we don't send options size_t ip_max_h_len = c->rem.is_ip6 ? 40 : 24; // we don't send options
size_t tcp_max_h_len = 60 /* RFC-9293 3.7.1; RFC-6691 2 */, udp_h_len = 8; size_t tcp_max_h_len = 60 /* RFC-9293 3.7.1; RFC-6691 2 */, udp_h_len = 8;
size_t max_headers_len = size_t max_headers_len =
@ -1715,11 +1721,13 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, 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->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68)) { if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
len >= offsetof(struct dhcp, options)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1); pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, &pkt->dhcp->options); mkpay(pkt, &pkt->dhcp->options);
rx_dhcp_client(ifp, pkt); rx_dhcp_client(ifp, pkt);
} else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) { } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67) &&
len >= offsetof(struct dhcp, options)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1); pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, &pkt->dhcp->options); mkpay(pkt, &pkt->dhcp->options);
rx_dhcp_server(ifp, pkt); rx_dhcp_server(ifp, pkt);
@ -1757,19 +1765,24 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
next = pkt->ip6->next; next = pkt->ip6->next;
nhdr = (uint8_t *) (pkt->ip6 + 1); nhdr = (uint8_t *) (pkt->ip6 + 1);
while (loop) { while (loop) {
uint16_t hlen;
switch (next) { switch (next) {
case 0: // Hop-by-Hop 4.3 case 0: // Hop-by-Hop 4.3
case 43: // Routing 4.4 case 43: // Routing 4.4
case 60: // Destination Options 4.6 case 60: // Destination Options 4.6
case 51: // Authentication RFC-4302 case 51: // Authentication RFC-4302
MG_INFO(("IPv6 extension header %d", (int) next)); MG_INFO(("IPv6 extension header %d", (int) next));
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
next = nhdr[0]; next = nhdr[0];
len += (uint16_t) (8 * (nhdr[1] + 1)); hlen = (uint16_t) (8 * (nhdr[1] + 1));
nhdr += 8 * (nhdr[1] + 1); if (((uint32_t) len + hlen) > plen) return; // malformed
len += hlen;
nhdr += hlen;
break; break;
case 44: // Fragment 4.5 case 44: // Fragment 4.5
{ {
struct mg_connection *c; struct mg_connection *c;
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf); if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf); if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
c = getpeer(ifp->mgr, pkt, false); c = getpeer(ifp->mgr, pkt, false);
@ -1784,7 +1797,6 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
break; break;
} }
} }
if (len >= plen) return;
// There can be link padding, take payload length from IPv6 header - options // There can be link padding, take payload length from IPv6 header - options
pkt->pay.buf = (char *) nhdr; pkt->pay.buf = (char *) nhdr;
pkt->pay.len = plen - len; pkt->pay.len = plen - len;
@ -2062,6 +2074,7 @@ void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
// If L2 address is not set, make a random one; fill MTU // If L2 address is not set, make a random one; fill MTU
mg_l2_init(ifp); mg_l2_init(ifp);
ifp->mtu = ifp->l2mtu;
if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip" if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip"
memcpy(ifp->dhcp_name, "mip", 4); memcpy(ifp->dhcp_name, "mip", 4);

View file

@ -70,6 +70,7 @@ struct mg_tcpip_if {
char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Hostname sent in DHCP requests; defaults to "mip" char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Hostname sent in DHCP requests; defaults to "mip"
uint16_t mtu; // IP MTU (max payload size at the IP layer) uint16_t mtu; // IP MTU (max payload size at the IP layer)
uint16_t framesize; // Maximum L2 frame size in bytes uint16_t framesize; // Maximum L2 frame size in bytes
uint16_t l2mtu; // L2 frame payload, default net MTU
#if MG_ENABLE_IPV6 #if MG_ENABLE_IPV6
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses

View file

@ -209,3 +209,31 @@ size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) {
if (len == 0) len = p == NULL ? 0 : strlen(p); if (len == 0) len = p == NULL ? 0 : strlen(p);
return qcpy(out, arg, p, len); return qcpy(out, arg, p, len);
} }
size_t mg_print_html_esc(void (*out)(char, void *), void *arg, va_list *ap) {
size_t i, n = 0;
int len = va_arg(*ap, int);
const char *s = va_arg(*ap, const char *);
for (i = 0; i < (size_t) len; i++) {
const char *esc = NULL;
switch (s[i]) {
// clang-format off
case '&': esc = "&amp;"; break;
case '<': esc = "&lt;"; break;
case '>': esc = "&gt;"; break;
case '"': esc = "&quot;"; break;
default: break;
// clang-format on
}
if (esc != NULL) {
while (*esc != '\0') {
out(*esc++, arg);
n++;
}
} else {
out(s[i], arg);
n++;
}
}
return n;
}

View file

@ -35,7 +35,7 @@ size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
// Built-in %M/%m printer functions. Each reads its argument(s) from ap. // Built-in %M/%m printer functions. Each reads its argument(s) from ap.
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int quote, const char *str -- use MG_ESC() size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: const void *buf, size_t len
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr * size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr * size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
@ -44,6 +44,8 @@ size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint64_t
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
// Output functions for use as the fn argument to mg_xprintf/mg_vxprintf. // Output functions for use as the fn argument to mg_xprintf/mg_vxprintf.
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed) void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed)

View file

@ -112,9 +112,9 @@ void mg_getlocaddr(struct mg_connection *c, struct mg_addr *to,
slen = tousa(to, &usa); slen = tousa(to, &usa);
if ((rc = connect(fd, &usa.sa, slen)) != 0) { if ((rc = connect(fd, &usa.sa, slen)) != 0) {
mg_error(c, "connect: %d", MG_SOCK_ERR(rc)); mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
return; } else {
setlocaddr(fd, addr);
} }
setlocaddr(fd, addr);
closesocket(fd); closesocket(fd);
} }

View file

@ -21,7 +21,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
size_t len = 0; size_t len = 0;
buf[0] = arg[0] = '\0'; buf[0] = arg[0] = '\0';
while ((ch = fgetc(fp)) != EOF) { while ((ch = fgetc(fp)) != EOF) {
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { if (intag && ch == '>' && len >= 2 && buf[len - 1] == '-' && buf[len - 2] == '-') {
buf[len++] = (char) (ch & 0xff); buf[len++] = (char) (ch & 0xff);
buf[len] = '\0'; buf[len] = '\0';
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg) > 0) { if (sscanf(buf, "<!--#include file=\"%[^\"]", arg) > 0) {
@ -29,7 +29,9 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
*p = (char *) path + strlen(path), *data; *p = (char *) path + strlen(path), *data;
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--; while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg); mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
if (depth < MG_MAX_SSI_DEPTH && if (!mg_path_is_sane(mg_str(tmp))) {
MG_ERROR(("SSI include path traversal blocked: %s", arg));
} else if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) { (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
size_t datalen = strlen(data); size_t datalen = strlen(data);
size_t ret = mg_iobuf_add(&b, b.len, data, datalen); size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
@ -41,7 +43,9 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) { } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) {
char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data; char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg); mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
if (depth < MG_MAX_SSI_DEPTH && if (!mg_path_is_sane(mg_str(tmp))) {
MG_ERROR(("SSI include path traversal blocked: %s", arg));
} else if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) { (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
size_t datalen = strlen(data); size_t datalen = strlen(data);
size_t ret = mg_iobuf_add(&b, b.len, data, datalen); size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
@ -89,6 +93,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
fail: fail:
fclose(fp); fclose(fp);
mg_iobuf_free(&b);
return NULL; return NULL;
} }

View file

@ -1009,6 +1009,7 @@ int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
uint64_t orig_add_len = ctx->add_len * 8; uint64_t orig_add_len = ctx->add_len * 8;
size_t i; size_t i;
if (tag_len > sizeof(work_buf)) return -1;
if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len); if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
if (orig_len || orig_add_len) { if (orig_len || orig_add_len) {

View file

@ -12,6 +12,10 @@
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
#ifndef MG_MAX_TLSOID_DEPTH
#define MG_MAX_TLSOID_DEPTH 15
#endif
// PKCS#8 algorithm OIDs // PKCS#8 algorithm OIDs
static const uint8_t mg_rsa_oid[] = { static const uint8_t mg_rsa_oid[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x2a, 0x86, 0x48, 0x86, 0xf7,
@ -136,25 +140,25 @@ struct tls_data {
#include <stdio.h> #include <stdio.h>
static void mg_ssl_key_log(const char *label, uint8_t client_random[32], static void mg_ssl_key_log(const char *label, uint8_t client_random[32],
uint8_t *secret, size_t secretsz) { uint8_t *secret, size_t secretsz) {
FILE *f;
char *keylogfile = getenv("SSLKEYLOGFILE"); char *keylogfile = getenv("SSLKEYLOGFILE");
size_t i; if (keylogfile == NULL) return;
if (keylogfile != NULL) { MG_DEBUG(("Dumping key log into %s", keylogfile));
MG_DEBUG(("Dumping key log into %s", keylogfile)); f = fopen(keylogfile, "a");
FILE *f = fopen(keylogfile, "a"); if (f != NULL) {
if (f != NULL) { size_t i;
fprintf(f, "%s ", label); fprintf(f, "%s ", label);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
fprintf(f, "%02x", client_random[i]); fprintf(f, "%02x", client_random[i]);
}
fprintf(f, " ");
for (i = 0; i < secretsz; i++) {
fprintf(f, "%02x", secret[i]);
}
fprintf(f, "\n");
fclose(f);
} else {
MG_ERROR(("Cannot open %s", keylogfile));
} }
fprintf(f, " ");
for (i = 0; i < secretsz; i++) {
fprintf(f, "%02x", secret[i]);
}
fprintf(f, "\n");
fclose(f);
} else {
MG_ERROR(("Cannot open %s", keylogfile));
} }
} }
#endif #endif
@ -178,24 +182,34 @@ struct mg_der_tlv {
uint8_t *value; uint8_t *value;
}; };
static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { // parse DER into a TLV record
size_t header_len = 2; static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
uint32_t len = dersz < 2 ? 0 : der[1]; uint32_t n = 0;
if (dersz < 2) return -1; // Invalid DER if (dersz < 2) return -1;
tlv->type = der[0]; tlv->type = der[0];
if (len > 0x7F) { // long-form length tlv->len = der[1];
uint8_t len_bytes = len & 0x7F, i; tlv->value = der + 2;
if (dersz < (size_t) (2 + len_bytes)) return -1; if (tlv->len > 0x7f) { // long-form length
len = 0; uint32_t i;
for (i = 0; i < len_bytes; i++) { n = tlv->len - 0x80;
len = (len << 8) | der[2 + i]; if (n > 4 || dersz < (2 + n)) return -1;
tlv->len = 0;
for (i = 0; i < n; i++) {
tlv->len = (tlv->len << 8) | der[2 + i];
} }
header_len += len_bytes; if (n > (dersz - 2)) return -1;
tlv->value += n;
} else {
if (der + dersz < tlv->value + tlv->len) return -1;
} }
if (dersz < header_len + len) return -1; return (int) n;
tlv->len = len; }
tlv->value = der + header_len;
return (int) (header_len + len); static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
int n = mg_der_to_tlv(der, dersz, tlv);
if (n < 0) return -1;
if (tlv->len >= (uint32_t)((unsigned) -6)) return -1; // avoid overflow
return 2 + n + (int) tlv->len; // 2: type, len; n = long form len bytes
} }
static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) { static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) {
@ -208,22 +222,34 @@ static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) {
return 1; return 1;
} }
static int mg_der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid, static int der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid, size_t oid_len, struct mg_der_tlv *found, int depth) {
size_t oid_len, struct mg_der_tlv *found) {
struct mg_der_tlv parent, child; struct mg_der_tlv parent, child;
int r;
parent = *tlv; parent = *tlv;
while (mg_der_next(&parent, &child) > 0) { while ((r = mg_der_next(&parent, &child)) > 0) {
if (child.type == 0x06 && child.len == oid_len && if (child.type == 0x06 && child.len == oid_len &&
memcmp(child.value, oid, oid_len) == 0) { memcmp(child.value, oid, oid_len) == 0) {
return mg_der_next(&parent, found); return mg_der_next(&parent, found);
} else if (child.type & 0x20) { } else if (child.type & 0x20) {
struct mg_der_tlv sub_parent = child; struct mg_der_tlv sub_parent = child;
if (mg_der_find_oid(&sub_parent, oid, oid_len, found)) return 1; if (depth >= MG_MAX_TLSOID_DEPTH) {
MG_ERROR(("too nested der"));
return -1;
}
if ((r = der_find_oid(&sub_parent, oid, oid_len, found, depth + 1)) > 0)
return 1;
if (r < 0) return -1; // exit on error; r = 0 => not found, keep parsing
} }
} }
if (r < 0) return -1;
return 0; return 0;
} }
static int mg_der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid,
size_t oid_len, struct mg_der_tlv *found) {
return der_find_oid(tlv, oid, oid_len, found, 0);
}
#if 0 #if 0
static void mg_der_debug(struct mg_der_tlv *tlv, int depth) { static void mg_der_debug(struct mg_der_tlv *tlv, int depth) {
MG_DEBUG(("> %.*sd=%d Type: 0x%02X, Length: %u\n", depth * 4, " ", depth, MG_DEBUG(("> %.*sd=%d Type: 0x%02X, Length: %u\n", depth * 4, " ", depth,
@ -239,28 +265,6 @@ static void mg_der_debug(struct mg_der_tlv *tlv, int depth) {
} }
#endif #endif
// 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;
}
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 (der + dersz < tlv->value + tlv->len) {
return -1;
}
return 0;
}
// Did we receive a full TLS record in the c->rtls buffer? // Did we receive a full TLS record in the c->rtls buffer?
static bool mg_tls_got_record(struct mg_connection *c) { static bool mg_tls_got_record(struct mg_connection *c) {
return c->rtls.len >= (size_t) TLS_RECHDR_SIZE && return c->rtls.len >= (size_t) TLS_RECHDR_SIZE &&
@ -667,12 +671,15 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
// store session_id // store session_id
session_id_len = rio->buf[43]; session_id_len = rio->buf[43];
if (session_id_len == sizeof(tls->session_id)) { if (session_id_len == sizeof(tls->session_id)) {
if (rio->len < (size_t)(46 + session_id_len)) goto fail; // 2: ciphers len
memmove(tls->session_id, rio->buf + 44, session_id_len); memmove(tls->session_id, rio->buf + 44, session_id_len);
} else if (session_id_len != 0) { } else if (session_id_len != 0) {
MG_INFO(("bad session id len")); MG_ERROR(("bad session id len"));
} goto fail;
} // session_id_len is either sizeof(tls->session_id) or 0
if (((uint32_t) 44 + 2 + session_id_len) > rio->len) goto fail;
cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len); cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len);
if (((uint32_t) cipher_suites_len + 46 + session_id_len) > rio->len) if (((uint32_t) cipher_suites_len + 46 + 2 + 2 + session_id_len) > rio->len)
goto fail; goto fail;
ext_len = MG_LOAD_BE16(rio->buf + 48 + session_id_len + cipher_suites_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; ext = rio->buf + 50 + session_id_len + cipher_suites_len;
@ -689,8 +696,7 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
} }
key_exchange_len = MG_LOAD_BE16(ext + j + 4); key_exchange_len = MG_LOAD_BE16(ext + j + 4);
key_exchange = ext + j + 6; key_exchange = ext + j + 6;
if (((size_t) key_exchange_len + if ((key_exchange + key_exchange_len) > ((uint8_t *) rio->buf + rio->len))
((size_t) key_exchange - (size_t) rio->buf)) > rio->len)
goto fail; goto fail;
for (k = 0; k < key_exchange_len;) { for (k = 0; k < key_exchange_len;) {
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
@ -745,8 +751,10 @@ static bool mg_tls_server_send_hello(struct mg_connection *c) {
uint8_t x25519_pub[X25519_BYTES]; uint8_t x25519_pub[X25519_BYTES];
uint8_t x25519_prv[X25519_BYTES]; uint8_t x25519_prv[X25519_BYTES];
if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG"); if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1); if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) {
mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1); mg_error(c, "bad key");
return false;
}
mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec));
// fill in the gaps: random + session ID + keyshare // fill in the gaps: random + session ID + keyshare
@ -1298,17 +1306,18 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
if (!mg_tls_got_record(c)) { if (!mg_tls_got_record(c)) {
return MG_IO_WAIT; return MG_IO_WAIT;
} }
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_SERVER_HELLO) { if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->len < 6 || rio->buf[5] != MG_TLS_SERVER_HELLO) {
if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) { if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) {
verbose_alert(&rio->buf[5]); verbose_alert(&rio->buf[5]);
mg_error(c, "TLS error alert"); mg_error(c, "TLS error alert");
return -1; return -1;
} }
MG_INFO(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5])); if (rio->len >= 6) MG_ERROR(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5]));
mg_error(c, "not a server hello packet"); mg_error(c, "not a server hello packet");
return -1; return -1;
} }
if (rio->len < 5 + 39 + 32 + 3 + 2) goto fail;
msgsz = MG_LOAD_BE16(rio->buf + 3); msgsz = MG_LOAD_BE16(rio->buf + 3);
mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz); mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
@ -1317,16 +1326,17 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
if (ext_len > (rio->len - (5 + 39 + 32 + 3 + 2))) goto fail; if (ext_len > (rio->len - (5 + 39 + 32 + 3 + 2))) goto fail;
for (j = 0; j < ext_len;) { for (j = 0; j < ext_len;) {
uint16_t ext_type = MG_LOAD_BE16(ext + j); uint16_t ext_type, ext_len2, group, key_exchange_len;
uint16_t ext_len2 = MG_LOAD_BE16(ext + j + 2);
uint16_t group;
uint8_t *key_exchange; uint8_t *key_exchange;
uint16_t key_exchange_len; if ((ext_len - j) < 4) goto fail;
ext_type = MG_LOAD_BE16(ext + j);
ext_len2 = MG_LOAD_BE16(ext + j + 2);
if (ext_len2 > (ext_len - j - 4)) goto fail; if (ext_len2 > (ext_len - j - 4)) goto fail;
if (ext_type != 0x0033) { // not a key share extension, ignore if (ext_type != 0x0033) { // not a key share extension, ignore
j += (uint16_t) (ext_len2 + 4); j += (uint16_t) (ext_len2 + 4);
continue; continue;
} }
if (ext_len2 < (2 + 2 + 32)) goto fail;
group = MG_LOAD_BE16(ext + j + 4); group = MG_LOAD_BE16(ext + j + 4);
if (group != 0x001d) { if (group != 0x001d) {
mg_error(c, "bad key exchange group"); mg_error(c, "bad key exchange group");
@ -1334,11 +1344,10 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) {
} }
key_exchange_len = MG_LOAD_BE16(ext + j + 6); key_exchange_len = MG_LOAD_BE16(ext + j + 6);
key_exchange = ext + j + 8; key_exchange = ext + j + 8;
if (key_exchange_len != 32) { if (key_exchange_len != 32 || mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < 0) {
mg_error(c, "bad key exchange length"); mg_error(c, "bad key");
return -1; 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_hexdump("c x25519 sec", tls->x25519_sec, 32);
mg_tls_drop_record(c); mg_tls_drop_record(c);
/* generate handshake keys */ /* generate handshake keys */
@ -1367,6 +1376,7 @@ static int mg_tls_client_recv_ext(struct mg_connection *c) {
struct mg_tls_cert { struct mg_tls_cert {
bool is_ec_pubkey; bool is_ec_pubkey;
bool is_ca;
struct mg_str sn; struct mg_str sn;
struct mg_str pubkey; struct mg_str pubkey;
struct mg_der_tlv issuer; struct mg_der_tlv issuer;
@ -1381,18 +1391,41 @@ static void mg_der_debug_cert_name(const char *name, struct mg_der_tlv *tlv) {
struct mg_str cn, c, o, ou; struct mg_str cn, c, o, ou;
if (mg_log_level < MG_LL_VERBOSE) return; // skip recursive computations if (mg_log_level < MG_LL_VERBOSE) return; // skip recursive computations
cn = c = o = ou = mg_str(""); cn = c = o = ou = mg_str("");
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x03", 3, &v)) if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x03", 3, &v) > 0)
cn = mg_str_n((const char *) v.value, v.len); cn = mg_str_n((const char *) v.value, v.len);
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x06", 3, &v)) if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x06", 3, &v) > 0)
c = mg_str_n((const char *) v.value, v.len); c = mg_str_n((const char *) v.value, v.len);
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0a", 3, &v)) if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0a", 3, &v) > 0)
o = mg_str_n((const char *) v.value, v.len); o = mg_str_n((const char *) v.value, v.len);
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0b", 3, &v)) if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0b", 3, &v) > 0)
ou = mg_str_n((const char *) v.value, v.len); ou = mg_str_n((const char *) v.value, v.len);
MG_VERBOSE(("%s: CN=%.*s, C=%.*s, O=%.*s, OU=%.*s", name, cn.len, cn.buf, MG_VERBOSE(("%s: CN=%.*s, C=%.*s, O=%.*s, OU=%.*s", name, cn.len, cn.buf,
c.len, c.buf, o.len, o.buf, ou.len, ou.buf)); c.len, c.buf, o.len, o.buf, ou.len, ou.buf));
} }
static uint64_t asnt2t(uint8_t *v, uint8_t type) {
unsigned int y, mo, d, h, mi, ss, ly;
uint16_t dm[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
y = 10U * (*v++ - '0'), y += (*v++ - '0');
if (type == 0x17) { // UTCTime, RFC-5280 4.1.2.5.1 YYMMDDHHMMSSZ
if (y >= 50) return (uint64_t) 0; // 19YY is in the past
y += 2000;
} else { // GeneralizedTime, RFC-5280 4.1.2.5.2 YYYYMMDDHHMMSSZ
y *= 100U, y += 10U * (*v++ - '0'), y += (*v++ - '0');
}
y -= 1900;
mo = 10U * (*v++ - '0'), mo += (*v++ - '0');
d = 10U * (*v++ - '0'), d += (*v++ - '0');
h = 10U * (*v++ - '0'), h += (*v++ - '0');
mi = 10U * (*v++ - '0'), mi += (*v++ - '0');
ss = 10U * (*v++ - '0'), ss += (*v++ - '0');
if (*v != 'Z') return 0; // invalid
ly = (mo > 2) ? y + 1 : y;
return (uint64_t) ss + 60U * mi + 3600U * h + 86400U * (dm[mo - 1] + d - 1) +
31536000U * (y - 70) + 86400U * ((ly - 69) / 4U) -
86400U * ((ly - 1) / 100U) + 86400U * ((ly + 299) / 400U);
}
static int mg_tls_parse_cert_der(void *buf, size_t dersz, static int mg_tls_parse_cert_der(void *buf, size_t dersz,
struct mg_tls_cert *cert) { struct mg_tls_cert *cert) {
uint8_t *tbs, *der = (uint8_t *) buf; uint8_t *tbs, *der = (uint8_t *) buf;
@ -1464,14 +1497,21 @@ static int mg_tls_parse_cert_der(void *buf, size_t dersz,
// validity dates (before/after) // validity dates (before/after)
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
if (1) { {
struct mg_der_tlv before, after; struct mg_der_tlv before, after;
mg_der_next(&field, &before); uint64_t now = mg_now() / 1000U, t;
mg_der_next(&field, &after); if (mg_der_next(&field, &before) <= 0 || ((before.type != 0x17 || before.len != 13) && (before.type != 0x18 || before.len != 15))) return -1;
if (after.len == 13 && memcmp(after.value, "250101000000Z", 13) < 0) { if (now < (t = asnt2t(before.value, before.type))) {
MG_ERROR(("invalid validity dates: before=%M after=%M", mg_print_hex, MG_ERROR(("cert is not yet valid: before=%.*s (%lu), now=%lu", before.len, before.value, t, now));
before.len, before.value, mg_print_hex, after.len, return -1;
after.value)); }
if (mg_der_next(&field, &after) <= 0 || ((after.type != 0x17 || after.len != 13) && (after.type != 0x18 || after.len != 15))) return -1;
if (memcmp(after.value, "99991231235959Z", 15) == 0) { // RFC-5280 4.1.2.5
MG_ERROR(("No well-defined expiration date"));
return -1;
}
if (now > (t = asnt2t(after.value, after.type))) {
MG_ERROR(("cert is no longer valid: after=%.*s (%lu), now=%lu", after.len, after.value, t, now));
return -1; return -1;
} }
} }
@ -1520,11 +1560,47 @@ static int mg_tls_parse_cert_der(void *buf, size_t dersz,
if (mg_der_next(&field, &pki_key) <= 0 || pki_key.type != 0x03) return -1; if (mg_der_next(&field, &pki_key) <= 0 || pki_key.type != 0x03) return -1;
if (cert->is_ec_pubkey) { // Skip leading 0x00 and 0x04 (=uncompressed) if (cert->is_ec_pubkey) { // Skip leading 0x00 and 0x04 (=uncompressed)
if (pki_key.len < 2) return -1;
cert->pubkey = mg_str_n((char *) pki_key.value + 2, pki_key.len - 2); cert->pubkey = mg_str_n((char *) pki_key.value + 2, pki_key.len - 2);
} else { // Skip leading 0x00 byte } else { // Skip leading 0x00 byte
if (pki_key.len < 1) return -1;
cert->pubkey = mg_str_n((char *) pki_key.value + 1, pki_key.len - 1); cert->pubkey = mg_str_n((char *) pki_key.value + 1, pki_key.len - 1);
} }
{ // parse optional fields
int r; // unique ids
while ((r = mg_der_next(&tbs_cert, &field)) > 0 && field.type == 0xa1);
if (r > 0 && field.type == 0xa3) { // extensions
bool ca = false, certsign = true;
struct mg_der_tlv ext, e, i; // ext[e(i, ...), ...]
if (mg_der_next(&field, &ext) <= 0 || ext.type != 0x30) return -1;
while (mg_der_next(&ext, &e) > 0) {
if (mg_der_next(&e, &i) <= 0 || i.type != 0x06) return -1;
if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x13", 3) == 0) {
struct mg_der_tlv s, v; // basicConstraints
MG_VERBOSE(("basicConstraints"));
if (mg_der_next(&e, &i) <= 0 || i.type != 0x01 || i.len != 1 || *i.value != 0xff) return -1;
if (mg_der_next(&e, &i) <= 0 || i.type != 0x04) return -1;
if (mg_der_next(&i, &s) <= 0 || s.type != 0x30) return -1;
if (s.len == 0) break;
if (mg_der_next(&s, &v) <= 0) return -1;
if (v.type == 0x01 && v.len == 1 && *v.value == 0xff) ca = true;
} else if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x0f", 3) == 0) {
struct mg_der_tlv b; // keyUsage, enforce when present
MG_VERBOSE(("keyUsage"));
if (mg_der_next(&e, &i) <= 0) return -1;
if (i.type == 0x01) { // SHOULD, defaults to false
if (i.len != 1) return -1;
if (mg_der_next(&e, &i) <= 0) return -1;
}
if (i.type != 0x04) return -1;
if (mg_der_next(&i, &b) <= 0 || b.type != 0x03) return -1;
if (!(b.value[1] & MG_BIT(2))) certsign = false;
}
}
if (ca && certsign) cert->is_ca = true;
}
}
// Parse signature // Parse signature
if (mg_der_next(&root, &field) <= 0 || field.type != 0x30) return -1; if (mg_der_next(&root, &field) <= 0 || field.type != 0x30) return -1;
if (mg_der_next(&root, &raw_sig) <= 0 || raw_sig.type != 0x03) return -1; if (mg_der_next(&root, &raw_sig) <= 0 || raw_sig.type != 0x03) return -1;
@ -1536,6 +1612,16 @@ static int mg_tls_parse_cert_der(void *buf, size_t dersz,
return 0; return 0;
} }
static int countdots(struct mg_str s) {
int count = 0;
size_t len = s.len;
char *p = s.buf;
while (len--) {
if (*(p++) == '.') ++count;
}
return count;
}
static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz, static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
const char *server_name, const char *server_name,
struct mg_addr *server_ip) { struct mg_addr *server_ip) {
@ -1565,9 +1651,10 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
return 1; // and matches the one we're connected to return 1; // and matches the one we're connected to
#endif #endif
} else { // this is a text SAN } else { // this is a text SAN
struct mg_str sn, tn;
MG_VERBOSE(("Found SAN, (%u): %.*s", name.type, name.len, name.value)); MG_VERBOSE(("Found SAN, (%u): %.*s", name.type, name.len, name.value));
if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len), sn = mg_str(server_name), tn = mg_str_n((char *) name.value, name.len);
NULL)) if (countdots(sn) == countdots(tn) && mg_match(sn, tn, NULL))
return 1; // and matches the host name return 1; // and matches the host name
} }
} }
@ -1589,10 +1676,12 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
#if MG_UECC_SUPPORTS_secp256r1 #if MG_UECC_SUPPORTS_secp256r1
if (issuer->pubkey.len == 64) { if (issuer->pubkey.len == 64) {
const uint32_t N = 32; const uint32_t N = 32;
if (a.len > N) a.value += (a.len - N), a.len = N; if (a.len > N) a.value += (a.len - N), a.len = N; // padding
if (b.len > N) b.value += (b.len - N), b.len = N; if (b.len > N) b.value += (b.len - N), b.len = N;
memmove(sig, a.value, N); memset(sig, 0, N - a.len); // short encoding
memmove(sig + N, b.value, N); memmove(sig + (N - a.len), a.value, a.len);
memset(sig + N, 0, N - b.len);
memmove(sig + N + (N - b.len), b.value, b.len);
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
(unsigned) cert->tbshashsz, sig, (unsigned) cert->tbshashsz, sig,
mg_uecc_secp256r1()); mg_uecc_secp256r1());
@ -1615,7 +1704,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
return 0; return 0;
} }
} else { } else {
int r; uint8_t r;
const uint8_t *n; const uint8_t *n;
size_t nlen; size_t nlen;
uint8_t sig2[512]; // 4096 bits uint8_t sig2[512]; // 4096 bits
@ -1623,7 +1712,8 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
if (mg_der_parse((uint8_t *) issuer->pubkey.buf, issuer->pubkey.len, if (mg_der_parse((uint8_t *) issuer->pubkey.buf, issuer->pubkey.len,
&seq) <= 0 || &seq) <= 0 ||
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
exponent.type != 2 || exponent.len == 0) {
return -1; return -1;
} }
n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len); n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
@ -1634,7 +1724,14 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
return 0; return 0;
} }
r = memcmp(sig2 + nlen - cert->tbshashsz, cert->tbshash, cert->tbshashsz); r = 0;
{
const uint8_t *p, *q;
size_t i;
p = sig2 + nlen - cert->tbshashsz;
q = cert->tbshash;
for (i = 0; i < cert->tbshashsz; i++) r |= (uint8_t)(*p++ ^ *q++);
}
return r == 0; return r == 0;
} }
} }
@ -1643,8 +1740,10 @@ static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) {
struct mg_der_tlv v; struct mg_der_tlv v;
int matched = 0; int matched = 0;
if (mg_der_find_oid(subj, (uint8_t *) "\x55\x04\x03", 3, &v) > 0) { if (mg_der_find_oid(subj, (uint8_t *) "\x55\x04\x03", 3, &v) > 0) {
struct mg_str hn, cn;
MG_VERBOSE(("using CN: %.*s <-> %s", v.len, v.value, host)); MG_VERBOSE(("using CN: %.*s <-> %s", v.len, v.value, host));
matched = mg_match(mg_str(host), mg_str_n((char *) v.value, v.len), NULL); hn = mg_str(host), cn = mg_str_n((char *) v.value, v.len);
matched = (int) (countdots(hn) == countdots(cn) && mg_match(hn, cn, NULL));
} }
return matched; return matched;
} }
@ -1693,7 +1792,7 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
return -1; return -1;
} }
if (tls->recv_len < 11) { if (tls->recv_len < 13) { // 8 + 3 + 2, chain len + cert len + ext len
mg_error(c, "certificate list too short"); mg_error(c, "certificate list too short");
return -1; return -1;
} }
@ -1705,14 +1804,13 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1);
uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5);
uint8_t *p = recv_buf + 8; uint8_t *p = recv_buf + 8;
uint8_t *endp = recv_buf + cert_chain_len; uint8_t *endp = recv_buf + cert_chain_len + 8;
bool found_ca = false; bool found_ca = false;
struct mg_tls_cert ca; struct mg_tls_cert ca;
if (cert_chain_len != full_cert_chain_len - 4) { if (cert_chain_len != full_cert_chain_len - 4 || cert_chain_len > (tls->recv_len - 8)) {
MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len, MG_ERROR(("full chain length: %d, chain length: %d, msg length: %d", full_cert_chain_len, cert_chain_len, tls->recv_len));
cert_chain_len)); mg_error(c, "invalid certificate chain length");
mg_error(c, "certificate chain length mismatch");
return -1; return -1;
} }
@ -1729,14 +1827,25 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
while (p < endp) { while (p < endp) {
struct mg_tls_cert *ci = &certs[certnum++]; struct mg_tls_cert *ci = &certs[certnum++];
uint32_t certsz = MG_LOAD_BE24(p); uint32_t certsz;
uint16_t certext;
uint8_t *cert = p + 3; uint8_t *cert = p + 3;
uint16_t certext = MG_LOAD_BE16(cert + certsz);
if ((endp - p) < 5) { // 3 + 2, cert len + ext len fields
mg_error(c, "truncated certificate in chain");
return -1;
}
certsz = MG_LOAD_BE24(p);
p = cert + certsz + 2; // skip cert extensions (size only, not supported)
if (p > endp) {
mg_error(c, "invalid certificate length");
return -1;
}
certext = MG_LOAD_BE16(cert + certsz);
if (certext != 0) { if (certext != 0) {
mg_error(c, "certificate extensions are not supported"); mg_error(c, "certificate extensions are not supported");
return -1; return -1;
} }
p = cert + certsz + 2;
if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) {
mg_error(c, "failed to parse certificate"); mg_error(c, "failed to parse certificate");
@ -1759,8 +1868,8 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len);
tls->pubkeysz = ci->pubkey.len; tls->pubkeysz = ci->pubkey.len;
} else { } else {
if (!mg_tls_verify_cert_signature(ci - 1, ci)) { if (!ci->is_ca || !mg_tls_verify_cert_signature(ci - 1, ci)) {
mg_error(c, "failed to verify certificate chain"); mg_error(c, "failed to verify certificate chain %s", ci->is_ca ? "" : "(not a true CA)");
return -1; return -1;
} }
} }
@ -1856,7 +1965,8 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 ||
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) { modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
exponent.type != 2 || exponent.len == 0) {
mg_error(c, "invalid public key"); mg_error(c, "invalid public key");
return -1; return -1;
} }
@ -1864,12 +1974,8 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) {
n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len); n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
if (nlen > sizeof(sig2) || if (nlen > sizeof(sig2) ||
mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value,
exponent.len, sigbuf, siglen, sig2, nlen) != 0) { exponent.len, sigbuf, siglen, sig2, nlen) != 0 ||
mg_error(c, "failed to verify RSA certificate (certverify)"); !mg_rsa_verify(sig2, nlen, tls->sighash)) {
return -1;
}
if (sig2[nlen - 1] != 0xbc) {
mg_error(c, "failed to verify RSA certificate (certverify)"); mg_error(c, "failed to verify RSA certificate (certverify)");
return -1; return -1;
} }
@ -2189,13 +2295,6 @@ static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
memset(key, 0, sizeof(*key)); memset(key, 0, sizeof(*key));
// Debug: show first few bytes
MG_VERBOSE(
("RSA key DER first 16 bytes: %02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x",
der[0], der[1], der[2], der[3], der[4], der[5], der[6], der[7], der[8],
der[9], der[10], der[11], der[12], der[13], der[14], der[15]));
// Parse outer SEQUENCE // Parse outer SEQUENCE
if (end - p < 2) { if (end - p < 2) {
MG_ERROR(("RSA key too short for SEQUENCE header")); MG_ERROR(("RSA key too short for SEQUENCE header"));
@ -2559,26 +2658,20 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
} }
if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) { 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, ...] // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...]
// 30 nn 02 01 01 04 20 [key] ... // 30 nn 02 01 01 04 20 [key] ...
if (key.buf[0] != 0x30 || (key.buf[1] & 0x80) != 0) { if (key.len < (2 + 5 + 32) || key.buf[0] != 0x30 ||
MG_ERROR(("EC private key: ASN.1 bad sequence")); (key.buf[1] & 0x80) != 0 ||
return; memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) {
mg_error(c, "EC private key: invalid ASN.1");
} else {
memmove(tls->ec_key, key.buf + 7, 32);
} }
if (memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) {
MG_ERROR(("EC private key: ASN.1 bad data"));
}
memmove(tls->ec_key, key.buf + 7, 32);
mg_free((void *) key.buf); mg_free((void *) key.buf);
} else if (mg_parse_pem(opts->key, mg_str_s("RSA PRIVATE KEY"), &key) == 0) { } else if (mg_parse_pem(opts->key, mg_str_s("RSA PRIVATE KEY"), &key) == 0) {
struct mg_rsa_key rsa_key; struct mg_rsa_key rsa_key;
// RSA private key found, store it for later use // RSA private key found, store it for later use
tls->rsa_key_der = key; tls->rsa_key_der = key;
MG_INFO(("Parsed RSA private key: %d bytes", (int) key.len));
// parse and validate the key structure // parse and validate the key structure
// we keep the DER buffer, rsa_key just points into it // we keep the DER buffer, rsa_key just points into it

View file

@ -23,9 +23,8 @@ static int mg_tls_err(struct mg_connection *c, int rc) {
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000 #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
#else #else
static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) { static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) {
mg_random(buf, len);
(void) ctx; (void) ctx;
return 0; return mg_random(buf, len) ? 0 : -1;
} }
#endif #endif

View file

@ -120,14 +120,17 @@ static int mg_bio_write(BIO *bio, const char *buf, int len) {
#ifdef MG_TLS_SSLKEYLOGFILE #ifdef MG_TLS_SSLKEYLOGFILE
static void ssl_keylog_cb(const SSL *ssl, const char *line) { static void ssl_keylog_cb(const SSL *ssl, const char *line) {
FILE *f;
char *keylogfile = getenv("SSLKEYLOGFILE"); char *keylogfile = getenv("SSLKEYLOGFILE");
if (keylogfile == NULL) { if (keylogfile == NULL) return;
return; f = fopen(keylogfile, "a");
if (f != NULL) {
fprintf(f, "%s\n", line);
fflush(f);
fclose(f);
} else {
MG_ERROR(("Cannot open %s", keylogfile));
} }
FILE *f = fopen(keylogfile, "a");
fprintf(f, "%s\n", line);
fflush(f);
fclose(f);
(void) ssl; (void) ssl;
} }
#endif #endif

View file

@ -1,3 +1,4 @@
#include "sha256.h"
#include "tls.h" #include "tls.h"
#include "tls_rsa.h" #include "tls_rsa.h"
#include "util.h" #include "util.h"
@ -885,4 +886,68 @@ done:
return 0; return 0;
} }
// MGF1-SHA256 RFC 8017 B.2.1
static void mgf1_sha256(const uint8_t *seed, size_t seed_len,
uint8_t *mask, size_t mask_len) {
uint8_t cnt[4]; // I2OSP(counter, 4), big-endian
uint8_t tmp[32]; // one SHA-256 output block
uint32_t counter = 0;
size_t off = 0;
mg_sha256_ctx ctx;
while (off < mask_len) {
size_t n = mask_len - off;
if (n > 32) n = 32;
br_enc32be(cnt, counter);
mg_sha256_init(&ctx);
mg_sha256_update(&ctx, seed, seed_len);
mg_sha256_update(&ctx, cnt, 4);
mg_sha256_final(tmp, &ctx);
memcpy(mask + off, tmp, n);
off += n;
++counter;
}
}
// Full RSASSA-PSS-VERIFY for rsa_pss_rsae_sha256 (RFC 8017 9.1.2)
// em - output of RSA public-key primitive (nlen bytes)
// nlen - modulus byte length (66 to 512)
// mhash - SHA-256 of the TLS 1.3 signed content (tls->sighash, 32 bytes)
static bool pss_verify_sha256(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
// TLS 1.3: H_len = salt_len = 32
uint8_t db[479]; // unmasked DB after MGF1 XOR
uint8_t dbmask[479]; // MGF1-SHA256(H, db_len); nlen - hLen - 1 = 479
uint8_t Hprime[32]; // SHA-256(0x00*8 || mhash || salt)
uint8_t Mprime[8 + 32 + 32]; // 0*8 || mhash || salt
const uint8_t *H;
const uint8_t *salt;
size_t db_len;
size_t i;
uint8_t bad;
if (nlen < 32 + 32 + 2 || nlen - 32 - 1 > sizeof(db)) return -1;
if (em[nlen - 1] != 0xbc) return false;
if (em[0] & 0x80) return false;
db_len = nlen - 32 - 1;
H = em + db_len;
mgf1_sha256(H, 32, dbmask, db_len);
for (i = 0; i < db_len; i++) db[i] = em[i] ^ dbmask[i];
db[0] &= 0x7f; // DB = 0x00^(db_len-salt_len-1) || 0x01 || salt(salt_Len)
bad = 0;
for (i = 0; i < db_len - 32 - 1; i++) bad |= db[i];
bad |= db[db_len - 32 - 1] ^ 0x01;
if (bad != 0) return false;
salt = db + db_len - 32;
memset(Mprime, 0, 8);
memcpy(Mprime + 8, mhash, 32);
memcpy(Mprime + 8 + 32, salt, 32);
mg_sha256(Hprime, Mprime, sizeof(Mprime));
bad = 0;
for (i = 0; i < 32; i++) bad |= Hprime[i] ^ H[i];
return (bad == 0);
}
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
return pss_verify_sha256(em, nlen, mhash);
}
#endif /* MG_TLS == MG_TLS_BUILTIN */ #endif /* MG_TLS == MG_TLS_BUILTIN */

View file

@ -10,4 +10,5 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *q, size_t q_len, const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len, const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len); uint8_t *signature, size_t sig_len);
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
#endif // TLS_RSA_H #endif // TLS_RSA_H

View file

@ -1,8 +1,11 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv MIIBizCCATGgAwIBAgIUCeZlJSIxVY+xQGNhIXY42jph+SYwCgYIKoZIzj0EAwIw
b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI EzERMA8GA1UEAwwITW9uZ29vc2UwHhcNMjYwNjE2MTcyMDI3WhcNMzYwNjEzMTcy
TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl MDI3WjATMREwDwYDVQQDDAhNb25nb29zZTBZMBMGByqGSM49AgEGCCqGSM49AwEH
fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg A0IABK4/7zpP+s5acakRWGV/Fhn6mMxsKYNn692c/SuOjG53BExFAK/myVGUMrsv
epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs G57vrWx2+rFavQxrpMUZM2B6mxGjYzBhMB0GA1UdDgQWBBRVNi9ypHsbLJh/hZwm
us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4= /IO+lYcIujAfBgNVHSMEGDAWgBRVNi9ypHsbLJh/hZwm/IO+lYcIujAPBgNVHRMB
Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAgNIADBFAiB7A6qe
3N9QNxYWDYMGgUSYlQr+V2ShL/eOIZcxLt/vQAIhALwYVw/W8n8H3grXtx1Gby+u
97aiTxPGbssyw6YShpaG
-----END CERTIFICATE----- -----END CERTIFICATE-----

View file

@ -12,9 +12,12 @@ subjectAltName=DNS:localhost
EOF EOF
# Generate CA # Generate CA
# Important: CN names must be different for CA and client/server certs # Important: CN names must be different for CA and client/server certs;
# extensions are required for a proper CA certificate
openssl ecparam -noout -name prime256v1 -genkey -out ca.key openssl ecparam -noout -name prime256v1 -genkey -out ca.key
openssl req -x509 -new -key ca.key -days 3650 -subj /CN=Mongoose -out ca.crt openssl req -x509 -new -key ca.key -days 3650 -subj /CN=Mongoose \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign" -out ca.crt
# Generate server cert # Generate server cert
openssl ecparam -noout -name prime256v1 -genkey -out server.key openssl ecparam -noout -name prime256v1 -genkey -out server.key

View file

@ -1267,10 +1267,17 @@ static void test_http_server(void) {
} }
// Directory listing // Directory listing
fetch(&mgr, buf, url, "GET /dirtest/ HTTP/1.0\n\n"); #if MG_ARCH == MG_ARCH_UNIX
ASSERT(system("touch 'dirtest/a<b&c>.txt'") == 0);
#endif
ASSERT(fetch(&mgr, buf, url, "GET /dirtest/ HTTP/1.0\n\n") == 200); ASSERT(fetch(&mgr, buf, url, "GET /dirtest/ HTTP/1.0\n\n") == 200);
MG_DEBUG(("%s", buf));
ASSERT(mgstrstr(mg_str(buf), mg_str(">Index of /dirtest/<")) != NULL); ASSERT(mgstrstr(mg_str(buf), mg_str(">Index of /dirtest/<")) != NULL);
ASSERT(mgstrstr(mg_str(buf), mg_str(">fuzz.c<")) != NULL); ASSERT(mgstrstr(mg_str(buf), mg_str(">fuzz.c<")) != NULL);
#if MG_ARCH == MG_ARCH_UNIX
ASSERT(mgstrstr(mg_str(buf), mg_str(">a&lt;b&amp;c&gt;.txt<")) != NULL);
if (system("rm 'dirtest/a<b&c>.txt'") == 0) (void) 0;
#endif
ASSERT(cmpheader(buf, "A", "B")); ASSERT(cmpheader(buf, "A", "B"));
ASSERT(!cmpheader(buf, "C", "D")); ASSERT(!cmpheader(buf, "C", "D"));
ASSERT(cmpheader(buf, "E", "F")); ASSERT(cmpheader(buf, "E", "F"));
@ -1749,6 +1756,14 @@ static void test_http_parse(void) {
ASSERT(req.body.len == 0); ASSERT(req.body.len == 0);
} }
{
const char *s = "GET / HTTP/1.0\r\nHost: test\r\n"
"Transfer-Encoding: chunked\r\n\r\n";
mg_http_parse(s, strlen(s), &req);
ASSERT(req.proto.len == 8 &&
mg_strcmp(req.proto, mg_str("HTTP/1.0")) == 0);
}
{ {
const char *s = "GET / \r\n"; const char *s = "GET / \r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == 0); ASSERT(mg_http_parse(s, strlen(s), &req) == 0);
@ -1979,10 +1994,25 @@ static void test_http_parse(void) {
{ {
// Test that query-string gets stripped // Test that query-string gets stripped
struct mg_http_message hm; struct mg_http_message hm;
const char *s = "GET /foo?bar HTTP/1.0\n\n"; const char *s = "GET /foo?bar HTTP/1.1\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s)); ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
ASSERT(mg_strcmp(hm.uri, mg_str("/foo")) == 0); ASSERT(mg_strcmp(hm.uri, mg_str("/foo")) == 0);
ASSERT(mg_strcmp(hm.query, mg_str("bar")) == 0); ASSERT(mg_strcmp(hm.query, mg_str("bar")) == 0);
s = "POST / HTTP/1.1\r\nHost: t\r\n"
"Content-Length: 5\r\nContent-Length: 10\r\n\r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
s = "POST / HTTP/1.1\r\nHost: t\r\n"
"Transfer-Encoding: chunked\r\nTransfer-Encoding: identity\r\n\r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
s = "POST / HTTP/1.1\r\nHost: t\r\n"
"Content-Length: 5\r\nTransfer-Encoding: chunked\r\n\r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
s = "POST / HTTP/1.1\r\nHost: t\r\n"
"Transfer-Encoding: chunked\r\nContent-Length: 5\r\n\r\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
} }
} }
@ -2450,15 +2480,23 @@ static void test_str(void) {
TESTDOUBLE("%g", -987.65432, "-987.654"); TESTDOUBLE("%g", -987.65432, "-987.654");
TESTDOUBLE("%g", 0.0000000001, "1e-10"); TESTDOUBLE("%g", 0.0000000001, "1e-10");
TESTDOUBLE("%g", 2.34567e-57, "2.34567e-57"); TESTDOUBLE("%g", 2.34567e-57, "2.34567e-57");
TESTDOUBLE("%.*g", DBLWIDTH(7, 9999999.0), "9999999"); TESTDOUBLE("%g", -2.34567e-57, "-2.34567e-57");
TESTDOUBLE("%.*g", DBLWIDTH(10, 0.123456333), "0.123456333");
TESTDOUBLE("%g", 123.456222, "123.456"); TESTDOUBLE("%g", 123.456222, "123.456");
TESTDOUBLE("%.*g", DBLWIDTH(10, 123.456222), "123.456222");
TESTDOUBLE("%g", 600.1234, "600.123"); TESTDOUBLE("%g", 600.1234, "600.123");
TESTDOUBLE("%g", -600.1234, "-600.123"); TESTDOUBLE("%g", -600.1234, "-600.123");
TESTDOUBLE("%g", 599.1234, "599.123"); TESTDOUBLE("%g", 599.1234, "599.123");
TESTDOUBLE("%g", -599.1234, "-599.123"); TESTDOUBLE("%g", -599.1234, "-599.123");
TESTDOUBLE("%g", 0.14, "0.14"); TESTDOUBLE("%g", 0.14, "0.14");
TESTDOUBLE("%.*g", DBLWIDTH(7, 9999999.0), "9999999");
TESTDOUBLE("%.*g", DBLWIDTH(10, 0.123456333), "0.123456333");
TESTDOUBLE("%.*g", DBLWIDTH(10, 123.456222), "123.456222");
TESTDOUBLE("%.*g", DBLWIDTH(10, 1e11), "1e+11"); // e > width
TESTDOUBLE("%.*g", DBLWIDTH(10, -1e11), "-1e+11"); // -e < -width
{
struct mg_iobuf io = {0, 0, 0, 16};
mg_xprintf(mg_pfn_iobuf, &io, "%.*g", 88, -1e-88); // > sizeof(tmp)
mg_iobuf_free(&io);
}
TESTDOUBLE("%f", 0.14, "0.140000"); TESTDOUBLE("%f", 0.14, "0.140000");
TESTDOUBLE("%.*f", DBLWIDTH(4, 0.14), "0.1400"); TESTDOUBLE("%.*f", DBLWIDTH(4, 0.14), "0.1400");
TESTDOUBLE("%.*f", DBLWIDTH(3, 0.14), "0.140"); TESTDOUBLE("%.*f", DBLWIDTH(3, 0.14), "0.140");
@ -2974,7 +3012,7 @@ static void uc(struct mg_connection *c, int ev, void *ev_data) {
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
mg_printf(c, mg_printf(c,
"POST /upload HTTP/1.0\r\n" "POST /upload HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"); "Transfer-Encoding: chunked\r\n\r\n");
mg_http_printf_chunk(c, "%s", "foo\n"); mg_http_printf_chunk(c, "%s", "foo\n");
mg_http_printf_chunk(c, "%s", "bar\n"); mg_http_printf_chunk(c, "%s", "bar\n");
@ -3272,7 +3310,19 @@ static void test_multipart(void) {
"hello world\r\n" "hello world\r\n"
"\r\n" "\r\n"
"--xyz--\r\n"; "--xyz--\r\n";
const char *bad_multipart =
"--xyz\r\n"
"Content-Disposition: form-data; name=\"foo\"; filename=\"safe\rname.txt\"\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"hello\r\n"
"--xyz--\r\n";
ASSERT(mg_http_next_multipart(mg_str(""), 0, NULL) == 0); ASSERT(mg_http_next_multipart(mg_str(""), 0, NULL) == 0);
ASSERT(mg_http_next_multipart(mg_str(bad_multipart), 0, &part) > 0);
ASSERT(mg_strcmp(part.name, mg_str("foo")) == 0);
ASSERT(mg_strcmp(part.filename, mg_str("safe\rname.txt")) == 0);
ASSERT(mg_strcmp(part.body, mg_str("hello")) == 0);
ASSERT((ofs = mg_http_next_multipart(mg_str(s), 0, &part)) > 0); ASSERT((ofs = mg_http_next_multipart(mg_str(s), 0, &part)) > 0);
ASSERT(mg_strcmp(part.name, mg_str("val")) == 0); ASSERT(mg_strcmp(part.name, mg_str("val")) == 0);
// MG_INFO(("--> [%.*s]", (int) part.body.len, part.body.buf)); // MG_INFO(("--> [%.*s]", (int) part.body.len, part.body.buf));
@ -3487,9 +3537,13 @@ static void test_rewrites(void) {
static void test_get_header_var(void) { static void test_get_header_var(void) {
struct mg_str empty = mg_str(""), bar = mg_str("bar"), baz = mg_str("baz"); struct mg_str empty = mg_str(""), bar = mg_str("bar"), baz = mg_str("baz");
struct mg_str header = mg_str("Digest foo=\"bar\", blah,boo=baz, x=\"yy\""); struct mg_str header = mg_str("Digest foo=\"bar\", blah,boo=baz, x=\"yy\"");
struct mg_str bad_header = mg_str("Digest foo=\"bar");
struct mg_str yy = mg_str("yy"); struct mg_str yy = mg_str("yy");
// struct mg_str x = mg_http_get_header_var(header, mg_str("x")); // struct mg_str x = mg_http_get_header_var(header, mg_str("x"));
// MG_INFO(("--> [%d] [%d]", (int) x.len, yy.len)); // MG_INFO(("--> [%d] [%d]", (int) x.len, yy.len));
struct mg_str bad_value = mg_http_get_header_var(bad_header, mg_str("foo"));
ASSERT(bad_value.len == 4);
ASSERT(mg_strcmp(mg_str("\"bar"), bad_value) == 0);
ASSERT(mg_strcmp(empty, mg_http_get_header_var(empty, empty)) == 0); ASSERT(mg_strcmp(empty, mg_http_get_header_var(empty, empty)) == 0);
ASSERT(mg_strcmp(empty, mg_http_get_header_var(header, empty)) == 0); ASSERT(mg_strcmp(empty, mg_http_get_header_var(header, empty)) == 0);
ASSERT(mg_strcmp(empty, mg_http_get_header_var(header, mg_str("fooo"))) == 0); ASSERT(mg_strcmp(empty, mg_http_get_header_var(header, mg_str("fooo"))) == 0);
@ -4134,57 +4188,116 @@ static void test_x25519(void) {
static void test_rsa(void) { static void test_rsa(void) {
#if MG_TLS == MG_TLS_BUILTIN #if MG_TLS == MG_TLS_BUILTIN
const unsigned char mod[] = { static const uint8_t tv_n[256] = {
0x00, 0xba, 0xee, 0x3b, 0x0b, 0x89, 0x58, 0xa6, 0x19, 0x0d, 0x4c, 0x89, 0xe5, 0xd5, 0x5c, 0xed, 0xa7, 0xeb, 0xdd, 0x7f, 0x2a, 0x23, 0xd3, 0x2b,
0x1a, 0x85, 0x9a, 0xf4, 0x55, 0xc2, 0xdd, 0x0d, 0xd4, 0x4a, 0xf5, 0xed, 0xd1, 0x01, 0x4e, 0xd3, 0x06, 0x51, 0x8a, 0x7f, 0x49, 0xaa, 0x1d, 0xa0,
0xda, 0x28, 0x55, 0x2f, 0x64, 0x46, 0x21, 0x9f, 0x46, 0x5c, 0xfa, 0x37, 0x6b, 0xa4, 0x75, 0x2d, 0x88, 0x99, 0x12, 0x20, 0x56, 0x43, 0x4a, 0x32,
0x88, 0x11, 0xdf, 0xcb, 0x51, 0x73, 0x42, 0x3d, 0x5e, 0x50, 0xde, 0x11, 0x52, 0xa4, 0x92, 0x4f, 0x9e, 0xae, 0x73, 0x7e, 0x22, 0x78, 0x8e, 0xec,
0x30, 0x61, 0x04, 0x59, 0xd0, 0xf4, 0x57, 0xed, 0x13, 0x90, 0x32, 0xc5, 0x64, 0x0a, 0xff, 0xeb, 0x02, 0x9e, 0xfe, 0x0c, 0xbf, 0x37, 0x4e, 0xf9,
0x3f, 0xe6, 0x66, 0xfc, 0x2a, 0x12, 0xa3, 0x1f, 0xd1, 0x77, 0x21, 0x65, 0xb3, 0x71, 0x23, 0x29, 0xae, 0x22, 0xc9, 0x9e, 0xa3, 0xc9, 0x63, 0xa8,
0xdf, 0x9a, 0xcf, 0x04, 0x05, 0xc3, 0x1c, 0xf8, 0x79, 0xb5, 0xf5, 0x97, 0x89, 0x39, 0x89, 0xf0, 0x37, 0x27, 0x1a, 0xbf, 0x9b, 0x70, 0x35, 0xf2,
0x68, 0x98, 0x2e, 0x96, 0x85, 0x3f, 0xee, 0x71, 0x91, 0xc1, 0x54, 0x71, 0x7c, 0x0f, 0x34, 0xf2, 0x80, 0x6b, 0x9b, 0x80, 0x98, 0x16, 0x64, 0xba,
0x9a, 0x80, 0x1f, 0xbe, 0x21, 0xd9, 0xc1, 0x80, 0x9b, 0xd0, 0x5d, 0xb3, 0x7e, 0x51, 0x22, 0xe1, 0xca, 0x39, 0x8c, 0x6c, 0x0b, 0xc6, 0x6b, 0xc8,
0x76, 0x3e, 0xcc, 0x14, 0x3d, 0xec, 0xb7, 0x18, 0x74, 0xfb, 0xc4, 0x0e, 0x74, 0x50, 0x84, 0x9b, 0xe3, 0xf1, 0xdb, 0xf5, 0xff, 0x7e, 0x49, 0xe8,
0x56, 0x8d, 0x3d, 0x78, 0xe6, 0xca, 0xcd, 0x9d, 0xc6, 0x20, 0x5a, 0xeb, 0xdc, 0x41, 0xb9, 0x25, 0x3e, 0x2d, 0xbc, 0x48, 0x8f, 0xc8, 0x6f, 0x1b,
0x9b, 0xc8, 0x19, 0x5e, 0xeb, 0x80, 0xd2, 0xb2, 0xfe, 0x88, 0x15, 0x5c, 0x6b, 0x8a, 0xeb, 0xdb, 0x68, 0xaa, 0x15, 0xd9, 0x5e, 0xd8, 0x11, 0x07,
0x7c, 0x6b, 0x26, 0xe0, 0x43, 0xda, 0xa4, 0x07, 0x85, 0x73, 0xc4, 0x80, 0x03, 0xbd, 0xd2, 0xa9, 0x6f, 0xce, 0x58, 0xb1, 0xb1, 0x86, 0xff, 0x86,
0x28, 0xcb, 0xda, 0x18, 0x56, 0x37, 0x91, 0xd6, 0x41, 0xa1, 0x0b, 0xa2, 0x6e, 0x4a, 0x81, 0x64, 0xa0, 0x6c, 0x83, 0xca, 0xfc, 0x3f, 0xfe, 0x7d,
0x77, 0xd0, 0x62, 0x31, 0xc7, 0xc2, 0x67, 0x6d, 0x75, 0x08, 0x80, 0xe7, 0x95, 0xd6, 0x40, 0x29, 0x21, 0x5a, 0x3b, 0x5d, 0xc8, 0x93, 0xa0, 0x1d,
0xb6, 0xbe, 0xc2, 0x25, 0xc9, 0xe0, 0x2c, 0x02, 0xbf, 0x39, 0x61, 0x7e, 0x2c, 0x6e, 0xb6, 0xc0, 0x65, 0x15, 0x69, 0x8b, 0x67, 0x71, 0x03, 0xde,
0x32, 0xa4, 0xc9, 0xe7, 0x91, 0xe3, 0xa0, 0xcd, 0x94, 0x24, 0xbf, 0x8c, 0xe7, 0xcc, 0x65, 0x83, 0x0e, 0x5a, 0x9d, 0xc9, 0x0e, 0xc1, 0xc7, 0xc4,
0xeb, 0x47, 0x76, 0x53, 0x85, 0xb3, 0xb7, 0x31, 0x80, 0x3c, 0x77, 0x10, 0xf3, 0x47, 0x1e, 0x9d, 0xce, 0x9d, 0xaf, 0x8b, 0x5f, 0xaa, 0x35, 0xfe,
0x69, 0xc3, 0x04, 0xd1, 0x60, 0x4c, 0x74, 0xda, 0x15, 0x18, 0x0b, 0x20, 0x15, 0x59, 0xd4, 0xc1, 0xd5, 0xaa, 0x3b, 0x3a, 0x0d, 0x9a, 0x98, 0x88,
0x6f, 0xb3, 0x03, 0x58, 0x4a, 0xfc, 0xd1, 0xd2, 0xcf, 0x37, 0x15, 0x0a, 0x1b, 0x5e, 0xf8, 0x5b, 0x07, 0xbf, 0xdb, 0x5e, 0x88, 0x46, 0x4a, 0xde,
0x63, 0xc8, 0xe9, 0xd5, 0x7d, 0xd5, 0xf2, 0x90, 0x78, 0x53, 0x49, 0xa9, 0x9a, 0x63, 0x30, 0x7b, 0x4f, 0x3f, 0xc6, 0x9b, 0x88, 0x98, 0x34, 0xbe,
0xc5, 0x25, 0x65, 0x5c, 0x01}; 0xfd, 0xbb, 0xf5, 0xd1};
const unsigned char exp[] = {1, 0, 1}; // 65537
const unsigned char sig[] = { static const uint8_t tv_e[3] = {0x01, 0x00, 0x01};
0x1e, 0xb1, 0x6a, 0xcb, 0x39, 0x63, 0x12, 0xed, 0x85, 0x62, 0x4b, 0x85,
0x47, 0x25, 0x67, 0xbd, 0xbd, 0x0e, 0xaa, 0x73, 0x34, 0x5f, 0x07, 0x2b, static const uint8_t tv_sig[256] = {
0xbb, 0x4f, 0xf5, 0x21, 0x88, 0xb1, 0x04, 0x2c, 0xbb, 0x52, 0x72, 0x64, 0x8f, 0xc5, 0x67, 0x8f, 0x44, 0xae, 0x2c, 0x03, 0x2b, 0xb4, 0xfb, 0xd5,
0x89, 0x45, 0x50, 0x41, 0x73, 0xca, 0xda, 0x97, 0xae, 0x81, 0x89, 0x4f, 0x1a, 0xba, 0xc7, 0xd2, 0x7a, 0x9a, 0xaa, 0x2d, 0x31, 0x49, 0x4c, 0x73,
0x83, 0x8d, 0x48, 0x65, 0x63, 0xe7, 0x82, 0x03, 0xd2, 0x40, 0x07, 0x1c, 0x46, 0x38, 0x1e, 0xd6, 0xb2, 0xc0, 0x8f, 0x2f, 0x8c, 0xf6, 0xba, 0x7c,
0x86, 0x58, 0xd5, 0xac, 0x89, 0xb1, 0xca, 0x5c, 0xde, 0x21, 0x06, 0x88, 0x81, 0xe4, 0xbe, 0x37, 0x09, 0xb3, 0x9a, 0xae, 0x7e, 0x06, 0xcd, 0x3a,
0x88, 0x0c, 0xe1, 0x20, 0xc0, 0xdf, 0xf1, 0x92, 0x9b, 0xb8, 0xa5, 0xeb, 0x48, 0xc3, 0x6a, 0x7b, 0x06, 0x28, 0x70, 0x06, 0x4c, 0xd9, 0x38, 0xa8,
0x6d, 0x89, 0xcc, 0x5c, 0x5c, 0x24, 0x3e, 0x9b, 0x3c, 0x35, 0x32, 0xa5, 0x25, 0x7e, 0x6f, 0xdc, 0x65, 0xc0, 0x17, 0x2c, 0x7b, 0x97, 0x1e, 0x61,
0x04, 0x9e, 0x8c, 0x49, 0x01, 0xee, 0xbf, 0x1f, 0x2c, 0xb0, 0x52, 0xa8, 0x00, 0xb9, 0xdf, 0xd0, 0x2b, 0x54, 0x3a, 0xff, 0x18, 0x32, 0x8e, 0x69,
0xab, 0x79, 0x11, 0xcf, 0xb5, 0x5a, 0x16, 0xa1, 0xee, 0x21, 0x6a, 0x5a, 0x5e, 0x64, 0x47, 0x68, 0xd8, 0x3a, 0x78, 0xf0, 0x91, 0x7d, 0x6b, 0xe0,
0x2b, 0x14, 0xae, 0x32, 0x3c, 0xa2, 0x6c, 0xa2, 0x40, 0x0c, 0xcb, 0x9e, 0xc1, 0x6b, 0x5f, 0xd8, 0x7b, 0x22, 0xd6, 0x5d, 0x3b, 0x73, 0xf2, 0x1c,
0x8f, 0x69, 0xab, 0xd7, 0xf3, 0xd8, 0xd1, 0xfb, 0x2d, 0xfa, 0xa9, 0x13, 0x9c, 0x73, 0xb7, 0x29, 0x22, 0xaf, 0x27, 0x0c, 0xce, 0x29, 0xa1, 0x46,
0x09, 0xbf, 0xa7, 0xca, 0xc8, 0x90, 0x74, 0x23, 0x7b, 0x3e, 0xdd, 0x81, 0x09, 0x5f, 0x9a, 0x9f, 0xa5, 0x6f, 0x88, 0x65, 0x23, 0x68, 0xaf, 0x1d,
0x32, 0xa7, 0x88, 0x42, 0x56, 0x8a, 0xcb, 0xe8, 0x8f, 0xef, 0x06, 0x9f, 0x56, 0x32, 0x69, 0x6f, 0x9c, 0x2d, 0x93, 0x0d, 0x99, 0x8a, 0x39, 0x53,
0x39, 0x7e, 0x8e, 0x24, 0x07, 0xb3, 0xae, 0x7e, 0x13, 0x6b, 0xf2, 0xf8, 0x82, 0x0a, 0xae, 0xe2, 0xe3, 0xf1, 0x03, 0x7a, 0xb3, 0x5b, 0x05, 0x6d,
0x35, 0xe4, 0x16, 0x3e, 0xae, 0xf2, 0x55, 0x79, 0x10, 0x39, 0xfa, 0x70, 0xdd, 0xbe, 0xb5, 0x0d, 0x53, 0x81, 0x93, 0x9b, 0xdb, 0xd6, 0x39, 0x61,
0x3a, 0x1b, 0x02, 0xb3, 0x2b, 0x1d, 0x44, 0xac, 0x30, 0x81, 0xd3, 0x11, 0x97, 0x0c, 0x23, 0xd3, 0x98, 0x51, 0xb1, 0xd9, 0x42, 0x1d, 0x5d, 0x29,
0xdd, 0x34, 0x1e, 0xcd, 0x26, 0xf5, 0x89, 0xc6, 0x55, 0x23, 0x17, 0x09, 0x2b, 0x64, 0xda, 0xa9, 0x37, 0x70, 0x30, 0x77, 0xa0, 0x99, 0x8d, 0x13,
0xd2, 0xc1, 0xdc, 0x49, 0xf9, 0x99, 0x36, 0x2b, 0xf5, 0xae, 0x42, 0x5c, 0x67, 0x5d, 0x68, 0x80, 0x9f, 0x68, 0x25, 0x30, 0x50, 0x31, 0xe3, 0xed,
0xb7, 0x80, 0xda, 0x32, 0x69, 0x28, 0xa3, 0xee, 0xb9, 0xd4, 0x90, 0xa6, 0xd2, 0xa2, 0xa0, 0xfc, 0xf7, 0xb4, 0x85, 0xbf, 0x68, 0xdc, 0x14, 0xbc,
0xab, 0x34, 0x17, 0x5e, 0xa0, 0xd6, 0xc1, 0x54, 0xc6, 0x9c, 0x58, 0x3a, 0xeb, 0xd7, 0x9f, 0x7a, 0x6a, 0xb1, 0x9c, 0x8b, 0xf5, 0xff, 0xc1, 0x5a,
0xaf, 0xbf, 0xdc, 0x64}; 0xf7, 0xaf, 0x52, 0x88, 0x0e, 0xf2, 0x5c, 0x10, 0x02, 0x35, 0xe2, 0xcc,
unsigned char v[256]; // 2048 bits 0xd9, 0x2b, 0x20, 0x80, 0xc2, 0xb9, 0xfa, 0x5f, 0xbd, 0xc6, 0xd2, 0xd7,
mg_rsa_mod_pow(mod, sizeof(mod), exp, sizeof(exp), sig, sizeof(sig), v, 0xc1, 0xe3, 0xcd, 0x59};
sizeof(v));
ASSERT(v[sizeof(v) - 1] == 0xbc); static const uint8_t tv_em[256] = {
0x17, 0x08, 0xf4, 0xe4, 0x67, 0x2b, 0xa9, 0x3b, 0x34, 0x13, 0x96, 0xeb,
0xd1, 0x14, 0xf1, 0x90, 0x4c, 0x6f, 0xc9, 0xec, 0xa0, 0x85, 0xa8, 0xa6,
0x89, 0xe4, 0xd4, 0x48, 0x41, 0x5b, 0x6b, 0x1c, 0x79, 0xce, 0x87, 0xdb,
0x12, 0x45, 0x97, 0x3b, 0x37, 0xd9, 0xd6, 0xe9, 0x7e, 0x6e, 0xcf, 0xb2,
0x84, 0x47, 0xe4, 0xda, 0x6c, 0x53, 0xfd, 0xe3, 0x18, 0x0c, 0xa7, 0xd4,
0x70, 0xc6, 0xd5, 0xd9, 0xc2, 0xbb, 0x08, 0xfd, 0xf4, 0x56, 0x66, 0x21,
0x68, 0xb5, 0xb5, 0x4c, 0x9b, 0x1d, 0xdb, 0xac, 0xa0, 0xc4, 0x64, 0x1e,
0xee, 0x1b, 0xe0, 0xc8, 0x84, 0xc5, 0xa9, 0xd0, 0x50, 0xd8, 0xb6, 0xd2,
0xb5, 0x1f, 0xbc, 0xf7, 0x01, 0xd2, 0x53, 0x44, 0xc9, 0x1c, 0xae, 0x45,
0x28, 0xbd, 0xbe, 0x28, 0x6e, 0xb9, 0x06, 0xd6, 0xc9, 0xcd, 0x5a, 0xdd,
0x31, 0x99, 0x56, 0x22, 0xf0, 0xd7, 0xa4, 0xb3, 0x38, 0x04, 0xcc, 0x7f,
0x45, 0xba, 0x05, 0x26, 0xf9, 0x34, 0x50, 0xb4, 0xcf, 0xf3, 0x81, 0xb7,
0xf9, 0xf1, 0x2a, 0xbc, 0x2e, 0xe1, 0x51, 0x12, 0x23, 0x5a, 0xec, 0xe8,
0x59, 0x1b, 0xb2, 0x58, 0x6e, 0x17, 0x3c, 0x9e, 0x3a, 0x24, 0xf2, 0x7d,
0xd8, 0xfa, 0x82, 0xf5, 0x30, 0x13, 0x53, 0xf5, 0x6e, 0x08, 0xdd, 0x0d,
0x92, 0x24, 0x84, 0x02, 0x7b, 0x64, 0x55, 0x1c, 0xda, 0xf4, 0xb7, 0xc1,
0x35, 0x87, 0xd2, 0x79, 0xf4, 0x34, 0xc3, 0xb7, 0x58, 0xdb, 0x8b, 0x82,
0x71, 0x49, 0xc1, 0x85, 0x7f, 0x56, 0x8a, 0xf7, 0xaf, 0xbb, 0xd6, 0x38,
0x38, 0x34, 0x4b, 0x93, 0xe9, 0x77, 0x37, 0xd0, 0x9c, 0xd2, 0xe0, 0x76,
0x6f, 0xa2, 0x20, 0x2e, 0x0a, 0x2e, 0x48, 0x5e, 0xa2, 0x83, 0xd2, 0xfa,
0xc2, 0xc8, 0xd9, 0xa7, 0xcd, 0x7b, 0xb3, 0x78, 0x30, 0x46, 0x7e, 0x83,
0xbc, 0x11, 0x40, 0xbc};
static const uint8_t tv_mhash[32] = {
0x21, 0x83, 0x9d, 0xb5, 0x7b, 0xe2, 0x84, 0xc2, 0x31, 0xf7, 0xb6,
0xa1, 0x90, 0x9b, 0x53, 0x99, 0x5f, 0x09, 0x1b, 0x84, 0xf7, 0x35,
0x57, 0xbb, 0xbb, 0xef, 0x7f, 0x7b, 0x93, 0xe8, 0xef, 0x16};
ASSERT(mg_rsa_verify(tv_em, 256, tv_mhash));
{
uint8_t em[256];
int r = mg_rsa_mod_pow(tv_n, sizeof(tv_n), tv_e, sizeof(tv_e), tv_sig,
sizeof(tv_sig), em, sizeof(em));
ASSERT(r == 0);
ASSERT(memcmp(em, tv_em, 256) == 0);
ASSERT(mg_rsa_verify(em, 256, tv_mhash));
}
{
uint8_t mhash[32];
uint8_t em[256];
memcpy(mhash, tv_mhash, 32);
memcpy(em, tv_em, 256);
mhash[0] ^= 0x01; // wrong mhash
ASSERT(!mg_rsa_verify(tv_em, 256, mhash));
mhash[0] = tv_mhash[0];
em[255] ^= 0x01; // bad trailer
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
em[255] = tv_em[255];
em[0] |= 0x80; // top bit set
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
em[0] = tv_em[0];
em[223] ^= 0xFF; // corrupt h field
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
em[223] = tv_em[223];
em[50] ^= 0x01; // corrupt zero padding
ASSERT(!mg_rsa_verify(em, 256, tv_mhash));
em[50] = tv_em[50];
}
#endif #endif
} }
@ -5148,7 +5261,6 @@ static void test_modbus(void) {
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
} }
struct wudata { struct wudata {
struct mg_mgr *mgr; struct mg_mgr *mgr;
unsigned long conn_id; // Parent connection ID unsigned long conn_id; // Parent connection ID
@ -5192,11 +5304,16 @@ static void test_wakeup(void) {
#endif #endif
} }
extern uint64_t mg_boot_timestamp_ms;
int main(void) { int main(void) {
const char *debug_level = getenv("V"); const char *debug_level = getenv("V");
if (debug_level == NULL) debug_level = "3"; if (debug_level == NULL) debug_level = "3";
mg_log_set(atoi(debug_level)); mg_log_set(atoi(debug_level));
// make sure there is a time reference for mg_now() regardless of SNTP tests
mg_boot_timestamp_ms = (uint64_t) time(NULL) * 1000;
s_error = false; s_error = false;
test_modbus(); test_modbus();
DASHBOARD("modbus"); DASHBOARD("modbus");

View file

@ -31,7 +31,7 @@ static size_t mg_mqtt_next_topic(struct mg_mqtt_message *msg,
size_t pos) { size_t pos) {
unsigned char *buf = (unsigned char *) msg->dgram.buf + pos; unsigned char *buf = (unsigned char *) msg->dgram.buf + pos;
size_t new_pos; size_t new_pos;
if (pos >= msg->dgram.len) return 0; if (pos + 2 > msg->dgram.len) return 0;
topic->len = (size_t) (((unsigned) buf[0]) << 8 | buf[1]); topic->len = (size_t) (((unsigned) buf[0]) << 8 | buf[1]);
topic->buf = (char *) buf + 2; topic->buf = (char *) buf + 2;
@ -77,7 +77,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
uint8_t qos, resp[256]; uint8_t qos, resp[256];
struct mg_str topic; struct mg_str topic;
int num_topics = 0; int num_topics = 0;
while ((pos = mg_mqtt_next_sub(mm, &topic, &qos, pos)) > 0) { while (num_topics < sizeof(resp) &&
(pos = mg_mqtt_next_sub(mm, &topic, &qos, pos)) > 0) {
struct sub *sub = (struct sub *)calloc(1, sizeof(*sub)); struct sub *sub = (struct sub *)calloc(1, sizeof(*sub));
sub->c = c; sub->c = c;
sub->topic = mg_strdup(topic); sub->topic = mg_strdup(topic);