diff --git a/SmartEVSE-3/include/mongoose.h b/SmartEVSE-3/include/mongoose.h index 3a439a6e..de6898ef 100644 --- a/SmartEVSE-3/include/mongoose.h +++ b/SmartEVSE-3/include/mongoose.h @@ -20,7 +20,7 @@ #ifndef MONGOOSE_H #define MONGOOSE_H -#define MG_VERSION "7.14" +#define MG_VERSION "7.15" #ifdef __cplusplus extern "C" { @@ -186,7 +186,7 @@ extern "C" { #define calloc(a, b) mg_calloc(a, b) #define free(a) vPortFree(a) #define malloc(a) pvPortMalloc(a) -#define strdup(s) mg_mprintf("%s", s) +#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) // Re-route calloc/free to the FreeRTOS's functions, don't use stdlib static inline void *mg_calloc(size_t cnt, size_t size) { @@ -288,7 +288,7 @@ extern uint32_t rt_time_get(void); #include "cmsis_os2.h" // keep this include #endif -#define strdup(s) mg_mprintf("%s", s) +#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) #if defined(__ARMCC_VERSION) #define mode_t size_t @@ -386,6 +386,10 @@ static inline int mg_mkdir(const char *path, mode_t mode) { #define MG_PATH_MAX FILENAME_MAX #endif +#ifndef MG_ENABLE_POSIX_FS +#define MG_ENABLE_POSIX_FS 1 +#endif + #endif @@ -499,6 +503,10 @@ typedef int socklen_t; #define SIGPIPE 0 #endif +#ifndef MG_ENABLE_POSIX_FS +#define MG_ENABLE_POSIX_FS 1 +#endif + #endif @@ -549,7 +557,6 @@ int sscanf(const char *, const char *, ...); #include #include -#include // contents to be moved and file removed, some day #define MG_SOCKET_TYPE Socket_t #define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET @@ -581,6 +588,9 @@ int sscanf(const char *, const char *, ...); #define sockaddr_in freertos_sockaddr #define sockaddr freertos_sockaddr +#if ipFR_TCP_VERSION_MAJOR >= 4 +#define sin_addr sin_address.ulIP_IPv4 +#endif #define accept(a, b, c) FreeRTOS_accept((a), (b), (c)) #define connect(a, b, c) FreeRTOS_connect((a), (b), (c)) #define bind(a, b, c) FreeRTOS_bind((a), (b), (c)) @@ -780,12 +790,8 @@ struct timeval { #endif #ifndef MG_ENABLE_POSIX_FS -#if defined(FOPEN_MAX) -#define MG_ENABLE_POSIX_FS 1 -#else #define MG_ENABLE_POSIX_FS 0 #endif -#endif #ifndef MG_INVALID_SOCKET #define MG_INVALID_SOCKET (-1) @@ -861,6 +867,7 @@ struct mg_str mg_str_n(const char *s, size_t n); int mg_casecmp(const char *s1, const char *s2); int mg_strcmp(const struct mg_str str1, const struct mg_str str2); int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); +struct mg_str mg_strdup(const struct mg_str s); bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps); bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim); @@ -1051,7 +1058,6 @@ uint16_t mg_ntohs(uint16_t net); uint32_t mg_ntohl(uint32_t net); uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); uint64_t mg_millis(void); // Return milliseconds since boot -uint64_t mg_now(void); // Return milliseconds since Epoch bool mg_path_is_sane(const struct mg_str path); #define mg_htons(x) mg_ntohs(x) @@ -1664,6 +1670,111 @@ int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, /* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ +#ifndef _UECC_TYPES_H_ +#define _UECC_TYPES_H_ + +#ifndef MG_UECC_PLATFORM +#if defined(__AVR__) && __AVR__ +#define MG_UECC_PLATFORM mg_uecc_avr +#elif defined(__thumb2__) || \ + defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ +#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 +#elif defined(__thumb__) +#define MG_UECC_PLATFORM mg_uecc_arm_thumb +#elif defined(__arm__) || defined(_M_ARM) +#define MG_UECC_PLATFORM mg_uecc_arm +#elif defined(__aarch64__) +#define MG_UECC_PLATFORM mg_uecc_arm64 +#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ + defined(__I86__) +#define MG_UECC_PLATFORM mg_uecc_x86 +#elif defined(__amd64__) || defined(_M_X64) +#define MG_UECC_PLATFORM mg_uecc_x86_64 +#else +#define MG_UECC_PLATFORM mg_uecc_arch_other +#endif +#endif + +#ifndef MG_UECC_ARM_USE_UMAAL +#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) +#define MG_UECC_ARM_USE_UMAAL 1 +#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ + (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) +#define MG_UECC_ARM_USE_UMAAL 1 +#else +#define MG_UECC_ARM_USE_UMAAL 0 +#endif +#endif + +#ifndef MG_UECC_WORD_SIZE +#if MG_UECC_PLATFORM == mg_uecc_avr +#define MG_UECC_WORD_SIZE 1 +#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) +#define MG_UECC_WORD_SIZE 8 +#else +#define MG_UECC_WORD_SIZE 4 +#endif +#endif + +#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ + (MG_UECC_WORD_SIZE != 8) +#error "Unsupported value for MG_UECC_WORD_SIZE" +#endif + +#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) +#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") +#undef MG_UECC_WORD_SIZE +#define MG_UECC_WORD_SIZE 1 +#endif + +#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ + MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ + MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ + (MG_UECC_WORD_SIZE != 4)) +#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") +#undef MG_UECC_WORD_SIZE +#define MG_UECC_WORD_SIZE 4 +#endif + +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +typedef int8_t cmpresult_t; + +#if (MG_UECC_WORD_SIZE == 1) + +typedef uint8_t mg_uecc_word_t; +typedef uint16_t mg_uecc_dword_t; + +#define HIGH_BIT_SET 0x80 +#define MG_UECC_WORD_BITS 8 +#define MG_UECC_WORD_BITS_SHIFT 3 +#define MG_UECC_WORD_BITS_MASK 0x07 + +#elif (MG_UECC_WORD_SIZE == 4) + +typedef uint32_t mg_uecc_word_t; +typedef uint64_t mg_uecc_dword_t; + +#define HIGH_BIT_SET 0x80000000 +#define MG_UECC_WORD_BITS 32 +#define MG_UECC_WORD_BITS_SHIFT 5 +#define MG_UECC_WORD_BITS_MASK 0x01F + +#elif (MG_UECC_WORD_SIZE == 8) + +typedef uint64_t mg_uecc_word_t; + +#define HIGH_BIT_SET 0x8000000000000000U +#define MG_UECC_WORD_BITS 64 +#define MG_UECC_WORD_BITS_SHIFT 6 +#define MG_UECC_WORD_BITS_MASK 0x03F + +#endif /* MG_UECC_WORD_SIZE */ + +#endif /* _UECC_TYPES_H_ */ + +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + #ifndef _UECC_VLI_H_ #define _UECC_VLI_H_ @@ -1830,111 +1941,117 @@ int mg_uecc_generate_random_int(mg_uecc_word_t *random, #endif /* _UECC_VLI_H_ */ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ +// End of uecc BSD-2 +// portable8439 v1.0.1 +// Source: https://github.com/DavyLandman/portable8439 +// Licensed under CC0-1.0 +// Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public +// Domain) -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ -#ifndef MG_UECC_PLATFORM -#if defined(__AVR__) && __AVR__ -#define MG_UECC_PLATFORM mg_uecc_avr -#elif defined(__thumb2__) || \ - defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ -#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 -#elif defined(__thumb__) -#define MG_UECC_PLATFORM mg_uecc_arm_thumb -#elif defined(__arm__) || defined(_M_ARM) -#define MG_UECC_PLATFORM mg_uecc_arm -#elif defined(__aarch64__) -#define MG_UECC_PLATFORM mg_uecc_arm64 -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ - defined(__I86__) -#define MG_UECC_PLATFORM mg_uecc_x86 -#elif defined(__amd64__) || defined(_M_X64) -#define MG_UECC_PLATFORM mg_uecc_x86_64 -#else -#define MG_UECC_PLATFORM mg_uecc_arch_other -#endif -#endif -#ifndef MG_UECC_ARM_USE_UMAAL -#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) -#define MG_UECC_ARM_USE_UMAAL 1 -#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ - (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) -#define MG_UECC_ARM_USE_UMAAL 1 -#else -#define MG_UECC_ARM_USE_UMAAL 0 -#endif -#endif -#ifndef MG_UECC_WORD_SIZE -#if MG_UECC_PLATFORM == mg_uecc_avr -#define MG_UECC_WORD_SIZE 1 -#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) -#define MG_UECC_WORD_SIZE 8 -#else -#define MG_UECC_WORD_SIZE 4 -#endif +#ifndef __PORTABLE_8439_H +#define __PORTABLE_8439_H +#if defined(__cplusplus) +extern "C" { #endif -#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ - (MG_UECC_WORD_SIZE != 8) -#error "Unsupported value for MG_UECC_WORD_SIZE" +// provide your own decl specificier like -DPORTABLE_8439_DECL=ICACHE_RAM_ATTR +#ifndef PORTABLE_8439_DECL +#define PORTABLE_8439_DECL #endif -#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) -#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 1 -#endif +/* + This library implements RFC 8439 a.k.a. ChaCha20-Poly1305 AEAD -#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ - (MG_UECC_WORD_SIZE != 4)) -#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 4 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (MG_UECC_WORD_SIZE == 1) - -typedef uint8_t mg_uecc_word_t; -typedef uint16_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80 -#define MG_UECC_WORD_BITS 8 -#define MG_UECC_WORD_BITS_SHIFT 3 -#define MG_UECC_WORD_BITS_MASK 0x07 - -#elif (MG_UECC_WORD_SIZE == 4) - -typedef uint32_t mg_uecc_word_t; -typedef uint64_t mg_uecc_dword_t; + You can use this library to avoid attackers mutating or reusing your + encrypted messages. This does assume you never reuse a nonce+key pair and, + if possible, carefully pick your associated data. +*/ -#define HIGH_BIT_SET 0x80000000 -#define MG_UECC_WORD_BITS 32 -#define MG_UECC_WORD_BITS_SHIFT 5 -#define MG_UECC_WORD_BITS_MASK 0x01F +/* Make sure we are either nested in C++ or running in a C99+ compiler +#if !defined(__cplusplus) && !defined(_MSC_VER) && \ + (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) +#error "C99 or newer required" +#endif */ + +// #if CHAR_BIT > 8 +// # error "Systems without native octals not suppoted" +// #endif + +#if defined(_MSC_VER) || defined(__cplusplus) +// add restrict support is possible +#if (defined(_MSC_VER) && _MSC_VER >= 1900) || defined(__clang__) || \ + defined(__GNUC__) +#define restrict __restrict +#else +#define restrict +#endif +#endif -#elif (MG_UECC_WORD_SIZE == 8) +#define RFC_8439_TAG_SIZE (16) +#define RFC_8439_KEY_SIZE (32) +#define RFC_8439_NONCE_SIZE (12) -typedef uint64_t mg_uecc_word_t; +/* + Encrypt/Seal plain text bytes into a cipher text that can only be + decrypted by knowing the key, nonce and associated data. -#define HIGH_BIT_SET 0x8000000000000000U -#define MG_UECC_WORD_BITS 64 -#define MG_UECC_WORD_BITS_SHIFT 6 -#define MG_UECC_WORD_BITS_MASK 0x03F + input: + - key: RFC_8439_KEY_SIZE bytes that all parties have agreed + upon beforehand + - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated + for the same key. A counter or a pseudo-random value are fine. + - ad: associated data to include with calculating the tag of the + cipher text. Can be null for empty. + - plain_text: data to be encrypted, pointer + size should not overlap + with cipher_text pointer -#endif /* MG_UECC_WORD_SIZE */ + output: + - cipher_text: encrypted plain_text with a tag appended. Make sure to + allocate at least plain_text_size + RFC_8439_TAG_SIZE -#endif /* _UECC_TYPES_H_ */ -// End of uecc BSD-2 + returns: + - size of bytes written to cipher_text, can be -1 if overlapping + pointers are passed for plain_text and cipher_text +*/ +PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt( + uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE], + const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad, + size_t ad_size, const uint8_t *restrict plain_text, size_t plain_text_size); + +/* + Decrypt/unseal cipher text given the right key, nonce, and additional data. + + input: + - key: RFC_8439_KEY_SIZE bytes that all parties have agreed + upon beforehand + - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated for + the same key. A counter or a pseudo-random value are fine. + - ad: associated data to include with calculating the tag of the + cipher text. Can be null for empty. + - cipher_text: encrypted message. + + output: + - plain_text: data to be encrypted, pointer + size should not overlap + with cipher_text pointer, leave at least enough room for + cipher_text_size - RFC_8439_TAG_SIZE + + returns: + - size of bytes written to plain_text, -1 signals either: + - incorrect key/nonce/ad + - corrupted cipher_text + - overlapping pointers are passed for plain_text and cipher_text +*/ +PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( + uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], + const uint8_t nonce[RFC_8439_NONCE_SIZE], + const uint8_t *restrict cipher_text, size_t cipher_text_size); +#if defined(__cplusplus) +} +#endif +#endif struct mg_connection; @@ -2044,6 +2161,7 @@ struct mg_connection { unsigned is_resp : 1; // Response is still being generated unsigned is_readable : 1; // Connection is ready to read unsigned is_writable : 1; // Connection is ready to write + unsigned is_io_err : 1; // Remember IO_ERR condition for later use }; void mg_mgr_poll(struct mg_mgr *, int ms); @@ -2261,6 +2379,8 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, void mg_sntp_request(struct mg_connection *c); int64_t mg_sntp_parse(const unsigned char *buf, size_t len); +uint64_t mg_now(void); // Return milliseconds since Epoch + @@ -2567,6 +2687,16 @@ struct mg_tcpip_driver { bool (*up)(struct mg_tcpip_if *); // Up/down status }; +typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev, + void *ev_data); + +enum { + MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) + MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr + MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr + MG_TCPIP_EV_USER // Starting ID for user events +}; + // Network interface struct mg_tcpip_if { uint8_t mac[6]; // MAC address. Must be set to a valid MAC @@ -2575,10 +2705,13 @@ struct mg_tcpip_if { bool enable_dhcp_client; // Enable DCHP client bool enable_dhcp_server; // Enable DCHP server bool enable_get_gateway; // DCHP server sets client as gateway + bool enable_req_dns; // DCHP client requests DNS server + bool enable_req_sntp; // DCHP client requests SNTP server bool enable_crc32_check; // Do a CRC check on RX frames and strip it bool enable_mac_check; // Do a MAC check on RX frames struct mg_tcpip_driver *driver; // Low level driver void *driver_data; // Driver-specific data + mg_tcpip_event_handler_t fn; // User-specified event handler function struct mg_mgr *mgr; // Mongoose event manager struct mg_queue recv_queue; // Receive queue uint16_t mtu; // Interface MTU @@ -2848,8 +2981,14 @@ struct mg_tcpip_driver_stm32f_data { #endif -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \ - MG_ENABLE_DRIVER_STM32H +#if MG_ENABLE_TCPIP +#if !defined(MG_ENABLE_DRIVER_STM32H) +#define MG_ENABLE_DRIVER_STM32H 0 +#endif +#if !defined(MG_ENABLE_DRIVER_MCXN) +#define MG_ENABLE_DRIVER_MCXN 0 +#endif +#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN struct mg_tcpip_driver_stm32h_data { // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz @@ -2862,7 +3001,8 @@ struct mg_tcpip_driver_stm32h_data { // 35-60 MHz HCLK/26 3 // 150-250 MHz HCLK/102 4 <-- value for max speed HSI // 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI - // 110, 111 Reserved + // 300-500 MHz HCLK/204 6 + // 500-800 MHz HCLK/324 7 int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 uint8_t phy_addr; // PHY address @@ -2894,6 +3034,7 @@ struct mg_tcpip_driver_stm32h_data { } while (0) #endif +#endif #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C @@ -2915,14 +3056,26 @@ struct mg_tcpip_driver_tm4c_data { #define MG_DRIVER_MDC_CR 1 #endif +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_tm4c_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_tm4c; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: tm4c, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + #endif #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500 -#undef MG_ENABLE_TCPIP_DRIVER_INIT -#define MG_ENABLE_TCPIP_DRIVER_INIT 0 - #endif diff --git a/SmartEVSE-3/src/mongoose.c b/SmartEVSE-3/src/mongoose.c index 3fc0e14a..e9592aa2 100644 --- a/SmartEVSE-3/src/mongoose.c +++ b/SmartEVSE-3/src/mongoose.c @@ -2401,7 +2401,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, } static bool isok(uint8_t c) { - return c == '\n' || c == '\r' || c >= ' '; + return c == '\n' || c == '\r' || c == '\t' || c >= ' '; } int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { @@ -2463,9 +2463,11 @@ static bool mg_http_parse_headers(const char *s, const char *end, if (s >= end || clen(s, end) == 0) return false; // Invalid UTF-8 if (*s++ != ':') return false; // Invalid, not followed by : // if (clen(s, end) == 0) return false; // Invalid UTF-8 - while (s < end && s[0] == ' ') s++; // Skip spaces + while (s < end && (s[0] == ' ' || s[0] == '\t')) s++; // Skip spaces if ((s = skiptorn(s, end, &v)) == NULL) return false; - while (v.len > 0 && v.buf[v.len - 1] == ' ') v.len--; // Trim spaces + while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) { + v.len--; // Trim spaces + } // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf)); h[i].name = k, h[i].value = v; // Success. Assign values } @@ -2851,7 +2853,6 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "", range, opts->extra_headers ? opts->extra_headers : ""); if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) { - c->is_draining = 1; c->is_resp = 0; mg_fs_close(fd); } else { @@ -3214,7 +3215,9 @@ static int skip_chunk(const char *buf, int len, int *pl, int *dl) { } static void http_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { + if (ev == MG_EV_READ || ev == MG_EV_CLOSE || + (ev == MG_EV_POLL && c->is_accepted && !c->is_draining && + c->recv.len > 0)) { // see #2796 struct mg_http_message hm; size_t ofs = 0; // Parsing offset while (c->is_resp == 0 && ofs < c->recv.len) { @@ -3255,6 +3258,7 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { // contain a Content-length header. Other requests can also contain a // body, but their content has no defined semantics (RFC 7231) require_content_len = true; + ofs += (size_t) n; // this request has been processed } else if (is_response) { // HTTP spec 7.2 Entity body: All other responses must include a body // or Content-Length header field defined with a value of 0. @@ -3297,6 +3301,13 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) { if (c->is_accepted) c->is_resp = 1; // Start generating response mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp + if (c->is_accepted) { + struct mg_str *cc = mg_http_get_header(&hm, "Connection"); + if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) { + c->is_draining = 1; // honor "Connection: close" + break; + } + } } if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data } @@ -4705,7 +4716,7 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { if ((str.buf[i] >= '0' && str.buf[i] <= '9') || (str.buf[i] >= 'a' && str.buf[i] <= 'f') || (str.buf[i] >= 'A' && str.buf[i] <= 'F')) { - unsigned long val; // TODO(): This loops on chars, refactor + unsigned long val = 0; // TODO(): This loops on chars, refactor if (i > j + 3) return false; // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j])); mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val)); @@ -5040,6 +5051,10 @@ struct pkt { struct dhcp *dhcp; }; +static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) { + if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data); +} + static void send_syn(struct mg_connection *c); static void mkpay(struct pkt *pkt, void *p) { @@ -5102,13 +5117,14 @@ static void onstatechange(struct mg_tcpip_if *ifp) { MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - arp_ask(ifp, ifp->gw); + arp_ask(ifp, ifp->gw); // unsolicited GW ARP request } else if (ifp->state == MG_TCPIP_STATE_UP) { MG_ERROR(("Link up")); srand((unsigned int) mg_millis()); } else if (ifp->state == MG_TCPIP_STATE_DOWN) { MG_ERROR(("Link down")); } + mg_tcpip_call(ifp, MG_TCPIP_EV_ST_CHG, &ifp->state); } static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst, @@ -5169,20 +5185,25 @@ static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; -// RFC-2131 #4.3.6, #4.4.1 +// RFC-2131 #4.3.6, #4.4.1; RFC-2132 #9.8 static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req, uint32_t ip_srv) { uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 55, 2, 1, 3, // GW and mask - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 54, 4, 0, 0, 0, 0, // DHCP server ID - 50, 4, 0, 0, 0, 0, // Requested IP - 255 // End of options + 53, 1, 3, // Type: DHCP request + 12, 3, 'm', 'i', 'p', // Host name: "mip" + 54, 4, 0, 0, 0, 0, // DHCP server ID + 50, 4, 0, 0, 0, 0, // Requested IP + 55, 2, 1, 3, 255, 255, // GW, mask [DNS] [SNTP] + 255 // End of options }; - memcpy(opts + 14, &ip_srv, sizeof(ip_srv)); - memcpy(opts + 20, &ip_req, sizeof(ip_req)); - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); + uint8_t addopts = 0; + memcpy(opts + 10, &ip_srv, sizeof(ip_srv)); + memcpy(opts + 16, &ip_req, sizeof(ip_req)); + if (ifp->enable_req_dns) opts[24 + addopts++] = 6; // DNS + if (ifp->enable_req_sntp) opts[24 + addopts++] = 42; // SNTP + opts[21] += addopts; + tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, + sizeof(opts) + addopts - 2, false); MG_DEBUG(("DHCP req sent")); } @@ -5278,7 +5299,7 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { } static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint32_t ip = 0, gw = 0, mask = 0, lease = 0; + uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0; uint8_t msgtype = 0, state = ifp->state; // perform size check first, then access fields uint8_t *p = pkt->dhcp->options, @@ -5291,6 +5312,12 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW memcpy(&gw, p + 2, sizeof(gw)); ip = pkt->dhcp->yiaddr; + } else if (ifp->enable_req_dns && p[0] == 6 && p[1] == sizeof(dns) && + p + 6 < end) { // DNS + memcpy(&dns, p + 2, sizeof(dns)); + } else if (ifp->enable_req_sntp && p[0] == 42 && p[1] == sizeof(sntp) && + p + 6 < end) { // SNTP + memcpy(&sntp, p + 2, sizeof(sntp)); } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease memcpy(&lease, p + 2, sizeof(lease)); lease = mg_ntohl(lease); @@ -5319,6 +5346,10 @@ static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint64_t rand; mg_random(&rand, sizeof(rand)); srand((unsigned int) (rand + mg_millis())); + if (ifp->enable_req_dns && dns != 0) + mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); + if (ifp->enable_req_sntp && sntp != 0) + mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp); } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew ifp->lease_expire = ifp->now + lease * 1000; MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); @@ -5626,6 +5657,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { c->is_connecting = 0; // Client connected settmout(c, MIP_TTYPE_KEEPALIVE); mg_call(c, MG_EV_CONNECT, NULL); // Let user know + if (c->is_tls_hs) mg_tls_handshake(c); } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { // mg_hexdump(pkt->raw.buf, pkt->raw.len); tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); @@ -5934,7 +5966,8 @@ void mg_connect_resolved(struct mg_connection *c) { if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { struct connstate *s = (struct connstate *) (c + 1); memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast - } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { + } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && + rem_ip != ifp->gw) { // skip if gw (onstatechange -> READY -> ARP) // If we're in the same LAN, fire an ARP lookup. MG_DEBUG(("%lu ARP lookup...", c->id)); arp_ask(ifp, rem_ip); @@ -6633,8 +6666,7 @@ void mg_rpc_add(struct mg_rpc **head, struct mg_str method, void (*fn)(struct mg_rpc_req *), void *fn_data) { struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc)); if (rpc != NULL) { - rpc->method.buf = mg_mprintf("%.*s", method.len, method.buf); - rpc->method.len = method.len; + rpc->method = mg_strdup(method); rpc->fn = fn; rpc->fn_data = fn_data; rpc->next = *head, *head = rpc; @@ -7126,6 +7158,12 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, #define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds #define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 +static uint64_t s_boot_timestamp = 0; // Updated by SNTP + +uint64_t mg_now(void) { + return mg_millis() + s_boot_timestamp; +} + static int64_t gettimestamp(const uint32_t *data) { uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); if (sec) sec -= SNTP_TIME_OFFSET; @@ -7133,7 +7171,7 @@ static int64_t gettimestamp(const uint32_t *data) { } int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { - int64_t res = -1; + int64_t epoch_milliseconds = -1; int mode = len > 0 ? buf[0] & 7 : 0; int version = len > 0 ? (buf[0] >> 3) & 7 : 0; if (len < 48) { @@ -7144,31 +7182,36 @@ int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { MG_ERROR(("%s", "server sent a kiss of death")); } else if (version == 4 || version == 3) { // int64_t ref = gettimestamp((uint32_t *) &buf[16]); - int64_t t0 = gettimestamp((uint32_t *) &buf[24]); - int64_t t1 = gettimestamp((uint32_t *) &buf[32]); - int64_t t2 = gettimestamp((uint32_t *) &buf[40]); - int64_t t3 = (int64_t) mg_millis(); - int64_t delta = (t3 - t0) - (t2 - t1); - MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta)); - res = t2 + delta / 2; + int64_t origin_time = gettimestamp((uint32_t *) &buf[24]); + int64_t receive_time = gettimestamp((uint32_t *) &buf[32]); + int64_t transmit_time = gettimestamp((uint32_t *) &buf[40]); + int64_t now = (int64_t) mg_millis(); + int64_t latency = (now - origin_time) - (transmit_time - receive_time); + epoch_milliseconds = transmit_time + latency / 2; + s_boot_timestamp = (uint64_t) (epoch_milliseconds - now); } else { MG_ERROR(("unexpected version: %d", version)); } - return res; + return epoch_milliseconds; } static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { + uint64_t *expiration_time = (uint64_t *) c->data; + if (ev == MG_EV_OPEN) { + *expiration_time = mg_millis() + 3000; // Store expiration time in 3s + } else if (ev == MG_EV_CONNECT) { + mg_sntp_request(c); + } else if (ev == MG_EV_READ) { int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); if (milliseconds > 0) { - MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); + s_boot_timestamp = (uint64_t) milliseconds - mg_millis(); mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); - MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000), - (unsigned) (milliseconds % 1000))); + MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); } - mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer - } else if (ev == MG_EV_CONNECT) { - mg_sntp_request(c); + // mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer + c->is_closing = 1; + } else if (ev == MG_EV_POLL) { + if (mg_millis() > *expiration_time) c->is_closing = 1; } else if (ev == MG_EV_CLOSE) { } (void) ev_data; @@ -7193,7 +7236,10 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fnd) { struct mg_connection *c = NULL; if (url == NULL) url = "udp://time.google.com:123"; - if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) c->pfn = sntp_cb; + if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) { + c->pfn = sntp_cb; + sntp_cb(c, MG_EV_OPEN, (void *) url); + } return c; } @@ -7478,17 +7524,27 @@ static void read_conn(struct mg_connection *c) { size_t len = c->recv.size - c->recv.len; long n = -1; if (c->is_tls) { - if (!ioalloc(c, &c->rtls)) return; - n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], - c->rtls.size - c->rtls.len); - if (n == MG_IO_ERR && c->rtls.len == 0) { - // Close only if we have fully drained both raw (rtls) and TLS buffers - c->is_closing = 1; - } else { - if (n > 0) c->rtls.len += (size_t) n; - if (c->is_tls_hs) mg_tls_handshake(c); - n = c->is_tls_hs ? (long) MG_IO_WAIT : mg_tls_recv(c, buf, len); + // Do not read to the raw TLS buffer if it already has enough. + // This is to prevent overflowing c->rtls if our reads are slow + if (c->rtls.len < 16 * 1024 + 40) { // TLS record, header, MAC, padding + if (!ioalloc(c, &c->rtls)) return; + n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], + c->rtls.size - c->rtls.len); + if (n == MG_IO_ERR) { + if (c->rtls.len == 0 || c->is_io_err) { + // Close only when we have fully drained both rtls and TLS buffers + c->is_closing = 1; // or there's nothing we can do about it. + } else { // TLS buffer is capped to max record size, mark and + c->is_io_err = 1; // give TLS a chance to process that. + } + } else { + if (n > 0) c->rtls.len += (size_t) n; + if (c->is_tls_hs) mg_tls_handshake(c); + } } + n = c->is_tls_hs ? (long) MG_IO_WAIT + : c->is_closing ? -1 + : mg_tls_recv(c, buf, len); } else { n = recv_raw(c, buf, len); } @@ -7555,8 +7611,9 @@ static void setsockopts(struct mg_connection *c) { void mg_connect_resolved(struct mg_connection *c) { int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; + int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP - c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket + c->fd = S2PTR(socket(af, type, proto)); // Create outbound socket c->is_resolving = 0; // Clear resolving flag if (FD(c) == MG_INVALID_SOCKET) { mg_error(c, "socket(): %d", MG_SOCK_ERR(-1)); @@ -7709,15 +7766,15 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { n = 0; for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { c->is_readable = c->is_writable = 0; + if (c->is_closing) ms = 1; if (skip_iotest(c)) { // Socket not valid, ignore - } else if (c->rtls.len > 0 || mg_tls_pending(c) > 0) { - ms = 1; // Don't wait if TLS is ready } else { + // Don't wait if TLS is ready + if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1; fds[n].fd = FD(c); if (can_read(c)) fds[n].events |= POLLIN; if (can_write(c)) fds[n].events |= POLLOUT; - if (c->is_closing) ms = 1; n++; } } @@ -7733,8 +7790,6 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { if (skip_iotest(c)) { // Socket not valid, ignore - } else if (c->rtls.len > 0 || mg_tls_pending(c) > 0) { - c->is_readable = 1; } else { if (fds[n].revents & POLLERR) { mg_error(c, "socket error"); @@ -7766,7 +7821,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { if (can_write(c)) FD_SET(FD(c), &wset); if (c->rtls.len > 0 || mg_tls_pending(c) > 0) tvp = &tv_zero; if (FD(c) > maxfd) maxfd = FD(c); - if (c->is_closing) ms = 1; + if (c->is_closing) tvp = &tv_zero; } if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { @@ -7802,8 +7857,8 @@ static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) { *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1 usa[1] = usa[0]; - if ((sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && - (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET && + if ((sp[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && + (sp[1] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && bind(sp[0], &usa[0].sa, n) == 0 && // bind(sp[1], &usa[1].sa, n) == 0 && // getsockname(sp[0], &usa[0].sa, &n) == 0 && // @@ -7946,7 +8001,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) { if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { buf[len++] = (char) (ch & 0xff); buf[len] = '\0'; - if (sscanf(buf, "