5749 lines
211 KiB
C++
5749 lines
211 KiB
C++
// Copyright (c) 2004-2013 Sergey Lyubka
|
|
// Copyright (c) 2013-2025 Cesanta Software Limited
|
|
// All rights reserved
|
|
//
|
|
// This software is dual-licensed: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2 as
|
|
// published by the Free Software Foundation. For the terms of this
|
|
// license, see http://www.gnu.org/licenses/
|
|
//
|
|
// You are free to use this software under the terms of the GNU General
|
|
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// Alternatively, you can license this software under a commercial
|
|
// license, as set out in https://www.mongoose.ws/licensing/
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-only or commercial
|
|
|
|
#ifndef MONGOOSE_H
|
|
#define MONGOOSE_H
|
|
|
|
#define MG_VERSION "7.22"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
|
|
#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_config.h
|
|
#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
|
|
#define MG_ARCH_WIN32 2 // Windows
|
|
#define MG_ARCH_ESP32 3 // ESP32
|
|
#define MG_ARCH_ESP8266 4 // ESP8266
|
|
|
|
#define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!!
|
|
|
|
#define MG_ARCH_THREADX 6 // Eclipse ThreadX (former MS Azure RTOS)
|
|
#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
|
|
#define MG_ARCH_ARMGCC 8 // Plain ARM GCC
|
|
#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
|
|
#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
|
|
#define MG_ARCH_PICOSDK 11 // Raspberry Pi Pico-SDK (RP2040, RP2350)
|
|
#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
|
|
#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
|
|
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
|
|
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
|
|
#define MG_ARCH_CUBE 16 // STM32Cube environment
|
|
|
|
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
|
|
|
|
#if !defined(MG_ARCH)
|
|
#if defined(__unix__) || defined(__APPLE__)
|
|
#define MG_ARCH MG_ARCH_UNIX
|
|
#elif defined(_WIN32)
|
|
#define MG_ARCH MG_ARCH_WIN32
|
|
#endif
|
|
#endif // !defined(MG_ARCH)
|
|
|
|
#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
|
|
#include "mongoose_config.h" // keep this include
|
|
#endif
|
|
|
|
#if !defined(MG_ARCH)
|
|
#error "MG_ARCH is not specified and we couldn't guess it. Define MG_ARCH=... in mongoose_config.h"
|
|
#elif MG_ARCH == MG_ARCH_FREERTOS
|
|
#error "MG_ARCH_FREERTOS has been deprecated, set MG_ARCH=your_build_environment and MG_ENABLE_FREERTOS=1 instead"
|
|
#undef MG_ARCH // avoid errors piling up, provide a clean environment so the error above is seen
|
|
#define MG_ARCH MG_ARCH_ARMGCC
|
|
#define MG_ENABLE_FREERTOS 1
|
|
#endif
|
|
|
|
// http://esr.ibiblio.org/?p=5095
|
|
#define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ARMCGT
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#define MG_PATH_MAX 100
|
|
#define MG_ENABLE_DIRLIST 0
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ARMGCC
|
|
#define _POSIX_TIMERS
|
|
|
|
#include <ctype.h>
|
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
|
#include <errno.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define MG_PATH_MAX 100
|
|
#define MG_ENABLE_DIRLIST 0
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_CUBE
|
|
|
|
#include <ctype.h>
|
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
|
#include <errno.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
// Cube-generated header, includes ST Cube HAL
|
|
// NOTE: use angle brackets to prevent amalgamator ditching it
|
|
#include <main.h>
|
|
|
|
#ifndef MG_PATH_MAX
|
|
#define MG_PATH_MAX 100
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_DIRLIST
|
|
#define MG_ENABLE_DIRLIST 0
|
|
#endif
|
|
|
|
|
|
#ifndef MG_ENABLE_TCPIP
|
|
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
|
|
#endif
|
|
|
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
|
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
|
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
|
!defined(MG_ENABLE_DRIVER_ST67W6)
|
|
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
|
#define MG_ENABLE_DRIVER_STM32F 1
|
|
#elif defined(STM32H5) || defined(STM32H7)
|
|
#define MG_ENABLE_DRIVER_STM32H 1
|
|
#elif defined(STM32N6)
|
|
#define MG_ENABLE_DRIVER_STM32N 1
|
|
#else
|
|
#error Select a driver in mongoose_config.h
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MG_TLS
|
|
#define MG_TLS MG_TLS_BUILTIN
|
|
#endif
|
|
|
|
#if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || \
|
|
defined(STM32F4) || defined(STM32F7)
|
|
#define MG_OTA MG_OTA_STM32F
|
|
#elif !defined(MG_OTA) && defined(STM32H5)
|
|
#define MG_OTA MG_OTA_STM32H5
|
|
#elif !defined(MG_OTA) && defined(STM32H7)
|
|
#define MG_OTA MG_OTA_STM32H7
|
|
#endif
|
|
|
|
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
|
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
|
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
|
// hangs for 10 s, the IWDG fires and the device rolls back.
|
|
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
|
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
|
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
|
#if defined(IWDG1) && !defined(IWDG)
|
|
#define MG_IWDG IWDG1
|
|
#else
|
|
#define MG_IWDG IWDG
|
|
#endif
|
|
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
|
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
#define MG_OTA_ROLLBACK_TIMER_START() \
|
|
do { \
|
|
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
|
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
|
MG_IWDG->PR = 6U; /* /256 */ \
|
|
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
|
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
|
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
|
} while (0)
|
|
#endif
|
|
|
|
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
|
#endif
|
|
|
|
// OTA state in a backup register that survives warm resets but clears on POR.
|
|
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
|
// and unlocks backup domain write access via direct register writes, so it is
|
|
// safe to call from any context (before or after HAL_Init).
|
|
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
|
#ifndef MG_OTA_STATE_GET
|
|
#if defined(PWR_DBPCR_DBP)
|
|
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
|
#elif defined(PWR_CR1_DBP)
|
|
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
|
#else
|
|
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
|
#endif
|
|
|
|
#if defined(TAMP)
|
|
#define MG_OTA_STATE_GET() \
|
|
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
|
#define MG_OTA_STATE_SET(v) \
|
|
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
|
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
|
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
#define MG_OTA_STATE_SET(v) \
|
|
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
(RTC->BKP0R = (uint32_t) (v)))
|
|
#else
|
|
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
#define MG_OTA_STATE_SET(v) \
|
|
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
|
(RTC->BKP0R = (uint32_t) (v)))
|
|
#endif
|
|
#endif
|
|
|
|
// use HAL-defined execute-in-ram section
|
|
#ifndef MG_IRAM
|
|
#define MG_IRAM __attribute__((section(".RamFunc")))
|
|
#endif
|
|
|
|
#ifndef MG_ETH_RAM
|
|
#define MG_ETH_RAM __attribute__((section(".eth_ram")))
|
|
#endif
|
|
|
|
#ifndef HAL_ICACHE_MODULE_ENABLED
|
|
#define HAL_ICACHE_IsEnabled() 0
|
|
#define HAL_ICACHE_Enable() (void) 0
|
|
#define HAL_ICACHE_Disable() (void) 0
|
|
#endif
|
|
|
|
#ifndef MG_SET_MAC_ADDRESS
|
|
// Construct MAC address from UUID
|
|
#define MGUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID
|
|
#define MG_SET_MAC_ADDRESS(mac) \
|
|
do { \
|
|
int icache_enabled_ = HAL_ICACHE_IsEnabled(); \
|
|
if (icache_enabled_) HAL_ICACHE_Disable(); \
|
|
mac[0] = 42; \
|
|
mac[1] = ((MGUID[0] >> 0) & 255) ^ ((MGUID[2] >> 19) & 255); \
|
|
mac[2] = ((MGUID[0] >> 10) & 255) ^ ((MGUID[1] >> 10) & 255); \
|
|
mac[3] = (MGUID[0] >> 19) & 255; \
|
|
mac[4] = ((MGUID[1] >> 0) & 255) ^ ((MGUID[2] >> 10) & 255); \
|
|
mac[5] = ((MGUID[2] >> 0) & 255) ^ ((MGUID[1] >> 19) & 255); \
|
|
if (icache_enabled_) HAL_ICACHE_Enable(); \
|
|
} while (0)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ESP32
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <netdb.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#include <esp_ota_ops.h> // Use angle brackets to avoid
|
|
#include <esp_timer.h> // amalgamation ditching them
|
|
#include <esp_system.h>
|
|
#include <esp_mac.h>
|
|
|
|
#define MG_PATH_MAX 128
|
|
|
|
#ifndef MG_ENABLE_POSIX_FS
|
|
#define MG_ENABLE_POSIX_FS 1
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_DIRLIST
|
|
#define MG_ENABLE_DIRLIST 1
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ESP8266
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <netdb.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#include <esp_system.h>
|
|
|
|
#define MG_PATH_MAX 128
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_PICOSDK
|
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
|
#include <errno.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <pico/stdlib.h>
|
|
#include <pico/rand.h>
|
|
#include <pico/unique_id.h>
|
|
int mkdir(const char *, mode_t);
|
|
|
|
#if MG_OTA == MG_OTA_PICOSDK
|
|
#include <hardware/flash.h>
|
|
#include <hardware/watchdog.h>
|
|
#include <hardware/structs/watchdog.h>
|
|
#include <pico/bootrom.h>
|
|
|
|
#ifndef MG_OTA_WATCHDOG_MS
|
|
#if PICO_RP2040
|
|
#define MG_OTA_WATCHDOG_MS 8000U // max delay on RP2040 is 8.3 seconds
|
|
#else
|
|
#define MG_OTA_WATCHDOG_MS 10000U // max delay on RP2350 is 16 seconds
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
#define MG_OTA_ROLLBACK_TIMER_START() watchdog_enable(MG_OTA_WATCHDOG_MS, false)
|
|
#endif
|
|
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
#define MG_OTA_ROLLBACK_TIMER_FEED() watchdog_update()
|
|
#endif
|
|
|
|
#define MG_OTA_STATE_GET() ((uint32_t) watchdog_hw->scratch[0])
|
|
#define MG_OTA_STATE_SET(v) \
|
|
(watchdog_hw->scratch[0] = (uint32_t) (v), (uint32_t) watchdog_hw->scratch[0])
|
|
|
|
#if PICO_RP2040
|
|
#define MG_OTA_ROLLBACK() \
|
|
do { \
|
|
mg_flash->swap_fn(); \
|
|
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; \
|
|
} while (0)
|
|
#else
|
|
#define MG_OTA_ROLLBACK() \
|
|
do { \
|
|
mg_flash->swap_fn(); \
|
|
((rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT))( \
|
|
BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); \
|
|
} while (0)
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_RTTHREAD
|
|
|
|
#include <rtthread.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#ifndef MG_IO_SIZE
|
|
#define MG_IO_SIZE 1460
|
|
#endif
|
|
|
|
#endif // MG_ARCH == MG_ARCH_RTTHREAD
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \
|
|
MG_ARCH == MG_ARCH_CMSIS_RTOS2
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <alloca.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#if MG_ARCH == MG_ARCH_CMSIS_RTOS1
|
|
#include "cmsis_os.h" // keep this include
|
|
// https://developer.arm.com/documentation/ka003821/latest
|
|
extern uint32_t rt_time_get(void);
|
|
#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
|
|
#include "cmsis_os2.h" // keep this include
|
|
#endif
|
|
|
|
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
|
|
|
|
#if defined(__ARMCC_VERSION)
|
|
#define mode_t size_t
|
|
#if !defined(MG_ENABLE_POSIX_FS) || !MG_ENABLE_POSIX_FS
|
|
#else
|
|
#define mkdir(a, b) mg_mkdir(a, b)
|
|
static inline int mg_mkdir(const char *path, mode_t mode) {
|
|
(void) path, (void) mode;
|
|
return -1;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \
|
|
!defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
|
|
(!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP)
|
|
#define MG_ENABLE_RL 1
|
|
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
|
|
#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_THREADX
|
|
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
// Do not include time.h and stdlib.h, since they conflict with nxd_bsd.h
|
|
// extern time_t time(time_t *);
|
|
#include <nxd_bsd.h>
|
|
|
|
#define MG_DIRSEP '\\'
|
|
#undef FOPEN_MAX
|
|
|
|
#ifndef MG_PATH_MAX
|
|
#define MG_PATH_MAX 32
|
|
#endif
|
|
|
|
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
|
|
#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_IPV6
|
|
#define MG_ENABLE_IPV6 0
|
|
#endif
|
|
|
|
#define socklen_t int
|
|
#define closesocket(x) soc_close(x)
|
|
|
|
// In order to enable BSD support in NetxDuo, do the following (assuming Cube):
|
|
// 1. Add nxd_bsd.h and nxd_bsd.c to the repo:
|
|
// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.c
|
|
// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.h
|
|
// 2. Add to tx_user.h
|
|
// #define TX_THREAD_USER_EXTENSION int bsd_errno;
|
|
// 3. Add to nx_user.h
|
|
// #define NX_ENABLE_EXTENDED_NOTIFY_SUPPORT
|
|
// 4. Add __CCRX__ build preprocessor constant
|
|
// Project -> Properties -> C/C++ -> Settings -> MCU Compiler -> Preprocessor
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_TIRTOS
|
|
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <serrno.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <ti/sysbios/knl/Clock.h>
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_UNIX
|
|
|
|
#define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors
|
|
|
|
#if defined(__APPLE__)
|
|
#include <mach/mach_time.h>
|
|
#endif
|
|
|
|
#if !defined(MG_ENABLE_EPOLL) && defined(__linux__)
|
|
#define MG_ENABLE_EPOLL 1
|
|
#elif !defined(MG_ENABLE_POLL)
|
|
#define MG_ENABLE_POLL 1
|
|
#endif
|
|
|
|
#include <arpa/inet.h>
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
|
#include <errno.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL
|
|
#include <sys/epoll.h>
|
|
#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL
|
|
#include <poll.h>
|
|
#else
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef MG_ENABLE_DIRLIST
|
|
#define MG_ENABLE_DIRLIST 1
|
|
#endif
|
|
|
|
#ifndef MG_PATH_MAX
|
|
#define MG_PATH_MAX FILENAME_MAX
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_POSIX_FS
|
|
#define MG_ENABLE_POSIX_FS 1
|
|
#endif
|
|
|
|
#ifndef MG_IO_SIZE
|
|
#define MG_IO_SIZE 16384
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_WIN32
|
|
|
|
// Avoid name clashing; (macro expansion producing 'defined' has undefined
|
|
// behaviour). See config.h for user options
|
|
#ifndef MG_ENABLE_WINSOCK
|
|
#if (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) && \
|
|
(!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
|
|
(!defined(MG_ENABLE_FREERTOS_TCP) || !MG_ENABLE_FREERTOS_TCP)
|
|
#define MG_ENABLE_WINSOCK 1
|
|
#else
|
|
#define MG_ENABLE_WINSOCK 0
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef _CRT_RAND_S
|
|
#define _CRT_RAND_S
|
|
#endif
|
|
|
|
#ifndef _WIN32_WINNT
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define _WIN32_WINNT 0x0400 // Let vc98 pick up wincrypt.h
|
|
#else
|
|
#define _WIN32_WINNT 0x0600
|
|
#endif
|
|
#endif
|
|
#ifndef WINVER
|
|
#define WINVER _WIN32_WINNT
|
|
#endif
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <direct.h>
|
|
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
|
|
#include <errno.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#include <winsock2.h> // fix missing macros and types
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#define __func__ ""
|
|
typedef __int64 int64_t;
|
|
typedef unsigned __int64 uint64_t;
|
|
typedef unsigned char uint8_t;
|
|
typedef char int8_t;
|
|
typedef unsigned short uint16_t;
|
|
typedef short int16_t;
|
|
typedef unsigned int uint32_t;
|
|
typedef int int32_t;
|
|
typedef enum { false = 0, true = 1 } bool;
|
|
#define inline __inline
|
|
#else
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#if MG_ENABLE_WINSOCK
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <process.h>
|
|
#include <winerror.h>
|
|
|
|
// For mg_random()
|
|
#if defined(_MSC_VER) && _MSC_VER < 1700
|
|
#include <wincrypt.h>
|
|
#pragma comment(lib, "advapi32.lib")
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
|
#ifndef IPPROTO_IP
|
|
#define IPPROTO_IP 0
|
|
#endif
|
|
|
|
#ifndef IP_ADD_MEMBERSHIP
|
|
struct ip_mreq {
|
|
struct in_addr imr_multiaddr;
|
|
struct in_addr imr_interface;
|
|
};
|
|
#define IP_ADD_MEMBERSHIP 12
|
|
#endif
|
|
#endif
|
|
|
|
// Protect from calls like std::snprintf in app code
|
|
// See https://github.com/cesanta/mongoose/issues/1047
|
|
#ifndef __cplusplus
|
|
#define snprintf _snprintf
|
|
#define vsnprintf _vsnprintf
|
|
#ifndef strdup // For MSVC with _DEBUG, see #1359
|
|
#define strdup(x) _strdup(x)
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(MG_ENABLE_POLL) && MG_ENABLE_POLL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
|
|
typedef unsigned long nfds_t; // see #3388
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#if MG_ENABLE_WINSOCK
|
|
#pragma comment(lib, "ws2_32.lib")
|
|
#endif
|
|
#ifndef alloca
|
|
#define alloca(a) _alloca(a)
|
|
#endif
|
|
#endif
|
|
|
|
#define MG_DIRSEP '\\'
|
|
|
|
#ifndef MG_PATH_MAX
|
|
#define MG_PATH_MAX FILENAME_MAX
|
|
#endif
|
|
|
|
#if MG_ENABLE_WINSOCK
|
|
|
|
#define MG_INVALID_SOCKET INVALID_SOCKET
|
|
#define MG_SOCKET_TYPE SOCKET
|
|
#define poll(a, b, c) WSAPoll((a), (b), (c))
|
|
#define closesocket(x) closesocket(x)
|
|
typedef int socklen_t;
|
|
|
|
#ifndef SO_EXCLUSIVEADDRUSE
|
|
#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR))
|
|
#endif
|
|
|
|
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0)
|
|
|
|
#define MG_SOCK_PENDING(errcode) \
|
|
(((errcode) < 0) && \
|
|
(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \
|
|
WSAGetLastError() == WSAEWOULDBLOCK))
|
|
|
|
#define MG_SOCK_RESET(errcode) \
|
|
(((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET))
|
|
|
|
#endif // MG_ENABLE_WINSOCK
|
|
|
|
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
|
#define sleep(x) Sleep((x) *1000)
|
|
#define mkdir(a, b) _mkdir(a)
|
|
#define rmdir(a) _rmdir(a)
|
|
#define timegm(x) _mkgmtime(x)
|
|
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_DIRLIST
|
|
#define MG_ENABLE_DIRLIST 1
|
|
#endif
|
|
|
|
#ifndef SIGPIPE
|
|
#define SIGPIPE 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_POSIX_FS
|
|
#define MG_ENABLE_POSIX_FS 1
|
|
#endif
|
|
|
|
#ifndef MG_IO_SIZE
|
|
#define MG_IO_SIZE 16384
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ARCH == MG_ARCH_ZEPHYR
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/net/socket.h>
|
|
// #include <zephyr/posix/dirent.h>
|
|
#include <zephyr/posix/fcntl.h>
|
|
#include <zephyr/posix/sys/select.h>
|
|
#include <zephyr/random/random.h>
|
|
#include <zephyr/version.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#define MG_PUTCHAR(x) printk("%c", x)
|
|
#ifndef strdup
|
|
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
|
|
#endif
|
|
#define strerror(x) zsock_gai_strerror(x)
|
|
|
|
#ifndef FD_CLOEXEC
|
|
#define FD_CLOEXEC 0
|
|
#endif
|
|
|
|
#ifndef F_SETFD
|
|
#define F_SETFD 0
|
|
#endif
|
|
|
|
#define MG_ENABLE_SSI 0
|
|
|
|
int rand(void);
|
|
int sscanf(const char *, const char *, ...);
|
|
|
|
#endif
|
|
|
|
|
|
#if defined(MG_ENABLE_FREERTOS) && MG_ENABLE_FREERTOS
|
|
|
|
#include <FreeRTOS.h>
|
|
#include <task.h>
|
|
|
|
#define MG_ENABLE_CUSTOM_CALLOC 1
|
|
|
|
static inline void mg_free(void *ptr) {
|
|
vPortFree(ptr);
|
|
}
|
|
|
|
// Re-route calloc/free to FreeRTOS functions, don't use stdlib
|
|
static inline void *mg_calloc(size_t cnt, size_t size) {
|
|
void *p = pvPortMalloc(cnt * size);
|
|
if (p != NULL) memset(p, 0, size * cnt);
|
|
return p;
|
|
}
|
|
|
|
#endif // MG_ENABLE_FREERTOS
|
|
|
|
|
|
#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP
|
|
|
|
#include <limits.h>
|
|
#include <list.h>
|
|
|
|
#include <FreeRTOS_IP.h>
|
|
#include <FreeRTOS_Sockets.h>
|
|
|
|
#define MG_SOCKET_TYPE Socket_t
|
|
#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET
|
|
|
|
// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
|
|
// with FreeRTOS_ prefix, is beyond me
|
|
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
|
|
#define IPPROTO_UDP FREERTOS_IPPROTO_UDP
|
|
#define AF_INET FREERTOS_AF_INET
|
|
#define SOCK_STREAM FREERTOS_SOCK_STREAM
|
|
#define SOCK_DGRAM FREERTOS_SOCK_DGRAM
|
|
#define SO_BROADCAST 0
|
|
#define SO_ERROR 0
|
|
#define SOL_SOCKET 0
|
|
#define SO_REUSEADDR 0
|
|
|
|
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
|
|
|
|
#define MG_SOCK_PENDING(errcode) \
|
|
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
|
|
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
|
|
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
|
|
(errcode) == -pdFREERTOS_ERRNO_EAGAIN)
|
|
|
|
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
|
|
|
|
// actually only if optional timeout is enabled
|
|
#define MG_SOCK_INTR(fd) (fd == NULL)
|
|
|
|
#define sockaddr_in freertos_sockaddr
|
|
#define sockaddr freertos_sockaddr
|
|
#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))
|
|
#define listen(a, b) FreeRTOS_listen((a), (b))
|
|
#define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
|
|
#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
|
|
#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
|
|
#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
|
|
#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
|
|
#define recvfrom(a, b, c, d, e, f) \
|
|
FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
|
|
#define closesocket(x) FreeRTOS_closesocket(x)
|
|
#define gethostbyname(x) FreeRTOS_gethostbyname(x)
|
|
#define getsockname(a, b, c) mg_getsockname((a), (b), (c))
|
|
#define getpeername(a, b, c) mg_getpeername((a), (b), (c))
|
|
|
|
static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
|
|
(void) fd, (void) buf, (void) len;
|
|
return -1;
|
|
}
|
|
|
|
static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
|
|
(void) fd, (void) buf, (void) len;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
|
|
|
|
#if defined(__GNUC__) && !defined(__ARMCC_VERSION)
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
struct timeval;
|
|
|
|
#include <lwip/sockets.h>
|
|
|
|
#if !LWIP_TIMEVAL_PRIVATE
|
|
#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both
|
|
#include <sys/time.h>
|
|
#else
|
|
struct timeval {
|
|
time_t tv_sec;
|
|
long tv_usec;
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
#if LWIP_SOCKET != 1
|
|
// Sockets support disabled in LWIP by default
|
|
#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
|
|
#include <rl_net.h>
|
|
|
|
#define closesocket(x) closesocket(x)
|
|
|
|
#define TCP_NODELAY SO_KEEPALIVE
|
|
|
|
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
|
|
|
|
#define MG_SOCK_PENDING(errcode) \
|
|
((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \
|
|
(errcode) == BSD_EINPROGRESS)
|
|
|
|
#define MG_SOCK_RESET(errcode) \
|
|
((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET)
|
|
|
|
// In blocking mode, which is enabled by default, accept() waits for a
|
|
// connection request. In non blocking mode, you must call accept()
|
|
// again if the error code BSD_EWOULDBLOCK is returned.
|
|
#define MG_SOCK_INTR(fd) (fd == BSD_EWOULDBLOCK)
|
|
|
|
#define socklen_t int
|
|
#endif
|
|
|
|
|
|
#ifndef MG_ENABLE_LOG
|
|
#define MG_ENABLE_LOG 1
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_CUSTOM_CALLOC
|
|
#define MG_ENABLE_CUSTOM_CALLOC 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_CUSTOM_LOG
|
|
#define MG_ENABLE_CUSTOM_LOG 0 // Let user define their own MG_LOG
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_FREERTOS
|
|
#define MG_ENABLE_FREERTOS 0 // FreeRTOS RTOS
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_TCPIP
|
|
#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_BSD_SOCKETS
|
|
#define MG_ENABLE_BSD_SOCKETS 0 // BSD API support for built-in stack
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_BSD_PROTOTYPES
|
|
#define MG_ENABLE_BSD_PROTOTYPES 1
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_LWIP
|
|
#define MG_ENABLE_LWIP 0 // lwIP network stack
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_FREERTOS_TCP
|
|
#define MG_ENABLE_FREERTOS_TCP 0 // Amazon FreeRTOS-TCP network stack
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_RL
|
|
#define MG_ENABLE_RL 0 // ARM MDK network stack
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_SOCKET
|
|
#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_POLL
|
|
#define MG_ENABLE_POLL 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_EPOLL
|
|
#define MG_ENABLE_EPOLL 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_FATFS
|
|
#define MG_ENABLE_FATFS 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_LFS
|
|
#define MG_ENABLE_LFS 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_SSI
|
|
#define MG_ENABLE_SSI 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_IPV6
|
|
#define MG_ENABLE_IPV6 0
|
|
#endif
|
|
|
|
#ifndef MG_IPV6_V6ONLY
|
|
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_MD5
|
|
#define MG_ENABLE_MD5 1
|
|
#endif
|
|
|
|
// Set MG_ENABLE_WINSOCK=0 for Win32 builds with other external IP stack not
|
|
// mentioned in arch_win32.h
|
|
#ifndef MG_ENABLE_WINSOCK
|
|
#define MG_ENABLE_WINSOCK 1
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_DIRLIST
|
|
#define MG_ENABLE_DIRLIST 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_CUSTOM_RANDOM
|
|
#define MG_ENABLE_CUSTOM_RANDOM 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_CUSTOM_MILLIS
|
|
#define MG_ENABLE_CUSTOM_MILLIS 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_ASSERT
|
|
#define MG_ENABLE_ASSERT 0
|
|
#endif
|
|
|
|
#ifndef MG_IO_SIZE
|
|
#define MG_IO_SIZE 512 // Granularity of the send/recv IO buffer growth
|
|
#endif
|
|
|
|
#ifndef MG_MAX_RECV_SIZE
|
|
#define MG_MAX_RECV_SIZE (3UL * 1024UL * 1024UL) // Maximum recv IO buffer size
|
|
#endif
|
|
|
|
#ifndef MG_DATA_SIZE
|
|
#define MG_DATA_SIZE 32 // struct mg_connection :: data size
|
|
#endif
|
|
|
|
#ifndef MG_MAX_HTTP_HEADERS
|
|
#define MG_MAX_HTTP_HEADERS 30
|
|
#endif
|
|
|
|
#ifndef MG_HTTP_INDEX
|
|
#define MG_HTTP_INDEX "index.html"
|
|
#endif
|
|
|
|
#ifndef MG_PATH_MAX
|
|
#ifdef PATH_MAX
|
|
#define MG_PATH_MAX PATH_MAX
|
|
#else
|
|
#define MG_PATH_MAX 128
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
|
|
#define MG_SOCK_LISTEN_BACKLOG_SIZE 128
|
|
#endif
|
|
|
|
#ifndef MG_DIRSEP
|
|
#define MG_DIRSEP '/'
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_POSIX_FS
|
|
#define MG_ENABLE_POSIX_FS 0
|
|
#endif
|
|
|
|
#ifndef MG_INVALID_SOCKET
|
|
#define MG_INVALID_SOCKET (-1)
|
|
#endif
|
|
|
|
#ifndef MG_SOCKET_TYPE
|
|
#define MG_SOCKET_TYPE int
|
|
#endif
|
|
|
|
#ifndef MG_SOCKET_ERRNO
|
|
#define MG_SOCKET_ERRNO errno
|
|
#endif
|
|
|
|
#if MG_ENABLE_EPOLL
|
|
#define MG_EPOLL_ADD(c) \
|
|
do { \
|
|
struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
|
|
epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \
|
|
} while (0)
|
|
#define MG_EPOLL_MOD(c, wr) \
|
|
do { \
|
|
struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
|
|
if (wr) ev.events |= EPOLLOUT; \
|
|
epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \
|
|
} while (0)
|
|
#else
|
|
#define MG_EPOLL_ADD(c)
|
|
#define MG_EPOLL_MOD(c, wr)
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_PROFILE
|
|
#define MG_ENABLE_PROFILE 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_TCPIP_DRIVER_INIT // mg_mgr_init() will also initialize
|
|
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
|
|
#endif // Mongoose built-in network stack
|
|
|
|
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
|
|
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_MASK
|
|
#define MG_TCPIP_MASK MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_GW
|
|
#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
|
|
#endif
|
|
|
|
#if MG_ENABLE_IPV6
|
|
|
|
#ifndef MG_TCPIP_GLOBAL
|
|
#define MG_TCPIP_GLOBAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_IPV6_LINKLOCAL
|
|
#define MG_TCPIP_IPV6_LINKLOCAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_PREFIX_LEN
|
|
#define MG_TCPIP_PREFIX_LEN 0
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_GW6
|
|
#define MG_TCPIP_GW6 MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifndef MG_SET_MAC_ADDRESS
|
|
#define MG_SET_MAC_ADDRESS(mac)
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_DHCPNAME_SIZE
|
|
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
|
|
#endif
|
|
|
|
#ifndef MG_SET_WIFI_CONFIG
|
|
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
|
|
#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0
|
|
#endif
|
|
|
|
#ifndef MG_ENABLE_CHACHA20
|
|
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Macros to record timestamped events that happens with a connection.
|
|
// They are saved into a c->prof IO buffer, each event is a name and a 32-bit
|
|
// timestamp in milliseconds since connection init time.
|
|
//
|
|
// Test (run in two separate terminals):
|
|
// make -C tutorials/http/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1
|
|
// curl localhost:8000
|
|
// Output:
|
|
// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile:
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE
|
|
// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE
|
|
//
|
|
// Usage:
|
|
// Enable profiling by setting MG_ENABLE_PROFILE=1
|
|
// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure
|
|
|
|
#if MG_ENABLE_PROFILE
|
|
struct mg_profitem {
|
|
const char *name; // Event name
|
|
uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN)
|
|
};
|
|
|
|
#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc
|
|
|
|
// Adding a profile item to the c->prof. Must be as fast as possible.
|
|
// Reallocation of the c->prof iobuf is not desirable here, that's why we
|
|
// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY.
|
|
// This macro just inits and copies 8 bytes, and calls mg_millis(),
|
|
// which should be fast enough.
|
|
#define MG_PROF_ADD(c, name_) \
|
|
do { \
|
|
struct mg_iobuf *io = &c->prof; \
|
|
uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \
|
|
struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \
|
|
mg_iobuf_add(io, io->len, &item, sizeof(item)); \
|
|
} while (0)
|
|
|
|
// Initialising profile for a new connection. Not time sensitive
|
|
#define MG_PROF_INIT(c) \
|
|
do { \
|
|
struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \
|
|
mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \
|
|
mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \
|
|
} while (0)
|
|
|
|
#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof)
|
|
|
|
// Dumping the profile. Not time sensitive
|
|
#define MG_PROF_DUMP(c) \
|
|
do { \
|
|
struct mg_iobuf *io = &c->prof; \
|
|
struct mg_profitem *p = (struct mg_profitem *) io->buf; \
|
|
struct mg_profitem *e = &p[io->len / sizeof(*p)]; \
|
|
MG_INFO(("%lu profile:", c->id)); \
|
|
while (p < e) { \
|
|
MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \
|
|
p++; \
|
|
} \
|
|
} while (0)
|
|
|
|
#else
|
|
#define MG_PROF_INIT(c)
|
|
#define MG_PROF_FREE(c)
|
|
#define MG_PROF_ADD(c, name)
|
|
#define MG_PROF_DUMP(c)
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// A non-owning view of an arbitrary byte buffer. Not NUL-terminated in general.
|
|
// Use mg_str() or mg_str_n() to construct; use mg_strdup() for an owned copy.
|
|
struct mg_str {
|
|
char *buf; // Pointer to the data (not necessarily NUL-terminated)
|
|
size_t len; // Number of bytes
|
|
};
|
|
|
|
// Using macro to avoid shadowing the C++ struct constructor, see #1298
|
|
#define mg_str(s) mg_str_s(s)
|
|
|
|
// Creates an mg_str from a NUL-terminated C string. Computes length via
|
|
// strlen. Returns {NULL, 0} when s is NULL.
|
|
struct mg_str mg_str(const char *s);
|
|
|
|
// Creates an mg_str from a pointer and explicit byte count. s need not be
|
|
// NUL-terminated. Does not copy the data.
|
|
struct mg_str mg_str_n(const char *s, size_t n);
|
|
|
|
// Case-insensitive strcmp for NUL-terminated C strings.
|
|
// Returns 0 if equal, <0 if s1 < s2, >0 if s1 > s2.
|
|
int mg_casecmp(const char *s1, const char *s2);
|
|
|
|
// Compares two mg_str by content and length. Returns 0 if identical,
|
|
// -1 if str1 < str2, 1 if str1 > str2. Length is used as a tiebreaker
|
|
// when one string is a prefix of the other.
|
|
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
|
|
|
|
// Case-insensitive mg_strcmp.
|
|
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2);
|
|
|
|
// Returns a heap-allocated, NUL-terminated copy of s.
|
|
// Returns {NULL, 0} on OOM or if s.buf is NULL or s.len is 0.
|
|
// Caller must mg_free(result.buf) when done.
|
|
struct mg_str mg_strdup(const struct mg_str s);
|
|
|
|
// Matches a string against a glob pattern.
|
|
//
|
|
// Returns:
|
|
// True if the whole str matches pattern.
|
|
// Example:
|
|
// if (mg_match(hm->uri, mg_str("/api/#"), NULL)) { ... }
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/http/link-checker,
|
|
// tutorials/websocket/websocket-server
|
|
// Related APIs:
|
|
// mg_str(), mg_str_n(), mg_span(), mg_strcmp(), mg_strcasecmp()
|
|
// Notes:
|
|
// Pattern wildcards: ? matches one character, * matches any sequence except
|
|
// '/', and # matches any sequence including '/'. If caps is not NULL, each
|
|
// wildcard capture is stored as a zero-copy mg_str slice into str. Pass
|
|
// caps=NULL when captures are not needed. The caps array size should be
|
|
// at least the number of wildcards in a pattern plus one. The last cap
|
|
// is initialised to an empty string.
|
|
// Attention: the pattern must consume the whole str in order to match.
|
|
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
|
|
|
|
// Splits s at the first occurrence of sep. Sets *a to the part before sep
|
|
// and *b to the part after it (sep is not included in either). Both slices
|
|
// are zero-copy views into s. a or b may be NULL if that half is not needed.
|
|
// If sep is not found, *a is the whole string and *b is empty.
|
|
// Returns false if s is empty or s.buf is NULL.
|
|
// Example - scan comma-separated key=value pairs:
|
|
// ```c
|
|
// struct mg_str entry, key, val;
|
|
// struct mg_str s = mg_str("a=333,b=777,hello");
|
|
//
|
|
// while (mg_span(s, &entry, &s, ',')) {
|
|
// mg_span(entry, &key, &val, '=');
|
|
// printf("key: %.*s, val: %.*s\n", (int) key.len, key.buf, (int) val.len, val.buf);
|
|
// }
|
|
// ```
|
|
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep);
|
|
|
|
// Parses str as an unsigned integer into the val_len-byte value at *val.
|
|
// val_len must be 1, 2, 4, or 8 (for uint8_t, uint16_t, uint32_t, uint64_t).
|
|
// base must be 2, 10, or 16; pass 0 to auto-detect from a "0b" or "0x" prefix.
|
|
// The entire string must consist of valid digits with no trailing characters.
|
|
// Returns false on overflow, invalid input, unsupported base, or unsupported val_len.
|
|
bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len);
|
|
|
|
|
|
|
|
|
|
// Lock-free, single-producer / single-consumer (SPSC) message queue.
|
|
// Safe to write from an ISR and read from the main loop without locking,
|
|
// as long as only one context writes and one context reads at a time.
|
|
// Each message is stored with an internal 32-bit length prefix; the buf
|
|
// pointers returned by mg_queue_book() and mg_queue_next() already skip it.
|
|
//
|
|
// Producer pattern:
|
|
// char buf[1024];
|
|
// if (mg_queue_book(&q, &buf, len) >= len) {
|
|
// memcpy(buf, data, len);
|
|
// mg_queue_add(&q, len);
|
|
// }
|
|
//
|
|
// Consumer pattern:
|
|
// char *buf;
|
|
// size_t len = mg_queue_next(&q, &buf);
|
|
// if (len > 0) { process(buf, len); mg_queue_del(&q, len); }
|
|
|
|
struct mg_queue {
|
|
char *buf; // Caller-supplied backing buffer
|
|
size_t size; // Size of buf in bytes
|
|
volatile size_t tail; // Read offset; updated by the consumer
|
|
volatile size_t head; // Write offset; updated by the producer
|
|
};
|
|
|
|
// Initialises q to use the caller-supplied buf/size. buf must remain valid
|
|
// for the lifetime of the queue.
|
|
void mg_queue_init(struct mg_queue *q, char *buf, size_t size);
|
|
|
|
// Reserves space for a message of len bytes. Sets *buf to the write location.
|
|
// Returns the number of bytes available (>= len means the reservation
|
|
// succeeded). Does NOT commit the message; call mg_queue_add() after writing.
|
|
size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len);
|
|
|
|
// Commits the most recently reserved message of len bytes. Must be called
|
|
// with the same len passed to the preceding mg_queue_book() call, after
|
|
// the data has been written into the buffer it returned.
|
|
void mg_queue_add(struct mg_queue *q, size_t len);
|
|
|
|
// Returns the length of the oldest unconsumed message and sets *buf to its
|
|
// start. Returns 0 if the queue is empty. Does NOT remove the message;
|
|
// call mg_queue_del() after processing to advance the read position.
|
|
size_t mg_queue_next(struct mg_queue *q, char **buf);
|
|
|
|
// Removes the oldest message of len bytes. Must be called with the same len
|
|
// returned by the preceding mg_queue_next() call.
|
|
void mg_queue_del(struct mg_queue *q, size_t len);
|
|
|
|
|
|
|
|
|
|
// Character output function. Called once per output character by mg_xprintf/mg_vxprintf.
|
|
// arg is an arbitrary user pointer passed through from the caller.
|
|
typedef void (*mg_pfn_t)(char, void *arg);
|
|
|
|
// Custom %M/%m printer. Reads its own arguments from ap and writes via fn.
|
|
// Returns the number of characters printed (excluding the quotes added by %m).
|
|
typedef size_t (*mg_pm_t)(mg_pfn_t fn, void *arg, va_list *);
|
|
|
|
// Formats fmt with printf-like specifiers, calling fn for each output character.
|
|
// Returns the total number of characters printed. Supported specifiers:
|
|
// - %c - expect char
|
|
// - %f, %g - expect double
|
|
// - %hhd, %hd, %d, %ld, %lld - for char, short, int, long, int64_t
|
|
// - %hhu, %hu, %u, %lu, %llu - same but for unsigned variants
|
|
// - %hhx, %hx, %x, %lx, %llx - same, unsigned and hex output
|
|
// - %p - expects any pointer, prints 0x..... hex value
|
|
// - %s - expects char *
|
|
// - %% - prints the % character itself
|
|
// - %X.Y - optional width and precision modifiers (e.g.: %1.2d)
|
|
// - %.* - optional precision modifier, expected as int argument (e.g.: %.*d)
|
|
// - %M - (EXTENSION) expects a pointer to a custom print function and its arguments
|
|
// - %m - (EXTENSION) exactly like %M but double-quotes the output
|
|
// - %M/%m (custom mg_pm_t printer; %m additionally wraps the output in quotes).
|
|
//
|
|
// There are higher level wrappers around these functions that use
|
|
// predefined output functions, and can print to char buffer, or connection
|
|
// send iobuffer, etc etc. - for example, mg_snrptinf(). The %M/%m non-standard
|
|
// specifier allows to use custom formtter. There are several pre-defined
|
|
// formatters:
|
|
// - mg_print_base64 - prints a buffer as a base64-encoded string
|
|
// - mg_print_esc - prints a JSON-escaped string
|
|
// - mg_print_hex - prints a buffer as a hex string
|
|
// - mg_print_ip - prints an IP address in a struct mg_str
|
|
// - mg_print_ip_port - prints IP address and port in a struct mg_str
|
|
// - mg_print_ip4 - prints an IPv4 address
|
|
// - mg_print_ip6 - prints an IPv6 address
|
|
// - mg_print_mac - prints a MAC address
|
|
//
|
|
// Usage example:
|
|
//
|
|
// ```c
|
|
// struct a { int a, b; };
|
|
//
|
|
// size_t print_a(mg_pfn_t fn, void *arg, va_list *ap) {
|
|
// struct a *p = va_arg(*ap, struct a *);
|
|
// return mg_xprintf(fn, arg, "{%m:%d}", MG_ESC("a"), p->a);
|
|
// }
|
|
//
|
|
// struct a temp = { 42, 43 };
|
|
// char buf[100];
|
|
//
|
|
// mg_snprintf(buf, sizeof(buf), "%M", print_a, &temp); // {"a":42}
|
|
// ```
|
|
size_t mg_vxprintf(mg_pfn_t fn, void *arg, const char *fmt, va_list *);
|
|
size_t mg_xprintf(mg_pfn_t fn, void *arg, const char *fmt, ...);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Formats data into a caller-supplied buffer.
|
|
//
|
|
// Returns:
|
|
// Number of bytes that would be written if buf were large enough, like
|
|
// snprintf().
|
|
// Example:
|
|
// char buf[20];
|
|
// mg_snprintf(buf, sizeof(buf), "{%m:%d}", MG_ESC("status"), 1);
|
|
// Full examples:
|
|
// tutorials/http/link-checker, tutorials/http/redirect-to-https,
|
|
// tutorials/mqtt/mqtt-dashboard/device
|
|
// Related APIs:
|
|
// mg_xprintf(), mg_printf(), mg_mprintf(), MG_ESC
|
|
// Notes:
|
|
// These functions are just wrappers around mg_xprintf().
|
|
// Call with buf=NULL and len=0 to measure. NUL-terminates if the result fits
|
|
// (return value < len). Supports mg_xprintf specifiers, including custom
|
|
// %M/%m printers. Use MG_ESC when printing JSON strings.
|
|
size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
|
|
size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...);
|
|
|
|
// Formats into a heap-allocated NUL-terminated string. Caller must mg_free() it.
|
|
char *mg_vmprintf(const char *fmt, va_list *ap);
|
|
char *mg_mprintf(const char *fmt, ...);
|
|
|
|
// Formats into a mg_queue slot. Returns bytes written, or 0 if no space.
|
|
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
|
|
|
|
// 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_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_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_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
|
|
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
|
|
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_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.
|
|
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed)
|
|
void mg_pfn_iobuf_noresize(char ch, void *param); // param: struct mg_iobuf * (fixed buffer, no resize)
|
|
void mg_pfn_stdout(char c, void *param); // param: ignored, writes to stdout
|
|
|
|
// Expands to the three arguments for %m to JSON-escape a string literal.
|
|
// Example: mg_snprintf(buf, len, "%m", MG_ESC("hello")) -> "\"hello\""
|
|
#define MG_ESC(str) mg_print_esc, 0, (str)
|
|
// Logging
|
|
//
|
|
// Mongoose provides a set of functions and macros for logging. The application can
|
|
// use these functions for its own purposes as well as the rest of Mongoose API.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Log levels
|
|
enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE };
|
|
|
|
// Set Mongoose logging level. Example: `mg_log_set(MG_LL_INFO);`
|
|
extern int mg_log_level; // Current log level, one of MG_LL_*
|
|
#define mg_log_set(level_) mg_log_level = (level_)
|
|
|
|
void mg_log(const char *fmt, ...);
|
|
void mg_log_prefix(int ll, const char *file, int line, const char *fname);
|
|
|
|
// Log a hex dump of binary data `buf`, `len`.
|
|
void mg_hexdump(const void *buf, size_t len);
|
|
|
|
// Set log printer function which prints one byte. Example:
|
|
//
|
|
// ```c
|
|
// static void print_char(char ch, void *param) {
|
|
// hal_uart_write_char(param, ch);
|
|
// }
|
|
// ...
|
|
// mg_log_set_fn(print_char, USART3);
|
|
// ```
|
|
void mg_log_set_fn(mg_pfn_t fn, void *param);
|
|
|
|
#if MG_ENABLE_LOG
|
|
#if !defined(_MSC_VER) && \
|
|
(!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
|
|
#define MG___FUNC__ ""
|
|
#else
|
|
#define MG___FUNC__ __func__ // introduced in C99
|
|
#endif
|
|
#define MG_LOG(level, args) \
|
|
do { \
|
|
if ((level) <= mg_log_level) { \
|
|
mg_log_prefix((level), __FILE__, __LINE__, MG___FUNC__); \
|
|
mg_log args; \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define MG_LOG(level, args) \
|
|
do { \
|
|
if (0) mg_log args; \
|
|
} while (0)
|
|
#endif
|
|
|
|
// Logging macros. Note: the argument is exactly like for mg_snprintf(),
|
|
// and it should be enclosed in double parenthesis. Example:
|
|
// ```c
|
|
// MG_INFO(("Conn %lu, recv buf: %.*s", c->id, c->recv.len, c->recv.buf));
|
|
// ```
|
|
#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args)
|
|
#define MG_INFO(args) MG_LOG(MG_LL_INFO, args)
|
|
#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args)
|
|
#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args)
|
|
|
|
|
|
|
|
|
|
struct mg_timer {
|
|
uint64_t period_ms; // Timer period in milliseconds
|
|
uint64_t expire; // Expiration timestamp in milliseconds
|
|
unsigned flags; // Possible flags values below
|
|
#define MG_TIMER_ONCE 0 // Call function once
|
|
#define MG_TIMER_REPEAT 1 // Call function periodically
|
|
#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
|
|
#define MG_TIMER_CALLED 4 // Timer function was called at least once
|
|
#define MG_TIMER_AUTODELETE 8 // mg_free() timer when done
|
|
void (*fn)(void *); // Function to call
|
|
void *arg; // Function argument
|
|
struct mg_timer *next; // Linkage
|
|
};
|
|
|
|
void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
|
|
uint64_t milliseconds, unsigned flags, void (*fn)(void *),
|
|
void *arg);
|
|
void mg_timer_free(struct mg_timer **head, struct mg_timer *);
|
|
void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
|
|
|
|
|
|
|
|
|
|
|
|
// 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, MG_FS_EXCL = 8 };
|
|
|
|
// Filesystem abstraction. Implement all function pointers to plug in a custom
|
|
// filesystem. Short UNIX-style names are used deliberately to avoid conflicts
|
|
// with libraries that define macros for stat(), read(), write(), etc.
|
|
struct mg_fs {
|
|
int (*st)(const char *path, size_t *size, time_t *mtime); // Stat: return MG_FS_* flags; size/mtime may be NULL
|
|
void (*ls)(const char *path, void (*fn)(const char *, void *), void *); // List dir: call fn(name, arg) per entry
|
|
void *(*op)(const char *path, int flags); // Open file; return opaque fd, or NULL on error
|
|
void (*cl)(void *fd); // Close file
|
|
size_t (*rd)(void *fd, void *buf, size_t len); // Read up to len bytes; return bytes read
|
|
size_t (*wr)(void *fd, const void *buf, size_t len); // Write len bytes; return bytes written
|
|
size_t (*sk)(void *fd, size_t offset); // Seek to offset from start; return new position
|
|
bool (*mv)(const char *from, const char *to); // Rename/move file
|
|
bool (*rm)(const char *path); // Delete file
|
|
bool (*mkd)(const char *path); // Create directory
|
|
};
|
|
|
|
extern struct mg_fs mg_fs_posix; // POSIX filesystem (open/close/read/write/seek)
|
|
extern struct mg_fs mg_fs_packed; // Read-only packed filesystem (see tutorials/core/embedded-filesystem)
|
|
extern struct mg_fs mg_fs_fat; // FAT filesystem
|
|
|
|
// Open file descriptor returned by mg_fs_open(). Bundles the raw fd with its fs.
|
|
struct mg_fd {
|
|
void *fd; // Opaque file handle from fs->op()
|
|
struct mg_fs *fs; // Filesystem that owns fd
|
|
};
|
|
|
|
// Opens path with the given MG_FS_* flags. Returns NULL on error.
|
|
struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
|
|
|
|
// Closes fd and frees the mg_fd struct. Safe to call with NULL.
|
|
void mg_fs_close(struct mg_fd *fd);
|
|
|
|
// Sequential directory iterator. Call repeatedly with the same buf/len;
|
|
// each call fills buf with the next entry name. Returns false when done.
|
|
bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len);
|
|
|
|
// Reads the entire file into a heap-allocated buffer. The returned mg_str is
|
|
// NUL-terminated. Caller must mg_free(result.buf). Returns {NULL,0} on error.
|
|
struct mg_str mg_file_read(struct mg_fs *fs, const char *path);
|
|
|
|
// Atomically writes buf/len to path (via a temp file + rename). Returns false on error.
|
|
bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, size_t len);
|
|
|
|
// Formats a string and atomically writes it to path. Returns false on error.
|
|
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
|
|
|
|
|
|
// A file stored in memory (flash or RAM), used by mg_fs_packed.
|
|
struct mg_mem_file {
|
|
const char *path; // Virtual file path, e.g. "/web_root/index.html"
|
|
const unsigned char *data; // Pointer to file content
|
|
size_t size; // Content size in bytes
|
|
time_t mtime; // Modification time (may be 0)
|
|
};
|
|
|
|
// Pointer to a NULL-path-terminated array of in-memory files for mg_fs_packed.
|
|
// Override to serve a custom set of files:
|
|
// static const struct mg_mem_file my_files[] = {
|
|
// {"/index.html", data, sizeof(data), 0},
|
|
// {NULL, NULL, 0, 0},
|
|
// };
|
|
// mg_mem_files = my_files;
|
|
extern const struct mg_mem_file *mg_mem_files;
|
|
|
|
// Looks up path in the packed file array. Returns an mg_str pointing directly
|
|
// into the stored data (zero-copy). Returns {NULL,0} if not found.
|
|
struct mg_str mg_unpacked(const char *path);
|
|
|
|
// Array of packed files generated by the "pack" utility into packed_fs.c.
|
|
// Activate by assigning: mg_mem_files = mg_packed_files;
|
|
extern const struct mg_mem_file mg_packed_files[];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_ENABLE_ASSERT
|
|
#include <assert.h>
|
|
#elif !defined(assert)
|
|
#define assert(x)
|
|
#endif
|
|
|
|
// Wrappers around calloc/free. Override by defining MG_ENABLE_CUSTOM_CALLOC=1
|
|
// and providing your own implementations.
|
|
void *mg_calloc(size_t count, size_t size);
|
|
void mg_free(void *ptr);
|
|
|
|
// Zeroes len bytes at buf using a volatile write loop that the compiler cannot
|
|
// elide. Safe to call with buf=NULL. Use instead of memset() for clearing
|
|
// sensitive data (keys, passwords).
|
|
void mg_bzero(volatile unsigned char *buf, size_t len);
|
|
|
|
// Fills buf with len cryptographically random bytes. Uses the best available
|
|
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
|
|
// Falls back to rand() with an error log if no strong source is available.
|
|
// Returns true when a strong source was used, false on fallback to rand().
|
|
// Override by defining MG_ENABLE_CUSTOM_RANDOM=1 and providing your own impl.
|
|
bool mg_random(void *buf, size_t len);
|
|
|
|
// Fills buf with len-1 random alphanumeric characters ([a-zA-Z0-9]) and
|
|
// NUL-terminates. Returns buf.
|
|
char *mg_random_str(char *buf, size_t len);
|
|
|
|
// Computes CRC32 (polynomial 0xEDB88320) over buf/len. Pass crc=0 to start
|
|
// a new checksum; pass the result of a prior call to extend over more data.
|
|
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
|
|
|
|
// Returns true if path is safe to serve from the filesystem. Rejects paths
|
|
// that start with '~' or '..', or contain a '/../' component, to prevent
|
|
// directory traversal attacks.
|
|
bool mg_path_is_sane(const struct mg_str path);
|
|
|
|
// Busy-waits for at least ms milliseconds using mg_millis(). Blocks the
|
|
// calling context; avoid in event handlers.
|
|
void mg_delayms(unsigned int ms);
|
|
|
|
// Packs four byte values into a uint32_t in big-endian order.
|
|
// MG_U32(1, 2, 3, 4) == 0x01020304
|
|
#define MG_U32(a, b, c, d) \
|
|
(((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \
|
|
((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255))
|
|
|
|
// Constructs an IPv4 address in network byte order from four decimal octets.
|
|
// Usage: uint32_t ip = MG_IPV4(192, 168, 1, 1);
|
|
#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d))
|
|
|
|
// Expands to a brace-enclosed byte initialiser for a 16-byte IPv6 address.
|
|
// Arguments are the eight 16-bit groups in the address, e.g.:
|
|
// uint8_t ip6[16] = MG_IPV6(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
|
|
#define MG_IPV6(a, b, c, d, e, f, g ,h) \
|
|
{ (uint8_t)((a)>>8),(uint8_t)(a), \
|
|
(uint8_t)((b)>>8),(uint8_t)(b), \
|
|
(uint8_t)((c)>>8),(uint8_t)(c), \
|
|
(uint8_t)((d)>>8),(uint8_t)(d), \
|
|
(uint8_t)((e)>>8),(uint8_t)(e), \
|
|
(uint8_t)((f)>>8),(uint8_t)(f), \
|
|
(uint8_t)((g)>>8),(uint8_t)(g), \
|
|
(uint8_t)((h)>>8),(uint8_t)(h) }
|
|
|
|
// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip))
|
|
#define MG_U8P(ADDR) ((uint8_t *) (ADDR))
|
|
#define MG_IPADDR_PARTS(ADDR) \
|
|
MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3]
|
|
|
|
// Read an unaligned big-endian value from byte pointer p into a native integer.
|
|
// Safe on architectures that forbid unaligned access (e.g. Cortex-M0).
|
|
#define MG_LOAD_BE16(p) \
|
|
((uint16_t) (((uint16_t) MG_U8P(p)[0] << 8U) | MG_U8P(p)[1]))
|
|
#define MG_LOAD_BE24(p) \
|
|
((uint32_t) (((uint32_t) MG_U8P(p)[0] << 16U) | \
|
|
((uint32_t) MG_U8P(p)[1] << 8U) | MG_U8P(p)[2]))
|
|
#define MG_LOAD_BE32(p) \
|
|
((uint32_t) (((uint32_t) MG_U8P(p)[0] << 24U) | \
|
|
((uint32_t) MG_U8P(p)[1] << 16U) | \
|
|
((uint32_t) MG_U8P(p)[2] << 8U) | MG_U8P(p)[3]))
|
|
#define MG_LOAD_BE64(p) \
|
|
((uint64_t) (((uint64_t) MG_U8P(p)[0] << 56U) | \
|
|
((uint64_t) MG_U8P(p)[1] << 48U) | \
|
|
((uint64_t) MG_U8P(p)[2] << 40U) | \
|
|
((uint64_t) MG_U8P(p)[3] << 32U) | \
|
|
((uint64_t) MG_U8P(p)[4] << 24U) | \
|
|
((uint64_t) MG_U8P(p)[5] << 16U) | \
|
|
((uint64_t) MG_U8P(p)[6] << 8U) | MG_U8P(p)[7]))
|
|
|
|
// Write a native integer to byte pointer p in big-endian byte order.
|
|
// Safe on architectures that forbid unaligned access.
|
|
#define MG_STORE_BE16(p, n) \
|
|
do { \
|
|
MG_U8P(p)[0] = (uint8_t) ((n) >> 8U); \
|
|
MG_U8P(p)[1] = (uint8_t) (n); \
|
|
} while (0)
|
|
#define MG_STORE_BE24(p, n) \
|
|
do { \
|
|
MG_U8P(p)[0] = (uint8_t) ((n) >> 16U); \
|
|
MG_U8P(p)[1] = (uint8_t) ((n) >> 8U); \
|
|
MG_U8P(p)[2] = (uint8_t) (n); \
|
|
} while (0)
|
|
#define MG_STORE_BE32(p, n) \
|
|
do { \
|
|
MG_U8P(p)[0] = (uint8_t) ((n) >> 24U); \
|
|
MG_U8P(p)[1] = (uint8_t) ((n) >> 16U); \
|
|
MG_U8P(p)[2] = (uint8_t) ((n) >> 8U); \
|
|
MG_U8P(p)[3] = (uint8_t) (n); \
|
|
} while (0)
|
|
#define MG_STORE_BE64(p, n) \
|
|
do { \
|
|
MG_U8P(p)[0] = (uint8_t) ((n) >> 56U); \
|
|
MG_U8P(p)[1] = (uint8_t) ((n) >> 48U); \
|
|
MG_U8P(p)[2] = (uint8_t) ((n) >> 40U); \
|
|
MG_U8P(p)[3] = (uint8_t) ((n) >> 32U); \
|
|
MG_U8P(p)[4] = (uint8_t) ((n) >> 24U); \
|
|
MG_U8P(p)[5] = (uint8_t) ((n) >> 16U); \
|
|
MG_U8P(p)[6] = (uint8_t) ((n) >> 8U); \
|
|
MG_U8P(p)[7] = (uint8_t) (n); \
|
|
} while (0)
|
|
|
|
// Network / host byte-order conversion (big-endian <-> native).
|
|
// mg_htons/mg_htonl/mg_htonll are aliases for the same operation (symmetric).
|
|
uint16_t mg_ntohs(uint16_t net);
|
|
uint32_t mg_ntohl(uint32_t net);
|
|
uint64_t mg_ntohll(uint64_t net);
|
|
#define mg_htons(x) mg_ntohs(x)
|
|
#define mg_htonl(x) mg_ntohl(x)
|
|
#define mg_htonll(x) mg_ntohll(x)
|
|
|
|
// Memory-mapped register access: reads/writes a volatile uint32_t at address x.
|
|
#define MG_REG(x) ((volatile uint32_t *) (x))[0]
|
|
|
|
// Produces a uint32_t with bit x set. x must be 0-31.
|
|
#define MG_BIT(x) (((uint32_t) 1U) << (x))
|
|
|
|
// Clears bits in CLRMASK and sets bits in SETMASK in register R atomically
|
|
// (read-modify-write). Example: MG_SET_BITS(MG_REG(addr), 0xF, 0x3);
|
|
#define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK)
|
|
|
|
// Round x up / down to the nearest multiple of a. Returns x when a is 0.
|
|
#define MG_ROUND_UP(x, a) ((a) == 0 ? (x) : ((((x) + (a) -1) / (a)) * (a)))
|
|
#define MG_ROUND_DOWN(x, a) ((a) == 0 ? (x) : (((x) / (a)) * (a)))
|
|
|
|
#if defined(__GNUC__) && defined(__arm__)
|
|
#ifdef __ZEPHYR__
|
|
#define MG_ARM_DISABLE_IRQ() __asm__ __volatile__("cpsid i" : : : "memory")
|
|
#define MG_ARM_ENABLE_IRQ() __asm__ __volatile__("cpsie i" : : : "memory")
|
|
#else
|
|
#define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory")
|
|
#define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory")
|
|
#endif // !ZEPHYR
|
|
#elif defined(__CCRH__)
|
|
#define MG_RH850_DISABLE_IRQ() __DI()
|
|
#define MG_RH850_ENABLE_IRQ() __EI()
|
|
#else
|
|
#define MG_ARM_DISABLE_IRQ()
|
|
#define MG_ARM_ENABLE_IRQ()
|
|
#endif
|
|
|
|
#if defined(__CC_ARM)
|
|
#define MG_DSB() __dsb(0xf)
|
|
#elif defined(__ARMCC_VERSION)
|
|
#define MG_DSB() __builtin_arm_dsb(0xf)
|
|
#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__)
|
|
#ifdef __ZEPHYR__
|
|
#define MG_DSB() __asm__("DSB 0xf")
|
|
#else
|
|
#define MG_DSB() asm("DSB 0xf")
|
|
#endif // !ZEPHYR
|
|
#elif defined(__ICCARM__)
|
|
#define MG_DSB() __iar_builtin_DSB()
|
|
#else
|
|
#define MG_DSB()
|
|
#endif
|
|
|
|
struct mg_addr;
|
|
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip);
|
|
|
|
// Linked list management macros
|
|
#define LIST_ADD_HEAD(type_, head_, elem_) \
|
|
do { \
|
|
(elem_)->next = (*head_); \
|
|
*(head_) = (elem_); \
|
|
} while (0)
|
|
|
|
#define LIST_ADD_TAIL(type_, head_, elem_) \
|
|
do { \
|
|
type_ **h = head_; \
|
|
while (*h != NULL) h = &(*h)->next; \
|
|
*h = (elem_); \
|
|
} while (0)
|
|
|
|
#define LIST_DELETE(type_, head_, elem_) \
|
|
do { \
|
|
type_ **h = head_; \
|
|
while (*h != (elem_)) h = &(*h)->next; \
|
|
*h = (elem_)->next; \
|
|
} while (0)
|
|
|
|
|
|
|
|
// Returns the port from url. Uses scheme defaults when no port is explicit:
|
|
// http/ws=80, https/wss=443, mqtt=1883, mqtts=8883. Returns 0 if unknown.
|
|
unsigned short mg_url_port(const char *url);
|
|
|
|
// Returns true if url uses a TLS scheme (https, wss, mqtts, ssl, tls, tcps).
|
|
bool mg_url_is_ssl(const char *url);
|
|
|
|
// Returns the hostname from url as mg_str, without port or brackets.
|
|
// E.g. "http://foo.com:8080/x" -> "foo.com". Not NUL-terminated.
|
|
struct mg_str mg_url_host(const char *url);
|
|
|
|
// Returns the username from url (user:pass@host form) as mg_str.
|
|
// Returns an empty mg_str if no credentials are present. Not NUL-terminated.
|
|
struct mg_str mg_url_user(const char *url);
|
|
|
|
// Returns the password from url (user:pass@host form) as mg_str.
|
|
// Returns an empty mg_str if no credentials are present. Not NUL-terminated.
|
|
struct mg_str mg_url_pass(const char *url);
|
|
|
|
// Returns a pointer to the URI path in url (e.g. "/api/v1").
|
|
// Returns "/" if the url has no path. Points into the original url string.
|
|
const char *mg_url_uri(const char *url);
|
|
|
|
|
|
|
|
|
|
// Dynamically-sized I/O buffer. len <= size always holds.
|
|
struct mg_iobuf {
|
|
unsigned char *buf; // Heap-allocated data buffer
|
|
size_t size; // Allocated capacity (rounded up to align)
|
|
size_t len; // Number of bytes currently stored
|
|
size_t align; // Allocation granularity; capacity is always a multiple of this
|
|
};
|
|
|
|
// Initializes io to empty, then allocates size bytes with the given alignment.
|
|
// Returns false on allocation failure.
|
|
bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align);
|
|
|
|
// Resizes the buffer to new_size (rounded up to io->align). new_size=0 frees
|
|
// the buffer. Uses mg_calloc+mg_free (not realloc) so old memory is zeroed on release.
|
|
// Returns false on allocation failure.
|
|
bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size);
|
|
|
|
// Frees the buffer and zeroes the struct. Equivalent to mg_iobuf_resize(io, 0).
|
|
void mg_iobuf_free(struct mg_iobuf *io);
|
|
|
|
// Inserts len bytes from buf at offset ofs, shifting existing data right.
|
|
// Pass buf=NULL to reserve space without writing. Returns bytes inserted, 0 on OOM.
|
|
size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf, size_t len);
|
|
|
|
// Removes len bytes at ofs, shifting remaining data left. Clamps to available data.
|
|
// Returns bytes actually removed.
|
|
size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len);
|
|
|
|
|
|
size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len);
|
|
size_t mg_base64_final(char *buf, size_t len);
|
|
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
|
|
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
uint32_t buf[4];
|
|
uint32_t bits[2];
|
|
unsigned char in[64];
|
|
} mg_md5_ctx;
|
|
|
|
void mg_md5_init(mg_md5_ctx *c);
|
|
void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
|
|
void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
uint32_t state[5];
|
|
uint32_t count[2];
|
|
unsigned char buffer[64];
|
|
} mg_sha1_ctx;
|
|
|
|
void mg_sha1_init(mg_sha1_ctx *);
|
|
void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
|
|
void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
|
|
// https://github.com/B-Con/crypto-algorithms
|
|
// Author: Brad Conte (brad AT bradconte.com)
|
|
// Disclaimer: This code is presented "as is" without any guarantees.
|
|
// Details: Defines the API for the corresponding SHA1 implementation.
|
|
// Copyright: public domain
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
uint32_t state[8];
|
|
uint64_t bits;
|
|
uint32_t len;
|
|
unsigned char buffer[64];
|
|
} mg_sha256_ctx;
|
|
|
|
|
|
void mg_sha256_init(mg_sha256_ctx *);
|
|
void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len);
|
|
void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *);
|
|
void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz);
|
|
void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
|
|
size_t datasz);
|
|
|
|
typedef struct {
|
|
uint64_t state[8];
|
|
uint8_t buffer[128];
|
|
uint64_t bitlen[2];
|
|
uint32_t datalen;
|
|
} mg_sha384_ctx;
|
|
void mg_sha384_init(mg_sha384_ctx *ctx);
|
|
void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len);
|
|
void mg_sha384_final(uint8_t digest[48], mg_sha384_ctx *ctx);
|
|
void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz);
|
|
|
|
|
|
|
|
struct mg_connection;
|
|
|
|
// User-supplied event handler. ev is enum mg_event; ev_data type depends on
|
|
// the event (see enum below). c->fn_data is the user pointer passed to
|
|
// mg_listen(), mg_connect(), mg_*_listen(), mg_*_connect().
|
|
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
|
|
void *ev_data);
|
|
|
|
// Fires ev on connection c, invoking c->fn and c->pfn with ev_data.
|
|
void mg_call(struct mg_connection *c, int ev, void *ev_data);
|
|
|
|
// Sets c->is_closing and fires MG_EV_ERROR with a printf-formatted message.
|
|
void mg_error(struct mg_connection *c, const char *fmt, ...);
|
|
|
|
// Event codes passed to mg_event_handler_t. Each entry shows the type of
|
|
// ev_data cast to use inside the handler, e.g.: char *msg = (char *) ev_data;
|
|
enum mg_event {
|
|
// Event Meaning ev_data type
|
|
MG_EV_ERROR, // Error char *error_message
|
|
MG_EV_OPEN, // Connection created NULL
|
|
MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
|
|
MG_EV_RESOLVE, // Host name is resolved NULL
|
|
MG_EV_CONNECT, // Connection established NULL
|
|
MG_EV_ACCEPT, // Connection accepted NULL
|
|
MG_EV_TLS_HS, // TLS handshake succeeded NULL
|
|
MG_EV_READ, // Data received from socket long *bytes_read
|
|
MG_EV_WRITE, // Data written to socket long *bytes_written
|
|
MG_EV_CLOSE, // Connection closed NULL
|
|
MG_EV_HTTP_HDRS, // HTTP headers struct mg_http_message *
|
|
MG_EV_HTTP_MSG, // Full HTTP request/response struct mg_http_message *
|
|
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
|
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
|
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
|
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
|
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
|
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
|
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
|
|
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
|
|
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
|
|
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
|
|
MG_EV_MODBUS_REQ, // Modbus TCP request struct mg_modbus_req *
|
|
MG_EV_USER // Starting ID for user events
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DNS server configuration used by struct mg_mgr.
|
|
struct mg_dns {
|
|
const char *url; // DNS server URL, e.g. "udp://8.8.8.8:53"
|
|
struct mg_connection *c; // Active DNS connection; NULL when idle
|
|
};
|
|
|
|
// Network address: IPv4 or IPv6 address plus port. All values in network byte order.
|
|
struct mg_addr {
|
|
union { // IP address bytes in network byte order
|
|
uint8_t ip[16]; // Raw 16-byte buffer; IPv4 uses the first 4 bytes
|
|
uint32_t ip4; // IPv4 address alias (same storage as ip[0..3])
|
|
uint64_t ip6[2]; // IPv6 address as two 64-bit words
|
|
} addr;
|
|
uint16_t port; // TCP or UDP port in network byte order
|
|
uint8_t scope_id; // IPv6 scope ID (from the %N suffix in the address)
|
|
bool is_ip6; // True when this address holds an IPv6 address
|
|
};
|
|
|
|
union mg_pipe {
|
|
MG_SOCKET_TYPE fd;
|
|
void *q;
|
|
};
|
|
|
|
// Central event manager. Zero-initialise with mg_mgr_init() before use.
|
|
struct mg_mgr {
|
|
struct mg_connection *conns; // Linked list of all open connections
|
|
struct mg_dns dns4; // IPv4 DNS server (default: 8.8.8.8:53)
|
|
struct mg_dns dns6; // IPv6 DNS server (default: [2001:4860:4860::8888]:53)
|
|
struct mg_connection *mdns; // mDNS listener connection, or NULL
|
|
int dnstimeout; // DNS resolve timeout in ms (default: 3000)
|
|
bool use_dns6; // If true, prefer DNS6 for hostname resolution
|
|
unsigned long nextid; // Auto-incrementing counter for connection IDs
|
|
void *userdata; // Arbitrary user pointer
|
|
void *tls_ctx; // Shared TLS context for all TLS connections
|
|
uint16_t mqtt_id; // Packet ID counter for MQTT pub/sub
|
|
void *active_dns_requests; // Pending DNS queries (internal)
|
|
void *active_mdns_requests; // Pending mDNS resolver queries (internal)
|
|
struct mg_timer *timers; // Linked list of active timers
|
|
int epoll_fd; // epoll file descriptor; -1 when unused (MG_EPOLL_ENABLE=1)
|
|
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
|
|
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
|
|
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
|
|
#if MG_ENABLE_FREERTOS_TCP
|
|
SocketSet_t ss; // FreeRTOS-TCP socket set
|
|
#endif
|
|
};
|
|
|
|
// A single network connection. Do not allocate directly; use mg_connect() or
|
|
// mg_listen(). Each connection has an event handler fn called for every event.
|
|
struct mg_connection {
|
|
struct mg_connection *next; // Next connection in mgr->conns linked list
|
|
struct mg_mgr *mgr; // Manager that owns this connection
|
|
struct mg_addr loc; // Local address (IP + port)
|
|
struct mg_addr rem; // Remote address (IP + port)
|
|
void *fd; // OS socket handle or LWIP PCB pointer
|
|
unsigned long id; // Unique, auto-incrementing connection ID
|
|
struct mg_iobuf recv; // Received data; consume in MG_EV_READ handler
|
|
struct mg_iobuf send; // Data to send; append with mg_send() / mg_printf()
|
|
struct mg_iobuf prof; // Profiling data (MG_ENABLE_PROFILE only)
|
|
struct mg_iobuf rtls; // TLS only: encrypted data buffered before decryption
|
|
mg_event_handler_t fn; // User event handler
|
|
void *fn_data; // User-supplied argument passed to fn
|
|
mg_event_handler_t pfn; // Protocol-level handler (set by protocol modules)
|
|
void *pfn_data; // Protocol-level handler argument
|
|
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
|
|
void *tls; // TLS state (internal)
|
|
unsigned is_listening : 1; // Listening connection; accepts inbound connections
|
|
unsigned is_client : 1; // Outbound connection created by mg_connect()
|
|
unsigned is_accepted : 1; // Inbound connection accepted from a listener
|
|
unsigned is_resolving : 1; // DNS resolution is in progress
|
|
unsigned is_arplooking : 1; // ARP resolution is in progress (builtin TCP/IP)
|
|
unsigned is_connecting : 1; // Non-blocking TCP connect is in progress
|
|
unsigned is_tls : 1; // TLS connection
|
|
unsigned is_tls_hs : 1; // TLS handshake is in progress
|
|
unsigned is_udp : 1; // UDP connection (not TCP)
|
|
unsigned is_websocket : 1; // WebSocket connection
|
|
unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login)
|
|
unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic
|
|
unsigned is_draining : 1; // Flush send buffer, then close
|
|
unsigned is_closing : 1; // Close and free immediately on next poll
|
|
unsigned is_full : 1; // Pause incoming reads until cleared
|
|
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
|
|
unsigned is_resp : 1; // HTTP: response is still being generated
|
|
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
|
|
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
|
|
};
|
|
|
|
// Runs one iteration of the event loop.
|
|
//
|
|
// Example:
|
|
// while (keep_running) mg_mgr_poll(&mgr, 50);
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/mqtt/mqtt-client,
|
|
// tutorials/websocket/websocket-server, tutorials/core/timers
|
|
// Related APIs:
|
|
// mg_mgr_init(), mg_mgr_free(), mg_timer_add(), mg_wakeup()
|
|
// Notes:
|
|
// Waits up to ms milliseconds for I/O events; use ms=0 to return
|
|
// immediately. Calls event handlers for ready connections and fires expired
|
|
// timers. Call repeatedly from the main loop or a dedicated network task.
|
|
void mg_mgr_poll(struct mg_mgr *, int ms);
|
|
|
|
// Initialises an event manager before use.
|
|
//
|
|
// Example:
|
|
// struct mg_mgr mgr;
|
|
// mg_mgr_init(&mgr);
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/mqtt/mqtt-client,
|
|
// tutorials/websocket/websocket-server, tutorials/core/embedded-filesystem
|
|
// Related APIs:
|
|
// mg_mgr_poll(), mg_mgr_free(), mg_http_listen(), mg_connect()
|
|
// Notes:
|
|
// Sets safe defaults such as DNS servers, DNS timeout, epoll/SIGPIPE setup,
|
|
// and TLS context. Call before creating connections, listeners, or timers.
|
|
// Set mgr.userdata after this call if your application needs it.
|
|
// On embedded systems, if the built-in TCP/IP stack is enabled
|
|
// (MG_ENABLE_TCPIP=1) and one built-in driver is selected, e.g.
|
|
// MG_ENABLE_DRIVER_STM32H=1, mg_mgr_init() also calls mg_tcpip_init().
|
|
// If no driver is selected, automatic driver init is disabled, or multiple
|
|
// interfaces are intended, call mg_tcpip_init() separately.
|
|
void mg_mgr_init(struct mg_mgr *);
|
|
|
|
// Closes all connections, frees all timers, and releases the TLS context.
|
|
// Safe to call on a partially initialised mgr.
|
|
void mg_mgr_free(struct mg_mgr *);
|
|
|
|
// Creates a listening connection on url (e.g. "tcp://0.0.0.0:8080").
|
|
// Fires MG_EV_OPEN on the listener, then MG_EV_ACCEPT for each new client.
|
|
// Returns NULL on error.
|
|
struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Opens an outbound connection to url (e.g. "tcp://example.com:80").
|
|
// DNS resolution is asynchronous. Fires MG_EV_RESOLVE, then MG_EV_CONNECT
|
|
// when the connection is established (or MG_EV_ERROR on failure).
|
|
// Returns NULL on error (OOM, network down, null url).
|
|
struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Wraps an existing OS file descriptor in a Mongoose connection.
|
|
// The connection is added to mgr and fires MG_EV_OPEN immediately.
|
|
// Useful for integrating pre-opened sockets (e.g. stdin, accept() fd).
|
|
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Called internally after DNS resolution completes to create the OS socket
|
|
// and initiate the TCP/IP connect. Normally not called by user code.
|
|
void mg_connect_resolved(struct mg_connection *);
|
|
|
|
// Appends buf/len to c->send. Returns true on success, false on OOM
|
|
// (which also fires MG_EV_ERROR and schedules the connection for close).
|
|
// Data is sent asynchronously by the next mg_mgr_poll() call.
|
|
bool mg_send(struct mg_connection *, const void *, size_t);
|
|
|
|
// Formats data and appends it to a connection send buffer.
|
|
//
|
|
// Returns:
|
|
// Number of bytes appended, or 0 on OOM.
|
|
// Example:
|
|
// mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n",
|
|
// (int) host.len, host.buf);
|
|
// Full examples:
|
|
// tutorials/http/http-client, tutorials/http/http-proxy-client,
|
|
// tutorials/http/http-restful-server
|
|
// Related APIs:
|
|
// mg_send(), mg_snprintf(), mg_http_reply(), mg_ws_printf()
|
|
// Notes:
|
|
// Data is sent asynchronously by a later mg_mgr_poll() call. Supports
|
|
// mg_xprintf specifiers, including custom %M/%m printers. See src/fmt.h for
|
|
// the full specifier list.
|
|
size_t mg_printf(struct mg_connection *, const char *fmt, ...);
|
|
size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap);
|
|
|
|
// Parses an IP address string str into addr. Handles "localhost", plain IPv4
|
|
// (e.g. "1.2.3.4"), IPv6 with optional brackets and scope ID, and
|
|
// IPv4-mapped IPv6 (::ffff:1.2.3.4). Does NOT parse the port.
|
|
// Returns true on success; addr->is_ip6 and addr->addr are set.
|
|
bool mg_aton(struct mg_str str, struct mg_addr *addr);
|
|
|
|
// Low-level functions for integrating with custom network stacks.
|
|
// Allocates a new connection struct with mgr->extraconnsize extra bytes.
|
|
// Returns NULL on OOM.
|
|
struct mg_connection *mg_alloc_conn(struct mg_mgr *);
|
|
|
|
// Fires MG_EV_CLOSE, frees all buffers (recv/send/rtls), removes c from
|
|
// mgr->conns, and calls mg_free(c). Do not use c after this call.
|
|
void mg_close_conn(struct mg_connection *c);
|
|
|
|
// Creates and binds the OS socket for a listening connection c at url.
|
|
// Called internally by mg_listen(); expose for custom network stack use.
|
|
// Returns false on error.
|
|
bool mg_open_listener(struct mg_connection *c, const char *url);
|
|
|
|
// Thread-safe wakeup: fires MG_EV_WAKEUP on the connection whose id matches,
|
|
// with ev_data pointing to an mg_str containing buf/len.
|
|
// Requires mg_wakeup_init() to have been called first.
|
|
// Returns false if the pipe is not initialised or conn_id is 0.
|
|
// Safe to call from any thread or interrupt context.
|
|
bool mg_wakeup(struct mg_mgr *, unsigned long id, const void *buf, size_t len);
|
|
|
|
// Initialises the internal socketpair used by mg_wakeup(). Call once after
|
|
// mg_mgr_init() if you intend to wake the event loop from another thread.
|
|
// Returns false on failure (e.g. socketpair creation failed).
|
|
bool mg_wakeup_init(struct mg_mgr *);
|
|
|
|
// Deprecated API, do not expose. Use mg_timer_expired instead
|
|
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
|
|
unsigned flags, void (*fn)(void *), void *arg);
|
|
|
|
// Like mg_connect() but also sets a protocol-level handler pfn/pfn_data.
|
|
// Used internally by protocol modules (MQTT, WebSocket, etc.).
|
|
struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
|
|
mg_event_handler_t fn, void *fn_data,
|
|
mg_event_handler_t pfn, void *pfn_data);
|
|
|
|
// Restores c->rem from a saved address buffer written by the caller, then
|
|
// maps the IP back through the builtin TCP/IP stack. Used after a UDP
|
|
// multicast send to restore the connection's original remote address.
|
|
void mg_multicast_restore(struct mg_connection *c, uint8_t *from);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A single HTTP header name/value pair.
|
|
struct mg_http_header {
|
|
struct mg_str name; // Header name, e.g. "Content-Type"
|
|
struct mg_str value; // Header value, e.g. "text/html"
|
|
};
|
|
|
|
// Parsed HTTP request or response.
|
|
// Passed as ev_data in MG_EV_HTTP_MSG and MG_EV_HTTP_HDRS.
|
|
// For requests: method="GET", uri="/path", proto="a=1&b=2", proto="HTTP/1.1"
|
|
// For responses: method="HTTP/1.0", uri="200", query="", proto="OK"
|
|
struct mg_http_message {
|
|
struct mg_str method, uri, query, proto; // Request/response line
|
|
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Parsed headers array
|
|
struct mg_str body; // Request or response body
|
|
struct mg_str head; // Raw bytes: request/status line + headers, no body
|
|
struct mg_str message; // Raw bytes: head + body
|
|
};
|
|
|
|
// Options for mg_http_serve_dir() and mg_http_serve_file().
|
|
struct mg_http_serve_opts {
|
|
const char *root_dir; // Root directory for serving files, e.g. "/web"
|
|
const char *ssi_pattern; // Pattern for Server-Side Include files, e.g. "#.shtml". NULL disables SSI
|
|
const char *extra_headers; // Extra response headers, e.g. "A: b\r\nC: d\r\n". NULL for none
|
|
const char *mime_types; // Additional MIME types: "ext1=type1,ext2=type2". NULL for defaults only
|
|
const char *page404; // Path to a custom 404 page, e.g. "/404.html". NULL for built-in
|
|
struct mg_fs *fs; // Filesystem to use. NULL defaults to POSIX
|
|
};
|
|
|
|
// A single part of a multipart/form-data body, filled by mg_http_next_multipart().
|
|
struct mg_http_part {
|
|
struct mg_str name; // Form field name, from Content-Disposition: name="..."
|
|
struct mg_str filename; // Original filename for file uploads, empty for plain fields
|
|
struct mg_str body; // Part contents, points into the original message buffer
|
|
};
|
|
|
|
// Parses HTTP request or response in buffer s/len into hm.
|
|
// Returns number of bytes consumed, 0 if incomplete, -1 on error.
|
|
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
|
|
|
// Returns the byte length of HTTP headers in buf (i.e. offset of the body).
|
|
// Returns 0 if headers are incomplete, -1 on error.
|
|
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
|
|
|
|
// Sends one chunk in HTTP chunked transfer encoding using printf-style fmt.
|
|
// Call with empty fmt ("") to send the terminating zero-length chunk.
|
|
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
|
|
|
|
// Sends one chunk in HTTP chunked transfer encoding from a raw buffer.
|
|
// Call with len=0 to send the terminating zero-length chunk.
|
|
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
|
|
|
|
// Creates an HTTP server on url, e.g. "http://0.0.0.0:8000".
|
|
//
|
|
// Returns:
|
|
// Listening connection, or NULL on error.
|
|
// Example:
|
|
// mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL);
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/http/*
|
|
// Related APIs:
|
|
// mg_http_reply(), mg_http_serve_dir(), mg_match()
|
|
// Notes:
|
|
// Call mg_mgr_poll() in the main loop. The user-supplied fn event handler
|
|
// receives normal connection events. It also receives MG_EV_HTTP_HDRS when
|
|
// headers are received and MG_EV_HTTP_MSG when the full request is received.
|
|
// ev_data for both HTTP events is struct mg_http_message *.
|
|
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Opens an HTTP client connection to url, e.g. "http://example.org".
|
|
//
|
|
// Returns:
|
|
// Client connection, or NULL on error.
|
|
// Example:
|
|
// mg_http_connect(&mgr, "http://example.org", fn, NULL);
|
|
// Full examples:
|
|
// tutorials/http/http-client, tutorials/http/huge-response
|
|
// Related APIs:
|
|
// mg_printf(), mg_http_status(), mg_http_get_header()
|
|
// Notes:
|
|
// In the user-supplied fn event handler, send the HTTP request on
|
|
// MG_EV_CONNECT. The handler receives normal connection events. It also
|
|
// receives MG_EV_HTTP_HDRS when headers are received and MG_EV_HTTP_MSG when
|
|
// the full response is received. ev_data for both HTTP events is struct
|
|
// mg_http_message *.
|
|
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Serves static files from a directory.
|
|
//
|
|
// Example:
|
|
// struct mg_http_serve_opts o = {.root_dir = "web_root", .fs = &mg_fs_posix};
|
|
// mg_http_serve_dir(c, hm, &o);
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/core/embedded-filesystem
|
|
// Related APIs:
|
|
// mg_http_listen(), mg_http_serve_file(), mg_http_reply(), mg_match()
|
|
// Notes:
|
|
// Call from an MG_EV_HTTP_MSG handler. The uri in hm is mapped under
|
|
// opts->root_dir. Directory listing depends on MG_ENABLE_DIRLIST; SSI uses
|
|
// opts->ssi_pattern when configured.
|
|
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
|
const struct mg_http_serve_opts *);
|
|
|
|
// Serves a single file at path. Call from an MG_EV_HTTP_MSG handler.
|
|
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
|
|
const char *path, const struct mg_http_serve_opts *);
|
|
|
|
// Sends a complete HTTP response with Content-Length.
|
|
//
|
|
// Example:
|
|
// mg_http_reply(c, 200, "Content-Type: application/json\r\n",
|
|
// "{%m:%d}", MG_ESC("temperature"), 123);
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/http/http-restful-server,
|
|
// tutorials/http/device-dashboard
|
|
// Related APIs:
|
|
// mg_http_listen(), mg_printf(), mg_http_printf_chunk(), MG_ESC
|
|
// Notes:
|
|
// headers must end with "\r\n"; pass "" or NULL for no extra headers.
|
|
// body_fmt is printf-style and supports %M/%m custom printers. Use MG_ESC
|
|
// when printing JSON strings.
|
|
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
|
|
const char *body_fmt, ...);
|
|
|
|
// Looks up a request header by name (case-insensitive).
|
|
// Returns a pointer to the mg_str value, or NULL if not found.
|
|
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
|
|
|
|
// Extracts a named variable from a query string or form-encoded body (buf).
|
|
// Returns the raw (URL-encoded) value as mg_str, or an empty mg_str if not found.
|
|
// Not NUL-terminated. Use mg_url_decode() to get a decoded string.
|
|
struct mg_str mg_http_var(struct mg_str buf, struct mg_str name);
|
|
|
|
// URL-decodes variable name from query string or form body into a NUL-terminated buffer.
|
|
// Returns the length of the decoded value, or negative if not found / buffer too small.
|
|
int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
|
|
|
|
// URL-decodes s/n into to/to_len. Set form=1 to also decode '+' as space.
|
|
// Returns number of bytes written, or -1 if to buffer is too small.
|
|
int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
|
|
|
|
// URL-encodes s/n into buf/len. Returns number of bytes written.
|
|
size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
|
|
|
|
// Extracts HTTP credentials into caller-supplied buffers.
|
|
// For Basic auth: fills user (buf1) and password (buf2).
|
|
// For Bearer auth: fills buf1 with "" and buf2 with the token.
|
|
void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
|
|
|
|
// Deprecated API, do not expose
|
|
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|
struct mg_fs *fs, const char *dir, size_t max_size);
|
|
|
|
// Streams the raw request body into dir/name via fs. Non-blocking: installs an
|
|
// internal handler and returns immediately. fn is called with NULL on success
|
|
// or an error string on failure.
|
|
void mg_http_start_upload(struct mg_connection *c, struct mg_http_message *hm,
|
|
struct mg_str name, struct mg_str dir,
|
|
struct mg_fs *fs,
|
|
void (*fn)(struct mg_connection *, const char *));
|
|
|
|
// Starts an OTA firmware update from an HTTP POST upload.
|
|
// Calls mg_ota_begin/write/end internally; fn is called with NULL on success,
|
|
// or an error string on failure.
|
|
void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
|
|
void (*fn)(struct mg_connection *, const char *));
|
|
|
|
// Sends a 401 Unauthorized response with a Basic Auth WWW-Authenticate challenge.
|
|
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
|
|
|
// Extracts a named parameter from a header value string s.
|
|
// E.g. for s="multipart/form-data; boundary=abc", v="boundary" returns "abc".
|
|
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
|
|
|
|
// Iterates over parts of a multipart/form-data body.
|
|
//
|
|
// Returns:
|
|
// Offset for the next call, or 0 when there are no more parts.
|
|
// Example:
|
|
// size_t ofs = 0;
|
|
// struct mg_http_part part;
|
|
// while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) { ... }
|
|
// Full examples:
|
|
// tutorials/http/file-upload-html-form, tutorials/http/http-server
|
|
// Related APIs:
|
|
// mg_http_listen(), mg_http_start_upload()
|
|
// Notes:
|
|
// Call from an MG_EV_HTTP_MSG handler after the full request body is
|
|
// received. part.name, part.filename, and part.body are zero-copy slices into
|
|
// hm->body and are not NUL-terminated.
|
|
size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
|
|
|
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
|
|
int mg_http_status(const struct mg_http_message *hm);
|
|
|
|
|
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
|
const char *fullpath);
|
|
// TLS / Security
|
|
|
|
|
|
// Available TLS backend libraries
|
|
#define MG_TLS_NONE 0 // No TLS support
|
|
#define MG_TLS_MBED 1 // mbedTLS
|
|
#define MG_TLS_OPENSSL 2 // OpenSSL
|
|
#define MG_TLS_WOLFSSL 5 // WolfSSL (based on OpenSSL)
|
|
#define MG_TLS_BUILTIN 3 // Built-in
|
|
#define MG_TLS_CUSTOM 4 // Custom implementation
|
|
|
|
#ifndef MG_TLS
|
|
// mongoose_config.h setting. Set MG_TLS to one of the MG_TLS_* values above to
|
|
// select a TLS backend. Defaults to MG_TLS_NONE (no TLS).
|
|
#define MG_TLS MG_TLS_NONE
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// TLS options structure passed to mg_tls_init(). All cert/key fields accept
|
|
// PEM strings or DER binary.
|
|
//
|
|
// One-way TLS:
|
|
// server sets `cert` + `key`,
|
|
// client sets `ca` + optionally `name` for hostname verification.
|
|
//
|
|
// Two-way (mutual) TLS: both sides set `ca` + `cert` + `key`.
|
|
//
|
|
// - `ca`: CA certificate. Verifies the peer's certificate.
|
|
// Set on clients to authenticate the server. Set on servers to require
|
|
// and verify a client certificate. If empty, peer is not verified.
|
|
// - `cert`: Our certificate. Required on servers. Also set on clients for
|
|
// mutual TLS.
|
|
// - `key`: Our private key. May equal `cert` when PEM bundles both.
|
|
// - `name`: Server name for SNI and hostname verification. Set on clients.
|
|
// Empty disables hostname verification.
|
|
// - `skip_verification`: Skip certificate and hostname verification.
|
|
// Useful during development; do not use in production.
|
|
struct mg_tls_opts {
|
|
struct mg_str ca; // CA certificate, PEM or DER
|
|
struct mg_str cert; // Our certificate, PEM or DER
|
|
struct mg_str key; // Our private key, PEM or DER
|
|
struct mg_str name; // Server name for SNI + hostname verification
|
|
bool skip_verification; // Skip certificate and hostname verification
|
|
};
|
|
|
|
// Initialises TLS on a connection.
|
|
//
|
|
// Example:
|
|
// ```c
|
|
// // Server: one-way TLS
|
|
// if (ev == MG_EV_ACCEPT) {
|
|
// struct mg_tls_opts opts = {.cert = mg_str(s_tls_cert),
|
|
// .key = mg_str(s_tls_key)};
|
|
// mg_tls_init(c, &opts);
|
|
// }
|
|
//
|
|
// // Client: verify server certificate and hostname
|
|
// if (ev == MG_EV_CONNECT) {
|
|
// struct mg_tls_opts opts = {.ca = mg_str(s_tls_ca),
|
|
// .name = mg_str("hostname")};
|
|
// mg_tls_init(c, &opts);
|
|
// }
|
|
// ```
|
|
// Full examples:
|
|
// tutorials/http/http-server, tutorials/http/http-client,
|
|
// tutorials/mqtt/mqtt-client-aws-iot, tutorials/websocket/websocket-client
|
|
// Related APIs:
|
|
// mg_http_listen(), mg_http_connect(), mg_ws_connect(), mg_mqtt_connect()
|
|
// Notes:
|
|
// Call from the user-supplied event handler on MG_EV_ACCEPT for servers or
|
|
// MG_EV_CONNECT for clients, before application data is sent. Servers usually
|
|
// set cert and key. Clients usually set ca and name; name enables SNI and
|
|
// hostname verification.
|
|
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts);
|
|
|
|
// Private API, do not expose
|
|
void mg_tls_free(struct mg_connection *);
|
|
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
|
|
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
|
|
size_t mg_tls_pending(struct mg_connection *);
|
|
void mg_tls_flush(struct mg_connection *);
|
|
void mg_tls_handshake(struct mg_connection *);
|
|
void mg_tls_ctx_init(struct mg_mgr *);
|
|
void mg_tls_ctx_free(struct mg_mgr *);
|
|
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
|
|
|
|
// Low-level IO primitives used by TLS layer
|
|
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
|
|
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
|
|
long mg_io_recv(struct mg_connection *c, void *buf, size_t len);
|
|
#ifndef TLS_X15519_H
|
|
#define TLS_X15519_H
|
|
|
|
|
|
|
|
#define X25519_BYTES 32
|
|
extern const uint8_t X25519_BASE_POINT[X25519_BYTES];
|
|
|
|
int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
|
|
const uint8_t x1[X25519_BYTES], int clamp);
|
|
|
|
|
|
#endif /* TLS_X15519_H */
|
|
/******************************************************************************
|
|
*
|
|
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
|
|
*
|
|
* This is a simple and straightforward implementation of AES-GCM authenticated
|
|
* encryption. The focus of this work was correctness & accuracy. It is written
|
|
* in straight 'C' without any particular focus upon optimization or speed. It
|
|
* should be endian (memory byte order) neutral since the few places that care
|
|
* are handled explicitly.
|
|
*
|
|
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
|
|
*
|
|
* It is intended for general purpose use, but was written in support of GRC's
|
|
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
|
|
*
|
|
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
|
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
|
|
* gcm/gcm-revised-spec.pdf
|
|
*
|
|
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
|
|
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
|
|
*
|
|
*******************************************************************************/
|
|
#ifndef TLS_AES128_H
|
|
#define TLS_AES128_H
|
|
|
|
/******************************************************************************
|
|
* AES_CONTEXT : cipher context / holds inter-call data
|
|
******************************************************************************/
|
|
typedef struct {
|
|
int mode; // 1 for Encryption, 0 for Decryption
|
|
int rounds; // keysize-based rounds count
|
|
uint32_t *rk; // pointer to current round key
|
|
uint32_t buf[68]; // key expansion buffer
|
|
} aes_context;
|
|
|
|
|
|
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
|
|
|
|
/******************************************************************************
|
|
* GCM_CONTEXT : MUST be called once before ANY use of this library
|
|
******************************************************************************/
|
|
int mg_gcm_initialize(void);
|
|
|
|
//
|
|
// aes-gcm.h
|
|
// MKo
|
|
//
|
|
// Created by Markus Kosmal on 20/11/14.
|
|
//
|
|
//
|
|
int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
|
|
size_t input_length, const unsigned char *key,
|
|
const size_t key_len, const unsigned char *iv,
|
|
const size_t iv_len, unsigned char *aead,
|
|
size_t aead_len, unsigned char *tag,
|
|
const size_t tag_len);
|
|
|
|
int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
|
|
size_t input_length, const unsigned char *key,
|
|
const size_t key_len, const unsigned char *iv,
|
|
const size_t iv_len, unsigned char *aead,
|
|
size_t aead_len, const unsigned char *tag,
|
|
const size_t tag_len);
|
|
|
|
#endif /* TLS_AES128_H */
|
|
|
|
// End of aes128 PD
|
|
|
|
|
|
|
|
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
|
|
|
#ifndef _UECC_H_
|
|
#define _UECC_H_
|
|
|
|
/* Platform selection options.
|
|
If MG_UECC_PLATFORM is not defined, the code will try to guess it based on
|
|
compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */
|
|
#define mg_uecc_arch_other 0
|
|
#define mg_uecc_x86 1
|
|
#define mg_uecc_x86_64 2
|
|
#define mg_uecc_arm 3
|
|
#define mg_uecc_arm_thumb 4
|
|
#define mg_uecc_arm_thumb2 5
|
|
#define mg_uecc_arm64 6
|
|
#define mg_uecc_avr 7
|
|
|
|
/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform
|
|
(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will
|
|
be automatically set based on your platform. */
|
|
|
|
/* Optimization level; trade speed for code size.
|
|
Larger values produce code that is faster but larger.
|
|
Currently supported values are 0 - 4; 0 is unusably slow for most
|
|
applications. Optimization level 4 currently only has an effect ARM platforms
|
|
where more than one curve is enabled. */
|
|
#ifndef MG_UECC_OPTIMIZATION_LEVEL
|
|
#define MG_UECC_OPTIMIZATION_LEVEL 2
|
|
#endif
|
|
|
|
/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a
|
|
specific function to be used for (scalar) squaring instead of the generic
|
|
multiplication function. This can make things faster somewhat faster, but
|
|
increases the code size. */
|
|
#ifndef MG_UECC_SQUARE_FUNC
|
|
#define MG_UECC_SQUARE_FUNC 0
|
|
#endif
|
|
|
|
/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will
|
|
switch to native little-endian format for *all* arrays passed in and out of the
|
|
public API. This includes public and private keys, shared secrets, signatures
|
|
and message hashes. Using this switch reduces the amount of call stack memory
|
|
used by uECC, since less intermediate translations are required. Note that this
|
|
will *only* work on native little-endian processors and it will treat the
|
|
uint8_t arrays passed into the public API as word arrays, therefore requiring
|
|
the provided byte arrays to be word aligned on architectures that do not support
|
|
unaligned accesses. IMPORTANT: Keys and signatures generated with
|
|
MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures
|
|
generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same
|
|
endianness. */
|
|
#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
|
|
#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0
|
|
#endif
|
|
|
|
/* Curve support selection. Set to 0 to remove that curve. */
|
|
#ifndef MG_UECC_SUPPORTS_secp160r1
|
|
#define MG_UECC_SUPPORTS_secp160r1 0
|
|
#endif
|
|
#ifndef MG_UECC_SUPPORTS_secp192r1
|
|
#define MG_UECC_SUPPORTS_secp192r1 0
|
|
#endif
|
|
#ifndef MG_UECC_SUPPORTS_secp224r1
|
|
#define MG_UECC_SUPPORTS_secp224r1 0
|
|
#endif
|
|
#ifndef MG_UECC_SUPPORTS_secp256r1
|
|
#define MG_UECC_SUPPORTS_secp256r1 1
|
|
#endif
|
|
#ifndef MG_UECC_SUPPORTS_secp256k1
|
|
#define MG_UECC_SUPPORTS_secp256k1 0
|
|
#endif
|
|
#ifndef MG_UECC_SUPPORTS_secp384r1
|
|
#define MG_UECC_SUPPORTS_secp384r1 1
|
|
#endif
|
|
|
|
/* Specifies whether compressed point format is supported.
|
|
Set to 0 to disable point compression/decompression functions. */
|
|
#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
#define MG_UECC_SUPPORT_COMPRESSED_POINT 1
|
|
#endif
|
|
|
|
struct MG_UECC_Curve_t;
|
|
typedef const struct MG_UECC_Curve_t *MG_UECC_Curve;
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#if MG_UECC_SUPPORTS_secp160r1
|
|
MG_UECC_Curve mg_uecc_secp160r1(void);
|
|
#endif
|
|
#if MG_UECC_SUPPORTS_secp192r1
|
|
MG_UECC_Curve mg_uecc_secp192r1(void);
|
|
#endif
|
|
#if MG_UECC_SUPPORTS_secp224r1
|
|
MG_UECC_Curve mg_uecc_secp224r1(void);
|
|
#endif
|
|
#if MG_UECC_SUPPORTS_secp256r1
|
|
MG_UECC_Curve mg_uecc_secp256r1(void);
|
|
#endif
|
|
#if MG_UECC_SUPPORTS_secp256k1
|
|
MG_UECC_Curve mg_uecc_secp256k1(void);
|
|
#endif
|
|
#if MG_UECC_SUPPORTS_secp384r1
|
|
MG_UECC_Curve mg_uecc_secp384r1(void);
|
|
#endif
|
|
|
|
/* MG_UECC_RNG_Function type
|
|
The RNG function should fill 'size' random bytes into 'dest'. It should return 1
|
|
if 'dest' was filled with random data, or 0 if the random data could not be
|
|
generated. The filled-in values should be either truly random, or from a
|
|
cryptographically-secure PRNG.
|
|
|
|
A correctly functioning RNG function must be set (using mg_uecc_set_rng())
|
|
before calling mg_uecc_make_key() or mg_uecc_sign().
|
|
|
|
Setting a correctly functioning RNG function improves the resistance to
|
|
side-channel attacks for mg_uecc_shared_secret() and
|
|
mg_uecc_sign_deterministic().
|
|
|
|
A correct RNG function is set by default when building for Windows, Linux, or OS
|
|
X. If you are building on another POSIX-compliant system that supports
|
|
/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined
|
|
RNG. For embedded platforms there is no predefined RNG function; you must
|
|
provide your own.
|
|
*/
|
|
typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size);
|
|
|
|
/* mg_uecc_set_rng() function.
|
|
Set the function that will be used to generate random bytes. The RNG function
|
|
should return 1 if the random data was generated, or 0 if the random data could
|
|
not be generated.
|
|
|
|
On platforms where there is no predefined RNG function (eg embedded platforms),
|
|
this must be called before mg_uecc_make_key() or mg_uecc_sign() are used.
|
|
|
|
Inputs:
|
|
rng_function - The function that will be used to generate random bytes.
|
|
*/
|
|
void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function);
|
|
|
|
/* mg_uecc_get_rng() function.
|
|
|
|
Returns the function that will be used to generate random bytes.
|
|
*/
|
|
MG_UECC_RNG_Function mg_uecc_get_rng(void);
|
|
|
|
/* mg_uecc_curve_private_key_size() function.
|
|
|
|
Returns the size of a private key for the curve in bytes.
|
|
*/
|
|
int mg_uecc_curve_private_key_size(MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_curve_public_key_size() function.
|
|
|
|
Returns the size of a public key for the curve in bytes.
|
|
*/
|
|
int mg_uecc_curve_public_key_size(MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_make_key() function.
|
|
Create a public/private key pair.
|
|
|
|
Outputs:
|
|
public_key - Will be filled in with the public key. Must be at least 2 *
|
|
the curve size (in bytes) long. For example, if the curve is secp256r1,
|
|
public_key must be 64 bytes long. private_key - Will be filled in with the
|
|
private key. Must be as long as the curve order; this is typically the same as
|
|
the curve size, except for secp160r1. For example, if the curve is secp256r1,
|
|
private_key must be 32 bytes long.
|
|
|
|
For secp160r1, private_key must be 21 bytes long! Note that
|
|
the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it
|
|
being non-zero).
|
|
|
|
Returns 1 if the key pair was generated successfully, 0 if an error occurred.
|
|
*/
|
|
int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key,
|
|
MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_shared_secret() function.
|
|
Compute a shared secret given your secret key and someone else's public key. If
|
|
the public key is not from a trusted source and has not been previously
|
|
verified, you should verify it first using mg_uecc_valid_public_key(). Note: It
|
|
is recommended that you hash the result of mg_uecc_shared_secret() before using
|
|
it for symmetric encryption or HMAC.
|
|
|
|
Inputs:
|
|
public_key - The public key of the remote party.
|
|
private_key - Your private key.
|
|
|
|
Outputs:
|
|
secret - Will be filled in with the shared secret value. Must be the same
|
|
size as the curve size; for example, if the curve is secp256r1, secret must be
|
|
32 bytes long.
|
|
|
|
Returns 1 if the shared secret was generated successfully, 0 if an error
|
|
occurred.
|
|
*/
|
|
int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
|
|
uint8_t *secret, MG_UECC_Curve curve);
|
|
|
|
#if MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
/* mg_uecc_compress() function.
|
|
Compress a public key.
|
|
|
|
Inputs:
|
|
public_key - The public key to compress.
|
|
|
|
Outputs:
|
|
compressed - Will be filled in with the compressed public key. Must be at
|
|
least (curve size + 1) bytes long; for example, if the curve is secp256r1,
|
|
compressed must be 33 bytes long.
|
|
*/
|
|
void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed,
|
|
MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_decompress() function.
|
|
Decompress a compressed public key.
|
|
|
|
Inputs:
|
|
compressed - The compressed public key.
|
|
|
|
Outputs:
|
|
public_key - Will be filled in with the decompressed public key.
|
|
*/
|
|
void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key,
|
|
MG_UECC_Curve curve);
|
|
#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
|
|
|
|
/* mg_uecc_valid_public_key() function.
|
|
Check to see if a public key is valid.
|
|
|
|
Note that you are not required to check for a valid public key before using any
|
|
other uECC functions. However, you may wish to avoid spending CPU time computing
|
|
a shared secret or verifying a signature using an invalid public key.
|
|
|
|
Inputs:
|
|
public_key - The public key to check.
|
|
|
|
Returns 1 if the public key is valid, 0 if it is invalid.
|
|
*/
|
|
int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_compute_public_key() function.
|
|
Compute the corresponding public key for a private key.
|
|
|
|
Inputs:
|
|
private_key - The private key to compute the public key for
|
|
|
|
Outputs:
|
|
public_key - Will be filled in with the corresponding public key
|
|
|
|
Returns 1 if the key was computed successfully, 0 if an error occurred.
|
|
*/
|
|
int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key,
|
|
MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_sign() function.
|
|
Generate an ECDSA signature for a given hash value.
|
|
|
|
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
|
|
pass it in to this function along with your private key.
|
|
|
|
Inputs:
|
|
private_key - Your private key.
|
|
message_hash - The hash of the message to sign.
|
|
hash_size - The size of message_hash in bytes.
|
|
|
|
Outputs:
|
|
signature - Will be filled in with the signature value. Must be at least 2 *
|
|
curve size long. For example, if the curve is secp256r1, signature must be 64
|
|
bytes long.
|
|
|
|
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
|
*/
|
|
int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash,
|
|
unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve);
|
|
|
|
/* MG_UECC_HashContext structure.
|
|
This is used to pass in an arbitrary hash function to
|
|
mg_uecc_sign_deterministic(). The structure will be used for multiple hash
|
|
computations; each time a new hash is computed, init_hash() will be called,
|
|
followed by one or more calls to update_hash(), and finally a call to
|
|
finish_hash() to produce the resulting hash.
|
|
|
|
The intention is that you will create a structure that includes
|
|
MG_UECC_HashContext followed by any hash-specific data. For example:
|
|
|
|
typedef struct SHA256_HashContext {
|
|
MG_UECC_HashContext uECC;
|
|
SHA256_CTX ctx;
|
|
} SHA256_HashContext;
|
|
|
|
void init_SHA256(MG_UECC_HashContext *base) {
|
|
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
SHA256_Init(&context->ctx);
|
|
}
|
|
|
|
void update_SHA256(MG_UECC_HashContext *base,
|
|
const uint8_t *message,
|
|
unsigned message_size) {
|
|
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
SHA256_Update(&context->ctx, message, message_size);
|
|
}
|
|
|
|
void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) {
|
|
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
|
SHA256_Final(hash_result, &context->ctx);
|
|
}
|
|
|
|
... when signing ...
|
|
{
|
|
uint8_t tmp[32 + 32 + 64];
|
|
SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64,
|
|
32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature);
|
|
}
|
|
*/
|
|
typedef struct MG_UECC_HashContext {
|
|
void (*init_hash)(const struct MG_UECC_HashContext *context);
|
|
void (*update_hash)(const struct MG_UECC_HashContext *context,
|
|
const uint8_t *message, unsigned message_size);
|
|
void (*finish_hash)(const struct MG_UECC_HashContext *context,
|
|
uint8_t *hash_result);
|
|
unsigned
|
|
block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */
|
|
unsigned
|
|
result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */
|
|
uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size +
|
|
block_size) bytes. */
|
|
} MG_UECC_HashContext;
|
|
|
|
/* mg_uecc_sign_deterministic() function.
|
|
Generate an ECDSA signature for a given hash value, using a deterministic
|
|
algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng()
|
|
before calling this function; however, if the RNG is defined it will improve
|
|
resistance to side-channel attacks.
|
|
|
|
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and
|
|
pass it to this function along with your private key and a hash context. Note
|
|
that the message_hash does not need to be computed with the same hash function
|
|
used by hash_context.
|
|
|
|
Inputs:
|
|
private_key - Your private key.
|
|
message_hash - The hash of the message to sign.
|
|
hash_size - The size of message_hash in bytes.
|
|
hash_context - A hash context to use.
|
|
|
|
Outputs:
|
|
signature - Will be filled in with the signature value.
|
|
|
|
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
|
*/
|
|
int mg_uecc_sign_deterministic(const uint8_t *private_key,
|
|
const uint8_t *message_hash, unsigned hash_size,
|
|
const MG_UECC_HashContext *hash_context,
|
|
uint8_t *signature, MG_UECC_Curve curve);
|
|
|
|
/* mg_uecc_verify() function.
|
|
Verify an ECDSA signature.
|
|
|
|
Usage: Compute the hash of the signed data using the same hash as the signer and
|
|
pass it to this function along with the signer's public key and the signature
|
|
values (r and s).
|
|
|
|
Inputs:
|
|
public_key - The signer's public key.
|
|
message_hash - The hash of the signed data.
|
|
hash_size - The size of message_hash in bytes.
|
|
signature - The signature value.
|
|
|
|
Returns 1 if the signature is valid, 0 if it is invalid.
|
|
*/
|
|
int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash,
|
|
unsigned hash_size, const uint8_t *signature,
|
|
MG_UECC_Curve curve);
|
|
|
|
#ifdef __cplusplus
|
|
} /* end of extern "C" */
|
|
#endif
|
|
|
|
#endif /* _UECC_H_ */
|
|
|
|
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
|
|
|
#ifndef _UECC_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_
|
|
|
|
//
|
|
//
|
|
|
|
/* Functions for raw large-integer manipulation. These are only available
|
|
if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */
|
|
#ifndef MG_UECC_ENABLE_VLI_API
|
|
#define MG_UECC_ENABLE_VLI_API 0
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#if MG_UECC_ENABLE_VLI_API
|
|
|
|
void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words);
|
|
|
|
/* Constant-time comparison to zero - secure way to compare long integers */
|
|
/* Returns 1 if vli == 0, 0 otherwise. */
|
|
mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli,
|
|
wordcount_t num_words);
|
|
|
|
/* Returns nonzero if bit 'bit' of vli is set. */
|
|
mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit);
|
|
|
|
/* Counts the number of bits required to represent vli. */
|
|
bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli,
|
|
const wordcount_t max_words);
|
|
|
|
/* Sets dest = src. */
|
|
void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src,
|
|
wordcount_t num_words);
|
|
|
|
/* Constant-time comparison function - secure way to compare long integers */
|
|
/* Returns one if left == right, zero otherwise */
|
|
mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right,
|
|
wordcount_t num_words);
|
|
|
|
/* Constant-time comparison function - secure way to compare long integers */
|
|
/* Returns sign of left - right, in constant time. */
|
|
cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, wordcount_t num_words);
|
|
|
|
/* Computes vli = vli >> 1. */
|
|
void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words);
|
|
|
|
/* Computes result = left + right, returning carry. Can modify in place. */
|
|
mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result,
|
|
const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = left - right, returning borrow. Can modify in place. */
|
|
mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
|
|
const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = left * right. Result must be 2 * num_words long. */
|
|
void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, wordcount_t num_words);
|
|
|
|
/* Computes result = left^2. Result must be 2 * num_words long. */
|
|
void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = (left + right) % mod.
|
|
Assumes that left < mod and right < mod, and that result does not overlap
|
|
mod. */
|
|
void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = (left - right) % mod.
|
|
Assumes that left < mod and right < mod, and that result does not overlap
|
|
mod. */
|
|
void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = product % mod, where product is 2N words long.
|
|
Currently only designed to work for mod == curve->p or curve_n. */
|
|
void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product,
|
|
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
|
|
/* Calculates result = product (mod curve->p), where product is up to
|
|
2 * curve->num_words long. */
|
|
void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product,
|
|
MG_UECC_Curve curve);
|
|
|
|
/* Computes result = (left * right) % mod.
|
|
Currently only designed to work for mod == curve->p or curve_n. */
|
|
void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, const mg_uecc_word_t *mod,
|
|
wordcount_t num_words);
|
|
|
|
/* Computes result = (left * right) % curve->p. */
|
|
void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result,
|
|
const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *right, MG_UECC_Curve curve);
|
|
|
|
/* Computes result = left^2 % mod.
|
|
Currently only designed to work for mod == curve->p or curve_n. */
|
|
void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left,
|
|
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
|
|
/* Computes result = left^2 % curve->p. */
|
|
void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
|
|
const mg_uecc_word_t *left,
|
|
MG_UECC_Curve curve);
|
|
|
|
/* Computes result = (1 / input) % mod.*/
|
|
void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input,
|
|
const mg_uecc_word_t *mod, wordcount_t num_words);
|
|
|
|
#if MG_UECC_SUPPORT_COMPRESSED_POINT
|
|
/* Calculates a = sqrt(a) (mod curve->p) */
|
|
void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve);
|
|
#endif
|
|
|
|
/* Converts an integer in uECC native format to big-endian bytes. */
|
|
void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
|
|
const mg_uecc_word_t *native);
|
|
/* Converts big-endian bytes to an integer in uECC native format. */
|
|
void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes,
|
|
int num_bytes);
|
|
|
|
unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve);
|
|
unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve);
|
|
unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve);
|
|
unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve);
|
|
unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve);
|
|
unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve);
|
|
|
|
const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve);
|
|
const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve);
|
|
const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve);
|
|
const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve);
|
|
|
|
int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve);
|
|
|
|
/* Multiplies a point by a scalar. Points are represented by the X coordinate
|
|
followed by the Y coordinate in the same array, both coordinates are
|
|
curve->num_words long. Note that scalar must be curve->num_n_words long (NOT
|
|
curve->num_words). */
|
|
void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
|
|
const mg_uecc_word_t *scalar, MG_UECC_Curve curve);
|
|
|
|
/* Generates a random integer in the range 0 < random < top.
|
|
Both random and top have num_words words. */
|
|
int mg_uecc_generate_random_int(mg_uecc_word_t *random,
|
|
const mg_uecc_word_t *top,
|
|
wordcount_t num_words);
|
|
|
|
#endif /* MG_UECC_ENABLE_VLI_API */
|
|
|
|
#ifdef __cplusplus
|
|
} /* end of extern "C" */
|
|
#endif
|
|
|
|
#endif /* _UECC_VLI_H_ */
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
#ifndef __PORTABLE_8439_H
|
|
#define __PORTABLE_8439_H
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
|
|
// provide your own decl specificier like -DPORTABLE_8439_DECL=ICACHE_RAM_ATTR
|
|
#ifndef PORTABLE_8439_DECL
|
|
#define PORTABLE_8439_DECL
|
|
#endif
|
|
|
|
/*
|
|
This library implements RFC 8439 a.k.a. ChaCha20-Poly1305 AEAD
|
|
|
|
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.
|
|
*/
|
|
|
|
/* 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
|
|
|
|
#define RFC_8439_TAG_SIZE (16)
|
|
#define RFC_8439_KEY_SIZE (32)
|
|
#define RFC_8439_NONCE_SIZE (12)
|
|
|
|
/*
|
|
Encrypt/Seal plain text bytes into a cipher text that can only be
|
|
decrypted by knowing the key, nonce and associated 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.
|
|
- plain_text: data to be encrypted, pointer + size should not overlap
|
|
with cipher_text pointer
|
|
|
|
output:
|
|
- cipher_text: encrypted plain_text with a tag appended. Make sure to
|
|
allocate at least plain_text_size + RFC_8439_TAG_SIZE
|
|
|
|
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 ad, size_t ad_size,
|
|
const uint8_t *restrict cipher_text, size_t cipher_text_size);
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifndef TLS_RSA_H
|
|
#define TLS_RSA_H
|
|
|
|
|
|
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz);
|
|
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
|
|
const uint8_t *dP, size_t dP_len,
|
|
const uint8_t *dQ, size_t dQ_len,
|
|
const uint8_t *p, size_t p_len,
|
|
const uint8_t *q, size_t q_len,
|
|
const uint8_t *qInv, size_t qInv_len,
|
|
uint8_t *signature, size_t sig_len);
|
|
bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
|
|
size_t hashlen);
|
|
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
|
|
#endif // TLS_RSA_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_TLS == MG_TLS_MBED
|
|
#include <mbedtls/debug.h>
|
|
#include <mbedtls/error.h>
|
|
#include <mbedtls/net_sockets.h>
|
|
#include <mbedtls/ssl.h>
|
|
#include <mbedtls/ssl_ticket.h>
|
|
|
|
struct mg_tls_ctx {
|
|
int dummy;
|
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
|
mbedtls_ssl_ticket_context tickets;
|
|
#endif
|
|
};
|
|
|
|
struct mg_tls {
|
|
mbedtls_x509_crt ca; // Parsed CA certificate
|
|
mbedtls_x509_crt cert; // Parsed certificate
|
|
mbedtls_pk_context pk; // Private key context
|
|
mbedtls_ssl_context ssl; // SSL/TLS context
|
|
mbedtls_ssl_config conf; // SSL/TLS config
|
|
#ifdef MBEDTLS_SSL_SESSION_TICKETS
|
|
mbedtls_ssl_ticket_context ticket; // Session tickets context
|
|
#endif
|
|
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
|
|
unsigned char *throttled_buf; // see #3074
|
|
size_t throttled_len;
|
|
bool check_name; // set when hostname was set, but no CA certificate given
|
|
};
|
|
#endif
|
|
|
|
|
|
#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
struct mg_tls {
|
|
BIO_METHOD *bm;
|
|
SSL_CTX *ctx;
|
|
SSL *ssl;
|
|
char *name; // matching hostname
|
|
bool check_name; // set when hostname was set, but no CA certificate given
|
|
};
|
|
#endif
|
|
|
|
|
|
// WebSocket frame opcodes, used in mg_ws_send() op parameter and
|
|
// in the low 4 bits of mg_ws_message.flags.
|
|
#define WEBSOCKET_OP_CONTINUE 0 // Continuation frame (fragmented message)
|
|
#define WEBSOCKET_OP_TEXT 1 // Text (UTF-8) data frame
|
|
#define WEBSOCKET_OP_BINARY 2 // Binary data frame
|
|
#define WEBSOCKET_OP_CLOSE 8 // Connection close control frame
|
|
#define WEBSOCKET_OP_PING 9 // Ping control frame (auto-replied with PONG)
|
|
#define WEBSOCKET_OP_PONG 10 // Pong control frame
|
|
|
|
|
|
|
|
// WebSocket message delivered as ev_data in MG_EV_WS_MSG and MG_EV_WS_CTL.
|
|
// data is a zero-copy slice into the receive buffer; valid until the next
|
|
// mg_mgr_poll() call.
|
|
struct mg_ws_message {
|
|
struct mg_str data; // Message payload
|
|
uint8_t flags; // First byte of the WS frame header: FIN (bit7) | opcode (bits3:0)
|
|
};
|
|
|
|
// Opens an outbound WebSocket connection.
|
|
//
|
|
// Returns:
|
|
// Client connection, or NULL on error.
|
|
// Example:
|
|
// mg_ws_connect(&mgr, "ws://localhost:8000/websocket", fn, NULL, NULL);
|
|
// Full examples:
|
|
// tutorials/websocket/websocket-client, tutorials/mqtt/mqtt-over-ws-client
|
|
// Related APIs:
|
|
// mg_ws_send(), mg_ws_printf(), mg_tls_init(), mg_ws_upgrade()
|
|
// Notes:
|
|
// url may use ws:// or wss://. Sends the HTTP Upgrade request immediately.
|
|
// fmt is a printf-style string for extra HTTP request headers; each header
|
|
// must end with "\r\n". The user-supplied fn event handler receives
|
|
// MG_EV_WS_OPEN on handshake success, MG_EV_WS_MSG for messages, and
|
|
// MG_EV_WS_CTL for control frames.
|
|
struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
|
|
mg_event_handler_t fn, void *fn_data,
|
|
const char *fmt, ...);
|
|
|
|
// Upgrades a server-side HTTP connection to WebSocket.
|
|
//
|
|
// Example:
|
|
// if (mg_match(hm->uri, mg_str("/websocket"), NULL)) {
|
|
// mg_ws_upgrade(c, hm, NULL);
|
|
// }
|
|
// Full examples:
|
|
// tutorials/websocket/websocket-server,
|
|
// tutorials/websocket/json-rpc-over-websocket, tutorials/core/timers
|
|
// Related APIs:
|
|
// mg_http_listen(), mg_match(), mg_ws_send(), mg_ws_printf()
|
|
// Notes:
|
|
// Call from an MG_EV_HTTP_MSG handler and pass that event's hm. fmt is a
|
|
// printf-style string for extra response headers; each header must end with
|
|
// "\r\n". Fires MG_EV_WS_OPEN immediately on success. Sends HTTP 426 and
|
|
// drains the connection if the request lacks Sec-WebSocket-Key.
|
|
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
|
|
const char *fmt, ...);
|
|
|
|
// Sends one WebSocket frame.
|
|
//
|
|
// Returns:
|
|
// Number of bytes appended to c->send; 0 if the frame header cannot be
|
|
// appended.
|
|
// Example:
|
|
// mg_ws_send(c, "hello", 5, WEBSOCKET_OP_TEXT);
|
|
// Full examples:
|
|
// tutorials/websocket/websocket-server, tutorials/websocket/websocket-client,
|
|
// tutorials/core/timers
|
|
// Related APIs:
|
|
// mg_ws_connect(), mg_ws_upgrade(), mg_ws_printf(), mg_ws_wrap()
|
|
// Notes:
|
|
// op is one of WEBSOCKET_OP_*. Client connections are automatically masked
|
|
// per RFC 6455. On OOM, the return value can be smaller than header + len.
|
|
// Data is appended to c->send and sent by a later mg_mgr_poll() call.
|
|
size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op);
|
|
|
|
// Wraps the last len bytes already in c->send with a WebSocket frame header
|
|
// and opcode op. Used internally by mg_ws_printf(); call it directly when you
|
|
// have written data into c->send manually and need to frame it.
|
|
// Returns c->send.len (total buffer size after the operation).
|
|
size_t mg_ws_wrap(struct mg_connection *, size_t len, int op);
|
|
|
|
// Formats a WebSocket message using printf-style fmt and sends it with
|
|
// opcode op (WEBSOCKET_OP_TEXT or WEBSOCKET_OP_BINARY).
|
|
// Returns the number of payload bytes written, or 0 on OOM.
|
|
size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
|
|
size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt,
|
|
va_list *);
|
|
// Time / SNTP functions
|
|
|
|
|
|
|
|
|
|
|
|
// Return milliseconds since boot (uptime). Never goes backwards.
|
|
// On RTOS targets (FreeRTOS, ThreadX, Zephyr, etc.) uses the RTOS tick count.
|
|
// On STM32/Cube uses HAL_GetTick(). On Arduino uses millis().
|
|
// On Linux/macOS uses CLOCK_MONOTONIC_RAW or CLOCK_MONOTONIC; falls back to
|
|
// CLOCK_REALTIME if neither is available.
|
|
uint64_t mg_millis(void);
|
|
|
|
// Boot timestamp in ms since Epoch. 0 until the first successful SNTP sync;
|
|
// updated automatically by mg_sntp_connect() on each successful response.
|
|
extern uint64_t mg_boot_timestamp_ms;
|
|
|
|
// Return milliseconds since Epoch: mg_millis() + mg_boot_timestamp_ms.
|
|
// Until a successful SNTP request completes, this is identical to mg_millis().
|
|
uint64_t mg_now(void);
|
|
|
|
// Checks whether a periodic deadline has expired.
|
|
//
|
|
// Returns:
|
|
// True when the timer has fired; false otherwise.
|
|
// Example:
|
|
// uint64_t timer = 0;
|
|
// if (mg_timer_expired(&timer, 1000, mg_millis())) {
|
|
// MG_INFO(("every second"));
|
|
// }
|
|
// Full examples:
|
|
// tutorials/http/http-server/arduino/teensy41-http,
|
|
// tutorials/mqtt/mqtt-client
|
|
// Related APIs:
|
|
// mg_millis(), mg_now()
|
|
// Notes:
|
|
// Initialise *expiration to 0 before first use. On expiry, this function
|
|
// advances *expiration by period and handles time wrap-around.
|
|
bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now);
|
|
|
|
// Connect to an SNTP server and send a time request.
|
|
// `url` defaults to "udp://time.google.com:123" when NULL.
|
|
// On success the internal boot timestamp is updated so that mg_now() returns
|
|
// the correct wall-clock time, and MG_EV_SNTP_TIME is fired on `fn` (if not
|
|
// NULL) with ev_data pointing to a uint64_t containing the epoch in milliseconds.
|
|
// Pass fn == NULL and fn_data == NULL for a fire-and-forget sync that only
|
|
// updates mg_boot_timestamp_ms. Typical polling usage:
|
|
//
|
|
// ```c
|
|
// uint64_t timer = 0;
|
|
// for (;;) {
|
|
// uint64_t period = mg_boot_timestamp_ms == 0 ? 1000 : 3600 * 1000;
|
|
// if (mg_timer_expired(&timer, period, mg_millis())) {
|
|
// mg_sntp_connect(&mgr, NULL, NULL, NULL);
|
|
// }
|
|
// mg_mgr_poll(&mgr, 1000);
|
|
// }
|
|
// ```
|
|
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Private API, do not expose
|
|
void mg_sntp_request(struct mg_connection *c);
|
|
int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
|
|
|
|
|
|
|
|
|
|
|
|
// MQTT packet type codes. The received command is in mg_mqtt_message.cmd.
|
|
#define MQTT_CMD_CONNECT 1
|
|
#define MQTT_CMD_CONNACK 2
|
|
#define MQTT_CMD_PUBLISH 3
|
|
#define MQTT_CMD_PUBACK 4
|
|
#define MQTT_CMD_PUBREC 5
|
|
#define MQTT_CMD_PUBREL 6
|
|
#define MQTT_CMD_PUBCOMP 7
|
|
#define MQTT_CMD_SUBSCRIBE 8
|
|
#define MQTT_CMD_SUBACK 9
|
|
#define MQTT_CMD_UNSUBSCRIBE 10
|
|
#define MQTT_CMD_UNSUBACK 11
|
|
#define MQTT_CMD_PINGREQ 12
|
|
#define MQTT_CMD_PINGRESP 13
|
|
#define MQTT_CMD_DISCONNECT 14
|
|
#define MQTT_CMD_AUTH 15
|
|
|
|
// MQTT5 property IDs. Used in mg_mqtt_prop.id.
|
|
#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01
|
|
#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02
|
|
#define MQTT_PROP_CONTENT_TYPE 0x03
|
|
#define MQTT_PROP_RESPONSE_TOPIC 0x08
|
|
#define MQTT_PROP_CORRELATION_DATA 0x09
|
|
#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B
|
|
#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11
|
|
#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12
|
|
#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13
|
|
#define MQTT_PROP_AUTHENTICATION_METHOD 0x15
|
|
#define MQTT_PROP_AUTHENTICATION_DATA 0x16
|
|
#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17
|
|
#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18
|
|
#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19
|
|
#define MQTT_PROP_RESPONSE_INFORMATION 0x1A
|
|
#define MQTT_PROP_SERVER_REFERENCE 0x1C
|
|
#define MQTT_PROP_REASON_STRING 0x1F
|
|
#define MQTT_PROP_RECEIVE_MAXIMUM 0x21
|
|
#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22
|
|
#define MQTT_PROP_TOPIC_ALIAS 0x23
|
|
#define MQTT_PROP_MAXIMUM_QOS 0x24
|
|
#define MQTT_PROP_RETAIN_AVAILABLE 0x25
|
|
#define MQTT_PROP_USER_PROPERTY 0x26
|
|
#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27
|
|
#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28
|
|
#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29
|
|
#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A
|
|
|
|
// Property value types used internally to encode/decode MQTT5 properties.
|
|
enum {
|
|
MQTT_PROP_TYPE_BYTE,
|
|
MQTT_PROP_TYPE_STRING,
|
|
MQTT_PROP_TYPE_STRING_PAIR,
|
|
MQTT_PROP_TYPE_BINARY_DATA,
|
|
MQTT_PROP_TYPE_VARIABLE_INT,
|
|
MQTT_PROP_TYPE_INT,
|
|
MQTT_PROP_TYPE_SHORT
|
|
};
|
|
|
|
// Return codes from mg_mqtt_parse().
|
|
enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
|
|
|
|
// A single MQTT5 property. Use in mg_mqtt_opts.props / mg_mqtt_opts.will_props
|
|
// arrays, and iterate received properties with mg_mqtt_next_prop().
|
|
struct mg_mqtt_prop {
|
|
uint8_t id; // Property ID, one of MQTT_PROP_*
|
|
uint32_t iv; // Integer value for BYTE, SHORT, INT, VARIABLE_INT types
|
|
struct mg_str key; // Property key string; only set for USER_PROPERTY pairs
|
|
struct mg_str val; // String/binary value; set for STRING, BINARY_DATA, and USER_PROPERTY
|
|
};
|
|
|
|
// Options passed to mg_mqtt_connect(), mg_mqtt_pub(), mg_mqtt_sub(),
|
|
// mg_mqtt_unsub(), and mg_mqtt_disconnect(). Zero-initialise and set only
|
|
// the fields relevant to the operation being called.
|
|
struct mg_mqtt_opts {
|
|
struct mg_str user; // CONNECT: username; empty = omit
|
|
struct mg_str pass; // CONNECT: password; empty = omit
|
|
struct mg_str client_id; // CONNECT: client ID; empty = auto-generate
|
|
struct mg_str topic; // CONNECT: will topic; PUBLISH/SUBSCRIBE/UNSUBSCRIBE: topic
|
|
struct mg_str message; // CONNECT: will payload; PUBLISH: message payload
|
|
uint8_t qos; // QoS level: 0, 1, or 2
|
|
uint8_t version; // MQTT version: 4 (3.1.1) or 5; 0 defaults to 4
|
|
uint16_t keepalive; // CONNECT: keepalive interval in seconds; 0 = no timeout
|
|
uint16_t retransmit_id; // PUBLISH: packet ID for retransmit; init to 0 for new message
|
|
bool retain; // PUBLISH/CONNECT will: set the retain flag
|
|
bool clean; // CONNECT: start a clean session
|
|
struct mg_mqtt_prop *props; // MQTT5: properties array; NULL if none
|
|
size_t num_props; // MQTT5: number of entries in props
|
|
struct mg_mqtt_prop *will_props; // MQTT5 CONNECT: will properties; NULL if none
|
|
size_t num_will_props; // MQTT5: number of entries in will_props
|
|
};
|
|
|
|
// Received MQTT message. Passed as ev_data for MG_EV_MQTT_MSG (PUBLISH),
|
|
// MG_EV_MQTT_CMD (all packets), and MG_EV_MQTT_OPEN (CONNACK).
|
|
// topic, data, and dgram are zero-copy slices into the receive buffer; they
|
|
// are only valid until the next mg_mgr_poll() call.
|
|
struct mg_mqtt_message {
|
|
struct mg_str topic; // PUBLISH: message topic; slice into dgram
|
|
struct mg_str data; // PUBLISH: message payload; slice into dgram
|
|
struct mg_str dgram; // Complete raw MQTT packet, including fixed header
|
|
uint16_t id; // Packet ID; set for QoS>0 PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK
|
|
uint8_t cmd; // Packet type, one of MQTT_CMD_*
|
|
uint8_t qos; // QoS level extracted from the PUBLISH fixed header
|
|
uint8_t ack; // CONNACK return code; 0 = success
|
|
size_t props_start; // MQTT5: byte offset from dgram.buf to start of properties
|
|
size_t props_size; // MQTT5: byte length of the properties section
|
|
};
|
|
|
|
// Opens an MQTT client connection and sends a CONNECT packet.
|
|
//
|
|
// Returns:
|
|
// Client connection, or NULL on error.
|
|
// Example:
|
|
// struct mg_mqtt_opts opts = {.client_id = mg_str("device1")};
|
|
// mg_mqtt_connect(&mgr, "mqtt://broker:1883", &opts, fn, NULL);
|
|
// Full examples:
|
|
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
|
|
// tutorials/mqtt/ota-over-mqtt
|
|
// Related APIs:
|
|
// mg_mqtt_pub(), mg_mqtt_sub(), mg_tls_init(), mg_mgr_poll()
|
|
// Notes:
|
|
// opts may be NULL to use MQTT 3.1.1 defaults with no auth and an auto client
|
|
// ID. The user-supplied fn event handler receives normal connection events,
|
|
// MG_EV_MQTT_OPEN when CONNACK is received, MG_EV_MQTT_MSG for incoming
|
|
// PUBLISH packets, and MG_EV_MQTT_CMD for every received MQTT packet.
|
|
struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
|
|
const struct mg_mqtt_opts *opts,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Creates an MQTT server listener on url.
|
|
// Fires MG_EV_MQTT_CMD for each complete packet received (ev_data: struct mg_mqtt_message *).
|
|
// Returns NULL on error.
|
|
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
// Sends a CONNECT packet on an already-open connection.
|
|
// Called automatically by mg_mqtt_connect(); exposed for advanced use such
|
|
// as re-authenticating on an existing connection.
|
|
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
|
|
|
|
// Sends an MQTT PUBLISH packet.
|
|
//
|
|
// Returns:
|
|
// 0 for QoS 0; assigned packet ID for QoS 1 or 2.
|
|
// Example:
|
|
// struct mg_mqtt_opts opts = {
|
|
// .topic = mg_str("device/status"),
|
|
// .message = mg_str("{\"ok\":true}"),
|
|
// .qos = 1,
|
|
// };
|
|
// uint16_t id = mg_mqtt_pub(c, &opts);
|
|
// Full examples:
|
|
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
|
|
// tutorials/mqtt/ota-over-mqtt
|
|
// Related APIs:
|
|
// mg_mqtt_connect(), mg_mqtt_sub(), mg_mqtt_send_header()
|
|
// Notes:
|
|
// On success, opts.topic and opts.message are copied into c->send before the
|
|
// function returns. To retransmit a QoS message, set opts.retransmit_id to the
|
|
// packet ID returned by the previous call; use 0 for a new message.
|
|
uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
|
|
|
|
// Sends an MQTT SUBSCRIBE packet.
|
|
//
|
|
// Example:
|
|
// struct mg_mqtt_opts opts = {.topic = mg_str("device/rx"), .qos = 1};
|
|
// mg_mqtt_sub(c, &opts);
|
|
// Full examples:
|
|
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
|
|
// tutorials/mqtt/ota-over-mqtt
|
|
// Related APIs:
|
|
// mg_mqtt_connect(), mg_mqtt_pub(), mg_mqtt_unsub()
|
|
// Notes:
|
|
// Send after MG_EV_MQTT_OPEN reports a successful CONNACK. Incoming PUBLISH
|
|
// messages are delivered to the user-supplied fn event handler as
|
|
// MG_EV_MQTT_MSG with ev_data pointing to struct mg_mqtt_message.
|
|
void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
|
|
|
|
// Sends an UNSUBSCRIBE packet for opts.topic.
|
|
void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
|
|
|
|
// Parses a raw MQTT packet in buf/len. version must be 4 (MQTT 3.1.1) or 5.
|
|
// On success, fills *m; topic, data, and dgram slice into buf (zero-copy).
|
|
// Returns MQTT_OK, MQTT_INCOMPLETE (need more data), or MQTT_MALFORMED.
|
|
int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
|
|
|
|
// Low-level helper: sends the MQTT fixed header (command byte and
|
|
// variable-length remaining-length field). Call before writing the PDU body.
|
|
// cmd: MQTT_CMD_*; flags: low 4 bits of the first byte; len: remaining length.
|
|
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
|
|
uint32_t len);
|
|
|
|
// Sends a PINGREQ packet.
|
|
void mg_mqtt_ping(struct mg_connection *);
|
|
|
|
// Sends a PINGRESP packet.
|
|
void mg_mqtt_pong(struct mg_connection *);
|
|
|
|
// Sends a DISCONNECT packet. For MQTT5, opts.props can carry reason
|
|
// properties. opts may be NULL for a plain disconnect with no properties.
|
|
void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *);
|
|
|
|
// Iterates MQTT5 properties in a received message. Start with ofs=0; pass
|
|
// the returned value back on each successive call. Returns 0 when done.
|
|
// Fills *prop with each property; key and val are zero-copy slices into msg->dgram.
|
|
size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *,
|
|
size_t ofs);
|
|
|
|
|
|
|
|
|
|
|
|
// DNS record types, used in mg_dns_rr.atype and mg_mdns_query().
|
|
#define MG_DNS_RTYPE_A 1 // IPv4 address (4 bytes)
|
|
#define MG_DNS_RTYPE_PTR 12 // Domain name pointer, used for reverse lookups and service discovery
|
|
#define MG_DNS_RTYPE_TXT 16 // Arbitrary text, used by DNS-SD for service metadata
|
|
#define MG_DNS_RTYPE_AAAA 28 // IPv6 address (16 bytes)
|
|
#define MG_DNS_RTYPE_SRV 33 // Service location: priority, weight, port, target hostname
|
|
|
|
// Mongoose sends DNS queries that contain only one question:
|
|
// either A (IPv4) or AAAA (IPv6) address lookup.
|
|
// Therefore, we expect zero or one answer.
|
|
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
|
|
struct mg_dns_message {
|
|
uint16_t txnid; // Transaction ID
|
|
bool resolved; // Resolve successful, addr is set
|
|
struct mg_addr addr; // Resolved address
|
|
char name[256]; // Host name
|
|
};
|
|
|
|
// DNS wire-format header (RFC 1035). All fields are in network byte order;
|
|
// use mg_ntohs() to read them.
|
|
struct mg_dns_header {
|
|
uint16_t txnid; // Transaction ID, matched between query and response
|
|
uint16_t flags; // Flags: bit15=QR (0=query,1=response), bit10=AA, bit8=RD
|
|
uint16_t num_questions; // Number of question records following the header
|
|
uint16_t num_answers; // Number of answer records in the response
|
|
uint16_t num_authority_prs; // Number of authority records
|
|
uint16_t num_other_prs; // Number of additional records
|
|
};
|
|
|
|
// Parsed DNS resource record fields, extracted from the wire format.
|
|
struct mg_dns_rr {
|
|
uint16_t nlen; // Byte length of the encoded name in the wire format
|
|
uint16_t atype; // Record type: MG_DNS_RTYPE_A=1, AAAA=28, PTR=12, SRV=33, TXT=16
|
|
uint16_t aclass; // Record class: 1=INET; mDNS queries: bit15=QU (unicast request)
|
|
uint16_t alen; // Byte length of the RDATA section (e.g. 4 for A, 16 for AAAA)
|
|
};
|
|
|
|
// DNS-SD service description. Used by the user to describe a service to advertise.
|
|
struct mg_dnssd_record {
|
|
struct mg_str srvcproto; // Service and protocol label, e.g. "_http._tcp"
|
|
struct mg_str txt; // TXT record contents, verbatim
|
|
uint16_t port; // Port number for the SRV record
|
|
};
|
|
|
|
// ev_data for MG_EV_MDNS_REQ. The handler must set is_resp=true and fill r
|
|
// (and optionally respname/addr) to send a response; leave is_resp=false to ignore.
|
|
struct mg_mdns_req {
|
|
struct mg_dns_rr *rr; // Parsed resource record from the incoming query
|
|
struct mg_dnssd_record *r; // User-supplied service record to include in the response
|
|
struct mg_str reqname; // Queried hostname, without the .local suffix
|
|
struct mg_str respname; // Hostname to use in response; defaults to fn_data if empty
|
|
struct mg_addr *addr; // IP address for A record; uses local interface if NULL
|
|
bool is_listing; // True if this is a service-discovery listing (_services._dns-sd._udp)
|
|
bool is_resp; // Set to true in the handler to trigger a response
|
|
bool is_unicast; // True if the client requested a unicast (QU) response
|
|
};
|
|
|
|
// ev_data for MG_EV_MDNS_RESP, carrying the resolved address from an mDNS response.
|
|
struct mg_mdns_resp {
|
|
struct mg_dns_rr *rr; // Resource record from the response
|
|
struct mg_str name; // Resolved hostname, without the .local suffix
|
|
struct mg_addr addr; // Resolved IP address
|
|
};
|
|
|
|
// Parses a DNS query or response from buf/len into dm. Returns true on success.
|
|
// On a response, sets dm->resolved=true and fills dm->addr if an A/AAAA record
|
|
// is found. Always fills dm->txnid and dm->name. Rejects packets larger than 512 bytes.
|
|
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm);
|
|
|
|
// Parses one DNS record at offset ofs in buf/len into rr. Set is_question=true
|
|
// for question records (no RDATA), false for answer records. Returns the number
|
|
// of bytes consumed (use to advance ofs), or 0 on error.
|
|
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
|
|
bool is_question, struct mg_dns_rr *rr);
|
|
|
|
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
|
|
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
|
|
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
|
|
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
|
|
void *fn_data);
|
|
|
|
// Sends an mDNS query on connection c (from mg_mdns_listen()) for name, with
|
|
// record type rtype (e.g. MG_DNS_RTYPE_A). Returns false on send error.
|
|
bool mg_mdns_query(struct mg_connection *c, const char *name, unsigned int rtype);
|
|
|
|
|
|
// Private API, do not expose or call in the user app
|
|
void mg_resolve(struct mg_connection *, const char *url);
|
|
void mg_resolve_cancel(struct mg_connection *);
|
|
|
|
|
|
|
|
|
|
// Modbus TCP function codes passed in mg_modbus_req.func.
|
|
#define MG_MODBUS_FUNC_READ_COILS 1 // Read 1-2000 discrete output coils
|
|
#define MG_MODBUS_FUNC_READ_DISCRETE_INPUTS 2 // Read 1-2000 discrete input coils
|
|
#define MG_MODBUS_FUNC_READ_HOLDING_REGISTERS 3 // Read 1-125 holding registers
|
|
#define MG_MODBUS_FUNC_READ_INPUT_REGISTERS 4 // Read 1-125 input registers
|
|
#define MG_MODBUS_FUNC_WRITE_SINGLE_COIL 5 // Write one output coil
|
|
#define MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER 6 // Write one holding register
|
|
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS 15 // Write 1-1968 output coils
|
|
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS 16 // Write 1-123 holding registers
|
|
|
|
// Standard Modbus exception codes. Set mg_modbus_req.error to one of these
|
|
// in the MG_EV_MODBUS_REQ handler to return an exception response to the client.
|
|
#define MG_MODBUS_ERR_NONE 0 // No error; send a normal response
|
|
#define MG_MODBUS_ERR_ILLEGAL_FUNCTION 1 // Function code not supported
|
|
#define MG_MODBUS_ERR_ILLEGAL_ADDRESS 2 // Starting address or range out of bounds
|
|
#define MG_MODBUS_ERR_ILLEGAL_VALUE 3 // Data value not accepted
|
|
#define MG_MODBUS_ERR_DEVICE_FAILURE 4 // Unrecoverable device error
|
|
|
|
// Modbus request passed as ev_data to the MG_EV_MODBUS_REQ event handler.
|
|
//
|
|
// For READ functions (READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS,
|
|
// READ_INPUT_REGISTERS): u.bits / u.regs points to a zeroed, heap-allocated
|
|
// array of len entries. The handler fills in the values to return.
|
|
//
|
|
// For WRITE functions: u.bits / u.regs is pre-populated from the client
|
|
// request. len=1 for single-write functions.
|
|
//
|
|
// Do NOT free u.bits or u.regs - the library frees them after the handler
|
|
// returns and the response has been sent.
|
|
//
|
|
// Set error to a non-zero MG_MODBUS_ERR_* code to send an exception response
|
|
// instead of a data response. The response is sent automatically on return.
|
|
struct mg_modbus_req {
|
|
uint8_t func; // Function code, one of MG_MODBUS_FUNC_*
|
|
uint8_t error; // Exception code; set to MG_MODBUS_ERR_* to return an error
|
|
uint16_t addr; // Starting coil/register address from the request
|
|
union {
|
|
bool *bits; // Coil values; used by coil function codes
|
|
uint16_t *regs; // Register values; used by register function codes
|
|
} u;
|
|
uint16_t len; // Number of entries in u.bits or u.regs
|
|
};
|
|
|
|
// Creates a Modbus TCP listener on url. Fires MG_EV_MODBUS_REQ for each
|
|
// complete request; ev_data is struct mg_modbus_req *. Returns the listening
|
|
// connection, or NULL on error.
|
|
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
|
|
mg_event_handler_t fn, void *fn_data);
|
|
|
|
|
|
|
|
|
|
|
|
// Maximum JSON nesting depth. mg_json_get() returns MG_JSON_TOO_DEEP if
|
|
// the path traverses more than this many nested objects/arrays.
|
|
#ifndef MG_JSON_MAX_DEPTH
|
|
#define MG_JSON_MAX_DEPTH 30
|
|
#endif
|
|
|
|
// Error codes returned by mg_json_get(). Successful returns are >= 0.
|
|
enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
|
|
|
|
// Low-level token locator. Finds the JSON token at path inside json and
|
|
// returns its byte offset in json.buf. Sets *toklen to the token length.
|
|
// toklen may be NULL if only a presence check is needed.
|
|
// path must start with "$" (root). Use ".key" for object fields and "[N]"
|
|
// for array indices. Example: "$.items[0].name"
|
|
// Returns MG_JSON_NOT_FOUND, MG_JSON_INVALID, or MG_JSON_TOO_DEEP on error.
|
|
int mg_json_get(struct mg_str json, const char *path, int *toklen);
|
|
|
|
// Returns the raw JSON token at path.
|
|
//
|
|
// Returns:
|
|
// Zero-copy mg_str slice into json.buf, or {NULL, 0} if not found or invalid.
|
|
// Example:
|
|
// struct mg_str tok = mg_json_get_tok(body, "$.device.name");
|
|
// Full examples:
|
|
// src/dash.c
|
|
// Related APIs:
|
|
// mg_json_get(), mg_json_get_str(), mg_json_unescape()
|
|
// Notes:
|
|
// The returned token is not NUL-terminated. String tokens include the
|
|
// surrounding double quotes and escape sequences; use mg_json_unescape() or
|
|
// mg_json_get_str() when you need decoded string content.
|
|
struct mg_str mg_json_get_tok(struct mg_str json, const char *path);
|
|
|
|
// Parses a numeric JSON value at path into *v.
|
|
// Returns true and sets *v on success. Returns false if not found or not a
|
|
// number. v may be NULL to check existence only.
|
|
bool mg_json_get_num(struct mg_str json, const char *path, double *v);
|
|
|
|
// Parses a boolean JSON value at path into *v.
|
|
// Returns true and sets *v on success. Returns false if not found or not a
|
|
// boolean literal. v may be NULL to check existence only.
|
|
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
|
|
|
|
// Returns a JSON number at path as a long, or dflt if not found or not a
|
|
// number. Use mg_json_get_num() when you need to distinguish missing from 0.
|
|
long mg_json_get_long(struct mg_str json, const char *path, long dflt);
|
|
|
|
// Returns a heap-allocated, NUL-terminated, JSON-unescaped copy of the string
|
|
// at path. Caller must mg_free() the result. Returns NULL if not found or the
|
|
// token is not a JSON string.
|
|
char *mg_json_get_str(struct mg_str json, const char *path);
|
|
|
|
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
|
|
// Sets *len to the decoded byte count. Caller must mg_free() the result.
|
|
// Returns NULL if not found or not a string.
|
|
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
|
|
|
|
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
|
|
// array. Sets *len to the decoded byte count. Caller must mg_free() the result.
|
|
// Returns NULL if not found or not a string.
|
|
char *mg_json_get_b64(struct mg_str json, const char *path, int *len);
|
|
|
|
// Writes a JSON-unescaped string value at path into a caller-supplied buffer.
|
|
//
|
|
// Returns:
|
|
// Number of bytes written excluding the NUL, or 0 on error, not found,
|
|
// non-string token, too-small buffer, or an empty string.
|
|
// Example:
|
|
// char name[32];
|
|
// mg_json_unescape(body, "$.device.name", name, sizeof(name));
|
|
// Full examples:
|
|
// src/dash.c, src/ota.c
|
|
// Related APIs:
|
|
// mg_json_get_tok(), mg_json_get_str(), mg_json_get_num()
|
|
// Notes:
|
|
// On success, NUL-terminates when n > 0. The destination buffer is owned by
|
|
// the caller. Only string tokens are unescaped; other token types return 0.
|
|
size_t mg_json_unescape(struct mg_str json, const char *path, char *, size_t);
|
|
|
|
// Sequential iterator over a JSON object or array. Start with ofs=0.
|
|
// Each call fills *key and *val with the next entry and returns the new ofs
|
|
// to pass on the next call. Returns 0 when there are no more entries.
|
|
// For arrays, *key is set to {NULL, 0}. For objects, *key is the raw
|
|
// quoted key (e.g. "\"name\""). *val is the raw unparsed token.
|
|
size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key,
|
|
struct mg_str *val);
|
|
|
|
|
|
|
|
|
|
// In-flight JSON-RPC request. Populate and pass to mg_rpc_process() to
|
|
// dispatch, or receive one pre-filled in a handler registered with mg_rpc_add().
|
|
struct mg_rpc_req {
|
|
struct mg_rpc **head; // Head of the handler list; allows handlers to call other RPCs
|
|
struct mg_rpc *rpc; // Handler that matched this request; set by mg_rpc_process()
|
|
mg_pfn_t pfn; // Output function for the response (e.g. mg_pfn_iobuf)
|
|
void *pfn_data; // Argument passed to pfn (e.g. pointer to an mg_iobuf)
|
|
void *req_data; // Arbitrary caller data, not touched by the RPC layer
|
|
struct mg_str frame; // Raw JSON-RPC frame, e.g. {"id":1,"method":"add","params":[1,2]}
|
|
};
|
|
|
|
// A registered JSON-RPC method handler. Managed by mg_rpc_add/del; do not
|
|
// allocate directly. method is a glob pattern matched via mg_match().
|
|
struct mg_rpc {
|
|
struct mg_rpc *next; // Next handler in the linked list
|
|
struct mg_str method; // Method name glob pattern (heap-allocated copy)
|
|
void (*fn)(struct mg_rpc_req *); // Handler function called on a match
|
|
void *fn_data; // Passed to fn as r->rpc->fn_data
|
|
};
|
|
|
|
// Allocates a handler entry and prepends it to *head. method_pattern is
|
|
// copied (mg_strdup). Glob patterns are supported, e.g. "get/*".
|
|
// To receive JSON-RPC responses ($.result / $.error), register with method=""
|
|
void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern,
|
|
void (*handler)(struct mg_rpc_req *), void *handler_data);
|
|
|
|
// Removes and frees all handlers in *head whose fn matches handler.
|
|
// Pass handler=NULL to remove every handler in the list.
|
|
void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
|
|
|
|
// Dispatches r->frame to the matching handler. Looks up "$.method" and calls
|
|
// the first handler whose pattern matches. If "$.result" or "$.error" is found
|
|
// instead (i.e. r->frame is a JSON-RPC response), dispatches to the "" handler.
|
|
// Sends a -32601 "not found" error if no handler matches, or -32700 on parse failure.
|
|
void mg_rpc_process(struct mg_rpc_req *);
|
|
|
|
// Sends a JSON-RPC success response: {"id":<id>,"result":<fmt>}.
|
|
// fmt is a printf-style format string written to r->pfn; NULL sends "null".
|
|
// Does nothing if the request frame has no "id" field (notification).
|
|
void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
|
|
void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
|
|
|
|
// Sends a JSON-RPC error response: {"id":<id>,"error":{"code":<code>,"message":<fmt>}}.
|
|
// fmt is a printf-style format string; NULL sends "null" as the message.
|
|
// The "id" field is omitted if the request had none; the error is always sent.
|
|
void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
|
|
void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
|
|
|
|
// Built-in handler: responds with a JSON array of all registered method names.
|
|
// Register it as a method handler, e.g. mg_rpc_add(&head, mg_str("rpc.list"), mg_rpc_list, NULL);
|
|
void mg_rpc_list(struct mg_rpc_req *r);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef MG_DASH_MAX_USER_NAME
|
|
#define MG_DASH_MAX_USER_NAME 32
|
|
#endif
|
|
|
|
#ifndef MG_HTTP_ADDR
|
|
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
|
|
#define MG_HTTP_ADDR "http://0.0.0.0:8000"
|
|
#define MG_HTTPS_ADDR "https://0.0.0.0:8443"
|
|
#define MG_MODBUS_ADDR "tcp://0.0.0.0:8502"
|
|
#else
|
|
#define MG_HTTP_ADDR "http://0.0.0.0:80"
|
|
#define MG_HTTPS_ADDR "http://0.0.0.0:443"
|
|
#define MG_MODBUS_ADDR "tcp://0.0.0.0:502"
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MG_DASH_MAX_SEND_BUF_SIZE
|
|
#define MG_DASH_MAX_SEND_BUF_SIZE (MG_IO_SIZE * 5)
|
|
#endif
|
|
|
|
// C type of a value bound to a dashboard field, see struct mg_field
|
|
enum mg_val_type {
|
|
MG_VAL_INT, // int
|
|
MG_VAL_UINT64, // uint64_t
|
|
MG_VAL_BOOL, // bool
|
|
MG_VAL_DBL, // double
|
|
MG_VAL_STR, // Zero-terminated string
|
|
MG_VAL_RAW, // Pre-formatted JSON value, copied verbatim
|
|
};
|
|
|
|
// Dashboard field: binds a JSON key to a C variable for read and/or write
|
|
struct mg_field {
|
|
const char *name; // JSON key
|
|
enum mg_val_type type; // C type of the value, see enum mg_val_type
|
|
void *value; // Pointer to the bound variable
|
|
size_t value_size; // sizeof(*value) for type checking; 0 makes it read-only
|
|
};
|
|
|
|
// Operation passed to a struct mg_field_set read/write/delete/add callback.
|
|
// MG_DASH_ADD: fields hold the values of a new element to append; *index is
|
|
// not set. Return true to accept and persist it, false to reject (e.g. a cap
|
|
// was reached)
|
|
enum mg_dash_op { MG_DASH_READ, MG_DASH_WRITE, MG_DASH_DELETE, MG_DASH_ADD };
|
|
|
|
struct mg_dash;
|
|
|
|
// Authenticated (or guest) dashboard user
|
|
struct mg_dash_user {
|
|
struct mg_dash_user *next; // Next user in the list
|
|
char name[MG_DASH_MAX_USER_NAME]; // Username
|
|
char token[21]; // Session token, sent to the client as a cookie
|
|
int level; // Access level, see struct mg_dash::authenticate
|
|
uint64_t expire; // Session expiration time, see mg_millis()
|
|
struct mg_dash *dash; // Dashboard this user belongs to
|
|
};
|
|
|
|
// Named group of related dashboard fields, e.g. "settings" or "metrics"
|
|
struct mg_field_set {
|
|
const char *name; // Set name, used as a JSON key
|
|
struct mg_field *fields; // Zero-terminated array of fields
|
|
bool (*fn)(enum mg_dash_op op,
|
|
struct mg_dash_user *u); // Read/write/delete/add callback
|
|
int *index; // Non-NULL: array set. *index is set before fn(READ) call;
|
|
// fn sets *index = -1 to signal end of iteration.
|
|
// Size query: framework sets *index = -1 before fn(READ); fn
|
|
// sets *index = total size
|
|
bool (*get_dir)(const struct mg_dash_user *u, char *buf,
|
|
size_t len); // Non-NULL: file array
|
|
struct mg_field_set *next; // Next set in the list
|
|
};
|
|
|
|
// Custom URI handler, registered with MG_DASH_REGISTER_CUSTOM_HANDLER()
|
|
struct mg_dash_custom_handler {
|
|
struct mg_dash_custom_handler *next; // Next handler in the list
|
|
struct mg_str uri_pattern; // URI to match, see mg_match()
|
|
mg_event_handler_t handler; // Event handler function
|
|
void *handler_data; // Opaque data for the handler
|
|
};
|
|
|
|
// Dashboard instance: fields, users and handlers served over HTTP and WS
|
|
struct mg_dash {
|
|
struct mg_field_set *sets; // Field sets, see MG_DASH_ADD_FIELD_SET()
|
|
struct mg_dash_custom_handler *custom_handlers; // Custom URI handlers
|
|
// Validates pass, fills in user, returns access level (<= 0: failure).
|
|
// NULL: no login required, all clients get guest access
|
|
int (*authenticate)(char *user, size_t userlen, const char *pass);
|
|
int session_auto_expiration_seconds; // Session lifetime, 0: 1 hour default
|
|
struct mg_dash_user *guest; // Used when authenticate == NULL
|
|
struct mg_fs *upload_fs; // FS for uploads/listing, default &mg_fs_posix
|
|
};
|
|
|
|
#define MG_DASH_ADD_FIELD_SET(dash_, set_) \
|
|
do { \
|
|
(set_)->next = (dash_)->sets; \
|
|
(dash_)->sets = (set_); \
|
|
} while (0)
|
|
|
|
#define MG_DASH_REGISTER_CUSTOM_HANDLER(dash_, uri_, fn_, data_) \
|
|
do { \
|
|
static struct mg_dash_custom_handler ch_; \
|
|
ch_.next = (dash_)->custom_handlers; \
|
|
ch_.uri_pattern = mg_str(uri_); \
|
|
ch_.handler = (fn_); \
|
|
ch_.handler_data = (data_); \
|
|
(dash_)->custom_handlers = &ch_; \
|
|
} while (0)
|
|
|
|
// Dashboard event handler. Pass to mg_http_listen() as the fn argument
|
|
void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data);
|
|
|
|
// Notify WebSocket clients that a field set changed: broadcasts the new
|
|
// array size if *set->index < 0, or a single element update otherwise
|
|
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *);
|
|
|
|
// Default reader for file-backed array sets (set->get_dir != NULL)
|
|
// Usage example - this is a complete implemenation of the file manager:
|
|
// ```c
|
|
// static struct file {
|
|
// int index;
|
|
// char name[64];
|
|
// size_t size;
|
|
// uint64_t checksum;
|
|
// } s_file;
|
|
// static struct mg_field_set set_files;
|
|
//
|
|
// static bool get_dir(const struct mg_dash_user *u, char *buf, size_t len) {
|
|
// (void) u;
|
|
// mkdir("/tmp/dashboard", 0755);
|
|
// mg_snprintf(buf, len, "%s", "/tmp/dashboard");
|
|
// return true;
|
|
// }
|
|
//
|
|
// static struct mg_field fields_files[] = {
|
|
// {"name", MG_VAL_STR, s_file.name, sizeof(s_file.name)},
|
|
// {"size", MG_VAL_UINT64, &s_file.size, 0},
|
|
// {"checksum", MG_VAL_UINT64, &s_file.checksum, sizeof(s_file.checksum)},
|
|
// {NULL, MG_VAL_INT, NULL, 0},
|
|
// };
|
|
//
|
|
// static bool files_fn(enum mg_dash_op op, struct mg_dash_user *u) {
|
|
// if (op == MG_DASH_WRITE) return s_uploads_enabled && u->level >= 7;
|
|
// if (op != MG_DASH_READ) return false;
|
|
// if (!mg_dash_dir_read(&set_files, u)) return false;
|
|
// s_file.checksum = 0; // Mock to show how to set custom fields
|
|
// return true;
|
|
// }
|
|
//
|
|
// static struct mg_field_set set_files = {"files", fields_files, files_fn, &s_file.index, get_dir, NULL};
|
|
// ```
|
|
bool mg_dash_dir_read(struct mg_field_set *set, struct mg_dash_user *u);
|
|
|
|
// Helper forward declarations for Mongoose CMSIS pack modules
|
|
extern struct mg_mgr g_mgr;
|
|
extern void mg_dash_init(struct mg_mgr *);
|
|
extern void mg_dash_poll(struct mg_mgr *);
|
|
extern void mg_mqtt_init(struct mg_mgr *);
|
|
extern void mg_mqtt_poll(struct mg_mgr *);
|
|
// Firmware OTA updates
|
|
//
|
|
// This is a low-level OTA API, not intended to be called directly by users.
|
|
// Users are provided with a higher-level API:
|
|
// - defining MG_OTA_URL enables HTTP pull-based updates
|
|
// - mg_http_start_ota() enables push-based updates
|
|
//
|
|
// However, it is possible to use the mg_ota_* API directly. Below is the
|
|
// example code of a function that is called repeatedly. It expects the
|
|
// last chink of size 0, which marks the end of the OTA process
|
|
//
|
|
// ```c
|
|
// static void ota(size_t total, size_t offset, void *buf, size_t len) {
|
|
// bool ok = false;
|
|
// if (ofs == 0 && (ok = mg_ota_begin(total)) == false) {
|
|
// mg_ota_end();
|
|
// } else if (len > 0 && (ok = mg_ota_write(buf, len)) == false) {
|
|
// mg_ota_end();
|
|
// } else if (len == 0) {}
|
|
// ok = mg_ota_end();
|
|
// }
|
|
// return ok;
|
|
// }
|
|
// ```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Supported OTA targets
|
|
#define MG_OTA_NONE 0 // No OTA support
|
|
#define MG_OTA_STM32H5 1 // STM32 H5
|
|
#define MG_OTA_STM32H7 2 // STM32 H7
|
|
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
|
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
|
#define MG_OTA_CH32V307 100 // WCH CH32V307
|
|
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
|
#define MG_OTA_RT1020 300 // IMXRT1020
|
|
#define MG_OTA_RT1050 301 // IMXRT1050
|
|
#define MG_OTA_RT1060 302 // IMXRT1060
|
|
#define MG_OTA_RT1064 303 // IMXRT1064
|
|
#define MG_OTA_RT1170 304 // IMXRT1170
|
|
#define MG_OTA_MCXN 310 // MCXN947
|
|
#define MG_OTA_RW612 320 // FRDM-RW612
|
|
#define MG_OTA_FLASH 900 // OTA via internal flash
|
|
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
|
|
#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash
|
|
#define MG_OTA_CUSTOM 1000 // Custom implementation
|
|
|
|
// OTA target. Settable in mongoose_config.h
|
|
#ifndef MG_OTA
|
|
#define MG_OTA MG_OTA_NONE
|
|
#else
|
|
#ifndef MG_IRAM
|
|
#if defined(__GNUC__)
|
|
// Places code block in .iram ELF section. A linker script can place it in RAM
|
|
#define MG_IRAM __attribute__((noinline, section(".iram")))
|
|
#else
|
|
#define MG_IRAM
|
|
#endif // compiler
|
|
#endif // IRAM
|
|
#endif // OTA
|
|
|
|
// Starts the OTA process. Called once at the beginning of a firmware update.
|
|
bool mg_ota_begin(size_t new_firmware_size);
|
|
|
|
// Writes the next firmware chunk. Called repeatedly until all data is written.
|
|
bool mg_ota_write(const void *buf, size_t len);
|
|
|
|
// Ends the OTA process and commits the written firmware.
|
|
bool mg_ota_end(void);
|
|
|
|
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
|
|
// default calls swap_fn() (no-op on single-bank) then resets. Override in
|
|
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
|
|
#ifndef MG_OTA_ROLLBACK
|
|
#define MG_OTA_ROLLBACK() \
|
|
do { \
|
|
mg_flash->swap_fn(); \
|
|
NVIC_SystemReset(); \
|
|
} while (0)
|
|
#endif
|
|
|
|
// Arm a hardware watchdog. mg_ota_poll() feeds it while in MG_OTA_TESTING state;
|
|
// if the event loop hangs, it fires, resets the device, and MG_OTA_FAILED
|
|
// triggers MG_OTA_ROLLBACK() on next boot. Timeout is arch-defined.
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
#define MG_OTA_ROLLBACK_TIMER_START()
|
|
#endif
|
|
|
|
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
#define MG_OTA_ROLLBACK_TIMER_FEED()
|
|
#endif
|
|
|
|
// OTA state values for MG_OTA_STATE_GET / MG_OTA_STATE_SET.
|
|
// MG_OTA_CONFIRMED: firmware is committed, normal operation.
|
|
// MG_OTA_TESTING: new firmware booted for the first time; IWDG is armed.
|
|
// If the device reboots while in this state, it transitions
|
|
// to MG_OTA_FAILED and rolls back on the next boot.
|
|
// MG_OTA_FAILED: previous boot did not commit in time; rollback on next boot.
|
|
// MG_OTA_ROLLEDBACK: device rolled back to previous firmware. Sent to the OTA
|
|
// server as boot=3 on every poll until a new OTA succeeds,
|
|
// which overwrites this state with MG_OTA_TESTING.
|
|
enum {
|
|
MG_OTA_CONFIRMED = 0,
|
|
MG_OTA_TESTING = 1,
|
|
MG_OTA_FAILED = 2,
|
|
MG_OTA_ROLLEDBACK = 3
|
|
};
|
|
|
|
// Persistent OTA state storage. Override in mongoose_config.h to persist state across resets.
|
|
#ifndef MG_OTA_STATE_GET
|
|
#define MG_OTA_STATE_GET() 0
|
|
#endif
|
|
#ifndef MG_OTA_STATE_SET
|
|
#define MG_OTA_STATE_SET(val) (void) (val)
|
|
#endif
|
|
|
|
// Evaluation window for new firmware in seconds. mg_ota_poll() feeds the
|
|
// rollback watchdog while in MG_OTA_TESTING state and within this window;
|
|
// when it expires, auto-commits if the OTA server confirmed the same version,
|
|
// otherwise stops feeding and the watchdog triggers a rollback.
|
|
#ifndef MG_OTA_ROLLBACK_TIMEOUT_SECONDS
|
|
#define MG_OTA_ROLLBACK_TIMEOUT_SECONDS 120
|
|
#endif
|
|
|
|
// Call once at boot, after clock and backup-domain init (e.g. after HAL_Init()
|
|
// + SystemClock_Config() on CubeMX projects, or after hal_clock_init() on
|
|
// Mongoose tutorials). The backup domain must be write-accessible so the state
|
|
// transition TESTING → FAILED persists across resets.
|
|
// Arms the rollback watchdog if new firmware is in TESTING state, rolls back
|
|
// immediately if FAILED. mg_ota_poll() feeds the watchdog and auto-commits
|
|
// when the server confirms the same version within the window.
|
|
// boot (TESTING) → set FAILED → arm IWDG → run firmware
|
|
// ├── server ok + window expires → set CONFIRMED → IWDG resets cleanly
|
|
// └── no server ok + window expires → stop feeding → IWDG fires → FAILED → rollback
|
|
#define MG_OTA_BOOT_CHECK() \
|
|
do { \
|
|
if (MG_OTA_STATE_GET() == MG_OTA_FAILED) { \
|
|
MG_OTA_STATE_SET(MG_OTA_ROLLEDBACK); \
|
|
MG_INFO(("Commit deadline expired, rolling back")); \
|
|
MG_OTA_ROLLBACK(); \
|
|
} else if (MG_OTA_STATE_GET() == MG_OTA_TESTING) { \
|
|
MG_OTA_STATE_SET(MG_OTA_FAILED); \
|
|
MG_INFO(("New firmware: commit within %u sec or rolls back", \
|
|
(unsigned) MG_OTA_ROLLBACK_TIMEOUT_SECONDS)); \
|
|
MG_OTA_ROLLBACK_TIMER_START(); \
|
|
} \
|
|
} while (0)
|
|
|
|
// Pull-based OTA over HTTP. Fetches metadata_url, which must return a JSON
|
|
// object like: { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
|
|
// If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads
|
|
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
|
|
// NULL on successful flash, "Same version" when already up to date, or an
|
|
// error string on failure. Pass NULL to use the default handler which logs
|
|
// failures via MG_ERROR.
|
|
void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
|
|
void (*fn)(const char *error_message));
|
|
|
|
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
|
|
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
|
|
// Settable in mongoose_config.h
|
|
#ifndef MG_OTA_URL
|
|
#define MG_OTA_URL NULL
|
|
#endif
|
|
|
|
// OTA status callback function for mg_ota_poll()
|
|
#ifndef MG_OTA_STATUS_FN
|
|
#define MG_OTA_STATUS_FN NULL
|
|
#endif
|
|
|
|
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
|
|
#ifndef MG_OTA_FIRMWARE_VERSION
|
|
#define MG_OTA_FIRMWARE_VERSION "1.0.0"
|
|
#endif
|
|
|
|
// Maximum version string length
|
|
#ifndef MG_OTA_MAX_VERSION_LEN
|
|
#define MG_OTA_MAX_VERSION_LEN 64
|
|
#endif
|
|
|
|
// How often mg_ota_poll() checks for a firmware update, in seconds
|
|
#ifndef MG_OTA_PULL_INTERVAL_SECONDS
|
|
#define MG_OTA_PULL_INTERVAL_SECONDS 60
|
|
#endif
|
|
|
|
// Set to 1 to define a custom mg_ota_device_id(). Settable in mongoose_config.h
|
|
#ifndef MG_ENABLE_CUSTOM_DEVICE_ID
|
|
#define MG_ENABLE_CUSTOM_DEVICE_ID 0
|
|
#endif
|
|
|
|
// Populates buf with a unique device ID string.
|
|
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
|
|
void mg_ota_device_id(char *buf, size_t len);
|
|
|
|
// Checks for a firmware update over HTTP. Called automatically by mg_mgr_poll()
|
|
// when MG_OTA_URL is set in mongoose_config.h. Do not call directly.
|
|
void mg_ota_poll(struct mg_mgr *);
|
|
|
|
|
|
|
|
|
|
#if (MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM) || MG_ENABLE_LFS
|
|
|
|
struct mg_flash {
|
|
void *start; // Address at which flash starts
|
|
size_t size; // Flash size
|
|
size_t secsz; // Sector size
|
|
size_t align; // Write alignment
|
|
bool (*write_fn)(void *, const void *, size_t); // Write function
|
|
bool (*swap_fn)(void); // Swap partitions
|
|
};
|
|
|
|
extern struct mg_flash *mg_flash;
|
|
bool mg_lfs_init(size_t size);
|
|
|
|
bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash);
|
|
bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash);
|
|
bool mg_ota_flash_end(struct mg_flash *flash);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct mg_wifi_data {
|
|
char *ssid, *pass; // STA mode, SSID to connect to
|
|
char *apssid, *appass; // AP mode, our SSID
|
|
uint32_t apip, apmask; // AP mode, our IP address and mask
|
|
uint8_t security; // STA mode, TBD
|
|
uint8_t apsecurity; // AP mode, TBD
|
|
uint8_t apchannel; // AP mode, channel to use
|
|
bool apmode; // start in AP mode; 'false' -> connect to 'ssid' != NULL
|
|
};
|
|
|
|
struct mg_wifi_scan_bss_data {
|
|
struct mg_str SSID;
|
|
char *BSSID;
|
|
int16_t RSSI;
|
|
uint8_t security;
|
|
#define MG_WIFI_SECURITY_OPEN 0
|
|
#define MG_WIFI_SECURITY_WEP MG_BIT(0)
|
|
#define MG_WIFI_SECURITY_WPA MG_BIT(1)
|
|
#define MG_WIFI_SECURITY_WPA2 MG_BIT(2)
|
|
#define MG_WIFI_SECURITY_WPA3 MG_BIT(3)
|
|
#define MG_WIFI_SECURITY_WPA_ENTERPRISE MG_BIT(4)
|
|
#define MG_WIFI_SECURITY_WPA2_ENTERPRISE MG_BIT(5)
|
|
#define MG_WIFI_SECURITY_WPA3_ENTERPRISE MG_BIT(6)
|
|
uint8_t channel;
|
|
unsigned band : 2;
|
|
#define MG_WIFI_BAND_2G 0
|
|
#define MG_WIFI_BAND_5G 1
|
|
unsigned has_n : 1;
|
|
unsigned has_ac : 1;
|
|
unsigned has_ax : 1;
|
|
};
|
|
|
|
bool mg_wifi_scan(void);
|
|
bool mg_wifi_connect(struct mg_wifi_data *);
|
|
bool mg_wifi_disconnect(void);
|
|
bool mg_wifi_ap_start(struct mg_wifi_data *);
|
|
bool mg_wifi_ap_stop(void);
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_ENABLE_TCPIP
|
|
|
|
// no config defaults to 0 => Ethernet
|
|
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
|
|
|
#if defined(__DCC__)
|
|
#pragma pack(1)
|
|
#else
|
|
#pragma pack(push, 1)
|
|
#endif
|
|
|
|
struct mg_l2addr {
|
|
union {
|
|
uint8_t mac[6]; // ieee48
|
|
uint64_t ieee64; // PPP ifcid
|
|
uint16_t ieee16;
|
|
} addr;
|
|
};
|
|
|
|
// L2 using L2 (PPPoE) must inherit the lower layer first, so they overlap
|
|
struct eth_data {
|
|
uint16_t vlan_id;
|
|
};
|
|
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
|
struct eth_data eth;
|
|
};
|
|
|
|
union mg_l2data {
|
|
struct eth_data eth;
|
|
struct pppoe_data pppoe;
|
|
};
|
|
|
|
#if defined(__DCC__)
|
|
#pragma pack(0)
|
|
#else
|
|
#pragma pack(pop)
|
|
#endif
|
|
|
|
|
|
enum mg_l2proto {
|
|
MG_TCPIP_L2PROTO_IPV4 = 0,
|
|
MG_TCPIP_L2PROTO_IPV6,
|
|
MG_TCPIP_L2PROTO_ARP,
|
|
MG_TCPIP_L2PROTO_PPPoE_DISC,
|
|
MG_TCPIP_L2PROTO_PPPoE_SESS
|
|
};
|
|
enum mg_l2addrtype {
|
|
MG_TCPIP_L2ADDR_BCAST,
|
|
MG_TCPIP_L2ADDR_MCAST,
|
|
MG_TCPIP_L2ADDR_MCAST6
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_ENABLE_TCPIP
|
|
|
|
// Mongoose TCP/IP network interface
|
|
struct mg_tcpip_if;
|
|
|
|
// Hardware driver interface. Implement all function pointers to plug in a
|
|
// new network device. Polling drivers set rx; interrupt/DMA drivers set
|
|
// rx=NULL and push frames via mg_tcpip_qwrite() from their ISR instead.
|
|
struct mg_tcpip_driver {
|
|
bool (*init)(struct mg_tcpip_if *); // One-time hardware init; return false on failure
|
|
size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit one raw frame; return bytes sent
|
|
size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Read one raw frame; return frame size, 0 if none. NULL for interrupt-driven drivers
|
|
bool (*poll)(struct mg_tcpip_if *, bool); // Check link status; second arg is true once per second. Return true=up, false=down
|
|
};
|
|
|
|
// Event handler called by the TCP/IP stack for network-level events.
|
|
// ev is one of MG_TCPIP_EV_*; ev_data type is documented per event below.
|
|
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
|
void *ev_data);
|
|
|
|
// clang-format off
|
|
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
|
// Each entry shows the ev_data type to cast inside the handler.
|
|
enum {
|
|
MG_TCPIP_EV_STATE_CHANGE, // IPv4 link state changed uint8_t *(&ifp->state)
|
|
MG_TCPIP_EV_DHCP_DNS, // DHCP assigned a DNS server uint32_t *ipaddr
|
|
MG_TCPIP_EV_DHCP_SNTP, // DHCP assigned an SNTP server uint32_t *ipaddr
|
|
MG_TCPIP_EV_ARP, // Raw ARP frame received struct mg_str *
|
|
MG_TCPIP_EV_TIMER_1S, // 1-second periodic tick NULL
|
|
MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi BSS scan entry struct mg_wifi_scan_bss_data *
|
|
MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan finished NULL
|
|
MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connection failed driver and chip specific
|
|
MG_TCPIP_EV_DRIVER, // Driver-defined event driver specific
|
|
MG_TCPIP_EV_STATE6_CHANGE, // IPv6 link state changed uint8_t *(&ifp->state6)
|
|
MG_TCPIP_EV_USER // Starting ID for user-defined events
|
|
};
|
|
// clang-format on
|
|
|
|
// Network interface. Zero-initialise, then set the fields marked "user-set"
|
|
// before calling mg_tcpip_init(). Fields in the "internal state" section
|
|
// below are managed by the stack; read them freely but do not modify them.
|
|
struct mg_tcpip_if {
|
|
// User-set configuration
|
|
uint8_t mac[sizeof(struct mg_l2addr)]; // MAC address; must be set to a valid address
|
|
uint32_t ip, mask, gw; // Static IPv4 address, netmask, gateway; 0 = use DHCP
|
|
struct mg_str tx; // TX frame buffer (managed by the stack)
|
|
bool enable_dhcp_client; // Enable DHCP client; auto-set if ip==0
|
|
bool enable_dhcp_server; // Enable DHCP server
|
|
bool enable_get_gateway; // DHCP server offers itself as the default gateway
|
|
bool enable_req_dns; // DHCP client requests a DNS server address
|
|
bool enable_req_sntp; // DHCP client requests an SNTP server address
|
|
bool enable_fcs_check; // Verify and strip FCS from received frames
|
|
bool enable_mac_check; // Drop frames not addressed to this MAC
|
|
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
|
|
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
|
|
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
|
|
void *driver_data; // Passed to all driver functions as ifp->driver_data
|
|
mg_tcpip_event_handler_t pfn; // Driver-level event handler; called before fn
|
|
mg_tcpip_event_handler_t fn; // User event handler for MG_TCPIP_EV_* events
|
|
struct mg_mgr *mgr; // Mongoose event manager; set by mg_tcpip_init()
|
|
struct mg_queue recv_queue; // Incoming frame queue; pre-size by setting recv_queue.size
|
|
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 framesize; // Maximum L2 frame size in bytes
|
|
uint16_t l2mtu; // L2 frame payload, default net MTU
|
|
|
|
#if MG_ENABLE_IPV6
|
|
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses
|
|
uint8_t prefix[8]; // Global prefix bytes
|
|
uint8_t prefix_len; // Global prefix length in bits
|
|
uint64_t gw6[2]; // IPv6 default gateway
|
|
bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration)
|
|
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
|
bool is_ip6_changed; // Set by stack when IPv6 address changes
|
|
#endif
|
|
|
|
// Internal state - read-only for users, managed by the stack
|
|
uint8_t gwmac[sizeof(struct mg_l2addr)]; // Gateway hardware address (resolved via ARP)
|
|
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
|
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
|
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
|
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
|
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
|
uint64_t lease_expire; // DHCP lease expiry time in ms
|
|
uint16_t eport; // Next ephemeral port to assign
|
|
volatile uint32_t ndrop; // Frames received but dropped (queue full)
|
|
volatile uint32_t nrecv; // Total frames received
|
|
volatile uint32_t nsent; // Total frames transmitted
|
|
volatile uint32_t nerr; // Driver-reported transmit errors
|
|
uint8_t state; // Current IPv4 link/IP state (MG_TCPIP_STATE_*)
|
|
#define MG_TCPIP_STATE_DOWN 0 // Physical link is down
|
|
#define MG_TCPIP_STATE_LINK_UP 1 // Driver reports physical link is up
|
|
#define MG_TCPIP_STATE_UP 2 // L2 is ready (e.g. PPP negotiated)
|
|
#define MG_TCPIP_STATE_REQ 3 // DHCP REQUESTING: waiting for DHCP offer
|
|
#define MG_TCPIP_STATE_IP 4 // IP address assigned; resolving gateway MAC
|
|
#define MG_TCPIP_STATE_READY 5 // Fully operational; gateway MAC resolved
|
|
bool gw_ready; // Gateway MAC address has been resolved via ARP
|
|
|
|
#if MG_ENABLE_IPV6
|
|
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
|
|
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
|
bool gw6_ready; // IPv6 gateway MAC address has been resolved
|
|
#endif
|
|
};
|
|
|
|
// Initialises the TCP/IP stack. Calls driver->init(), allocates the TX frame
|
|
// buffer (ifp->framesize bytes) and the RX queue. If ifp->ip == 0 and the
|
|
// L2 type is not PPP, DHCP client is enabled automatically.
|
|
// Must be called once after populating ifp->driver, ifp->mac, and any
|
|
// static IP/mask/gw values.
|
|
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
|
|
|
|
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
|
|
void mg_tcpip_free(struct mg_tcpip_if *);
|
|
|
|
// Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven
|
|
// or DMA-based drivers (those with rx=NULL) to hand frames to the stack from
|
|
// an ISR or DMA completion callback. Drops the frame and increments ndrop if
|
|
// the queue is full.
|
|
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
|
|
|
|
// Sends a broadcast ARP who-has request for ip. mac may be NULL for a plain
|
|
// broadcast, or a non-NULL hint to set the target MAC field in the request.
|
|
void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac);
|
|
|
|
// Built-in hardware drivers. Assign one to mg_tcpip_if.driver before calling
|
|
// mg_tcpip_init(). Each driver also requires a corresponding driver_data
|
|
// struct; see the matching driver header for details.
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_stm32f;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_w5500;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_w5100;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_tm4c;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_tms570;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_stm32h;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_imxrt;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_same54;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_cmsis;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_ra;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_xmc;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_xmc7;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
|
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
|
|
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
|
// Populate and assign to the driver's driver_data field.
|
|
struct mg_tcpip_spi {
|
|
void *spi; // Opaque SPI bus handle passed to all callbacks
|
|
void (*begin)(void *); // Assert chip-select (start of transaction)
|
|
void (*end)(void *); // Deassert chip-select (end of transaction)
|
|
void (*txn)(void *, uint8_t *write, uint8_t *read, size_t); // Full-duplex transfer: write and read len bytes simultaneously
|
|
};
|
|
|
|
// Alignment attributes for DMA-capable Ethernet descriptor buffers.
|
|
// Use these on driver RX/TX descriptor arrays where the hardware requires
|
|
// specific alignment.
|
|
#ifndef MG_8BYTE_ALIGNED
|
|
#if defined(__GNUC__)
|
|
#define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
|
|
#else
|
|
#define MG_8BYTE_ALIGNED
|
|
#endif // compiler
|
|
#endif // 8BYTE_ALIGNED
|
|
|
|
#ifndef MG_16BYTE_ALIGNED
|
|
#if defined(__GNUC__)
|
|
#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
|
|
#else
|
|
#define MG_16BYTE_ALIGNED
|
|
#endif // compiler
|
|
#endif // 16BYTE_ALIGNED
|
|
|
|
#ifndef MG_32BYTE_ALIGNED
|
|
#if defined(__GNUC__)
|
|
#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
|
|
#else
|
|
#define MG_32BYTE_ALIGNED
|
|
#endif // compiler
|
|
#endif // 32BYTE_ALIGNED
|
|
|
|
#ifndef MG_64BYTE_ALIGNED
|
|
#if defined(__GNUC__)
|
|
#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
|
|
#else
|
|
#define MG_64BYTE_ALIGNED
|
|
#endif // compiler
|
|
#endif // 64BYTE_ALIGNED
|
|
|
|
// Memory section attribute for Ethernet frame buffers. Define to
|
|
// __attribute__((section(".eth_ram"))) or similar if the Ethernet DMA
|
|
// requires buffers in a specific RAM region.
|
|
#ifndef MG_ETH_RAM
|
|
#define MG_ETH_RAM
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if MG_ENABLE_BSD_SOCKETS
|
|
|
|
#ifndef MG_ENABLE_BSD_PROTOTYPES
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <poll.h>
|
|
#include <sys/select.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/un.h>
|
|
#else
|
|
#include <errno.h>
|
|
typedef unsigned int socklen_t;
|
|
typedef uint32_t in_addr_t;
|
|
struct in_addr { in_addr_t s_addr; };
|
|
struct in6_addr { uint8_t s6_addr[16]; };
|
|
struct sockaddr { uint16_t sa_family; char sa_data[14]; };
|
|
struct sockaddr_in {
|
|
uint16_t sin_family;
|
|
uint16_t sin_port;
|
|
struct in_addr sin_addr;
|
|
char sin_zero[8];
|
|
};
|
|
struct sockaddr_in6 {
|
|
uint16_t sin6_family;
|
|
uint16_t sin6_port;
|
|
uint32_t sin6_flowinfo;
|
|
struct in6_addr sin6_addr;
|
|
uint32_t sin6_scope_id;
|
|
};
|
|
struct sockaddr_storage { uint16_t ss_family; char __ss_pad[126]; };
|
|
struct hostent {
|
|
char *h_name;
|
|
char **h_aliases;
|
|
int h_addrtype;
|
|
int h_length;
|
|
char **h_addr_list;
|
|
};
|
|
#define h_addr h_addr_list[0]
|
|
struct addrinfo {
|
|
int ai_flags;
|
|
int ai_family;
|
|
int ai_socktype;
|
|
int ai_protocol;
|
|
socklen_t ai_addrlen;
|
|
struct sockaddr *ai_addr;
|
|
char *ai_canonname;
|
|
struct addrinfo *ai_next;
|
|
};
|
|
struct pollfd { int fd; short events; short revents; };
|
|
#define AF_INET 2
|
|
#define AF_INET6 10
|
|
#define AF_UNSPEC 0
|
|
#define PF_INET AF_INET
|
|
#define PF_INET6 AF_INET6
|
|
#define PF_UNSPEC AF_UNSPEC
|
|
#define SOCK_STREAM 1
|
|
#define SOCK_DGRAM 2
|
|
#define IPPROTO_TCP 6
|
|
#define IPPROTO_UDP 17
|
|
#define INADDR_ANY 0
|
|
#define INADDR_LOOPBACK 0x7f000001
|
|
#define SOL_SOCKET 0xffff
|
|
#define SO_REUSEADDR 2
|
|
#define SO_REUSEPORT 15
|
|
#define SO_KEEPALIVE 9
|
|
#define SO_ERROR 4
|
|
#define SO_BROADCAST 6
|
|
#define SO_RCVBUF 8
|
|
#define SO_SNDBUF 7
|
|
#define TCP_NODELAY 1
|
|
#define MSG_DONTWAIT 0x40
|
|
#define MSG_NOSIGNAL 0x4000
|
|
#define MSG_PEEK 0x02
|
|
#define F_GETFL 3
|
|
#define F_SETFL 4
|
|
#define O_NONBLOCK 0x0004
|
|
#define SHUT_RD 0
|
|
#define SHUT_WR 1
|
|
#define SHUT_RDWR 2
|
|
#define AI_PASSIVE 0x0001
|
|
#define NI_MAXHOST 1025
|
|
#define NI_MAXSERV 32
|
|
#ifndef EAGAIN
|
|
#define EAGAIN 11
|
|
#endif
|
|
#ifndef EWOULDBLOCK
|
|
#define EWOULDBLOCK EAGAIN
|
|
#endif
|
|
#ifndef EINPROGRESS
|
|
#define EINPROGRESS 36
|
|
#endif
|
|
#ifndef ENOTCONN
|
|
#define ENOTCONN 107
|
|
#endif
|
|
#ifndef ECONNREFUSED
|
|
#define ECONNREFUSED 111
|
|
#endif
|
|
#ifndef ECONNRESET
|
|
#define ECONNRESET 104
|
|
#endif
|
|
#ifndef EADDRINUSE
|
|
#define EADDRINUSE 98
|
|
#endif
|
|
#ifndef ETIMEDOUT
|
|
#define ETIMEDOUT 110
|
|
#endif
|
|
#define POLLIN 0x001
|
|
#define POLLOUT 0x004
|
|
#define POLLERR 0x008
|
|
#define POLLHUP 0x010
|
|
#define POLLNVAL 0x020
|
|
uint16_t htons(uint16_t);
|
|
uint16_t ntohs(uint16_t);
|
|
uint32_t htonl(uint32_t);
|
|
uint32_t ntohl(uint32_t);
|
|
#endif // MG_ENABLE_BSD_PROTOTYPES
|
|
|
|
#define closesocket(a) close(a)
|
|
|
|
#ifndef MG_BSD_BACKLOG
|
|
#define MG_BSD_BACKLOG 5
|
|
#endif
|
|
|
|
#ifndef MG_BSD_CONNECT_TIMEOUT_MS
|
|
#define MG_BSD_CONNECT_TIMEOUT_MS 10000
|
|
#endif
|
|
|
|
// Mongoose-specific API
|
|
void mg_bsd_init(void); // must be called before socket()
|
|
void mg_bsd_poll(struct mg_mgr *); // process pending BSD commands
|
|
|
|
// Transport backend: implement these OR define MG_ENABLE_FREERTOS for the built-in backend
|
|
void *mg_bsd_transport_new(int domain, int type, int proto);
|
|
void mg_bsd_transport_free(void *t);
|
|
int mg_bsd_transport_listen(void *t, const struct sockaddr_in *addr);
|
|
void *mg_bsd_transport_accept(void *t, struct sockaddr_in *peer, bool nonblock);
|
|
ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock);
|
|
ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock);
|
|
int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock);
|
|
void mg_bsd_transport_close(void *t);
|
|
|
|
// Standard BSD socket API
|
|
int socket(int, int, int);
|
|
int bind(int, const struct sockaddr *, socklen_t);
|
|
int listen(int, int);
|
|
int accept(int, struct sockaddr *, socklen_t *);
|
|
int connect(int, const struct sockaddr *, socklen_t);
|
|
ssize_t send(int, const void *, size_t, int);
|
|
ssize_t recv(int, void *, size_t, int);
|
|
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
|
|
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
|
|
int close(int);
|
|
int shutdown(int, int);
|
|
int fcntl(int, int, int);
|
|
int setsockopt(int, int, int, const void *, socklen_t);
|
|
int getsockopt(int, int, int, void *, socklen_t *);
|
|
int getsockname(int, struct sockaddr *, socklen_t *);
|
|
int getpeername(int, struct sockaddr *, socklen_t *);
|
|
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
|
|
int poll(struct pollfd *, unsigned int, int);
|
|
struct hostent *gethostbyname(const char *);
|
|
int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **);
|
|
void freeaddrinfo(struct addrinfo *);
|
|
int inet_pton(int, const char *, void *);
|
|
const char *inet_ntop(int, const void *, char *, socklen_t);
|
|
in_addr_t inet_addr(const char *);
|
|
char *inet_ntoa(struct in_addr);
|
|
|
|
#endif // MG_ENABLE_BSD_SOCKETS
|
|
|
|
|
|
struct mg_tcpip_driver_atcmd_data {
|
|
void *usart; // Opaque UART/SPI descriptor
|
|
void (*reset)(void *); // Modem hardware reset
|
|
void (*tx)(void *, uint8_t); // USART transmit single byte
|
|
int (*rx)(void *); // USART receive single byte
|
|
const char **script; // List of AT commands and expected replies
|
|
int script_index; // Index of the current AT command in the list
|
|
uint64_t deadline; // AT command deadline in ms
|
|
bool no_byte_stuff; // Do not perform byte stuffing/unstuffing
|
|
bool link; // internal use: done with AT processing
|
|
bool unstuffing; // internal use: unstuffing state
|
|
};
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS
|
|
|
|
#include "Driver_ETH_MAC.h" // keep this include
|
|
#include "Driver_ETH_PHY.h" // keep this include
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && \
|
|
((defined(MG_ENABLE_DRIVER_CYW) && MG_ENABLE_DRIVER_CYW) || \
|
|
(defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO))
|
|
|
|
struct mg_tcpip_driver_cyw_firmware {
|
|
const uint8_t *code_addr;
|
|
size_t code_len;
|
|
const uint8_t *nvram_addr;
|
|
size_t nvram_len;
|
|
const uint8_t *clm_addr;
|
|
size_t clm_len;
|
|
};
|
|
|
|
struct mg_tcpip_driver_cyw_data {
|
|
struct mg_wifi_data wifi;
|
|
void *bus;
|
|
struct mg_tcpip_driver_cyw_firmware *fw;
|
|
bool hs; // use chip "high-speed" mode; otherwise SPI CPOL0 CPHA0 (DS 4.2.3 Table 6)
|
|
};
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_cyw_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_cyw; \
|
|
mif_.driver_data = &driver_data_; \
|
|
mif_.recv_queue.size = 8192; \
|
|
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: cyw, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && \
|
|
(defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \
|
|
(defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) || \
|
|
(defined(MG_ENABLE_DRIVER_MCXE) && MG_ENABLE_DRIVER_MCXE)
|
|
|
|
struct mg_tcpip_driver_imxrt_data {
|
|
// MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk),
|
|
// must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz
|
|
// 37.5.1.8.2, Table 37-46 : f = ipg_clk / (2(mdc_cr + 1))
|
|
// ipg_clk mdc_cr VALUE
|
|
// --------------------------
|
|
// -1 <-- TODO() tell driver to guess the value
|
|
// 25 MHz 4
|
|
// 33 MHz 6
|
|
// 40 MHz 7
|
|
// 50 MHz 9
|
|
// 66 MHz 13
|
|
int mdc_cr; // Valid values: -1 to 63
|
|
|
|
uint8_t phy_addr; // PHY address
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 2
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 24
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_imxrt_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_imxrt; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: imxrt, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && \
|
|
defined(MG_ENABLE_DRIVER_NXP_WIFI) && MG_ENABLE_DRIVER_NXP_WIFI
|
|
|
|
|
|
struct mg_tcpip_driver_nxp_wifi_data {
|
|
struct mg_wifi_data wifi;
|
|
};
|
|
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_nxp_wifi_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_nxp_wifi; \
|
|
mif_.driver_data = &driver_data_; \
|
|
mif_.recv_queue.size = 8192; \
|
|
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: nxp wifi, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
struct mg_phy {
|
|
uint16_t (*read_reg)(uint8_t addr, uint8_t reg);
|
|
void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value);
|
|
};
|
|
|
|
// PHY configuration settings, bitmask
|
|
enum {
|
|
// Set if PHY LEDs are connected to ground
|
|
MG_PHY_LEDS_ACTIVE_HIGH = (1 << 0),
|
|
// Set when PHY clocks MAC. Otherwise, MAC clocks PHY
|
|
MG_PHY_CLOCKS_MAC = (1 << 1)
|
|
};
|
|
|
|
enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M };
|
|
|
|
void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t config);
|
|
bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex,
|
|
uint8_t *speed);
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && MG_ARCH == MG_ARCH_PICOSDK && \
|
|
defined(MG_ENABLE_DRIVER_PICO_W) && MG_ENABLE_DRIVER_PICO_W
|
|
|
|
#include "cyw43.h" // keep this include
|
|
#include "pico/cyw43_arch.h" // keep this include
|
|
#include "pico/unique_id.h" // keep this include
|
|
|
|
struct mg_tcpip_driver_pico_w_data {
|
|
struct mg_wifi_data wifi;
|
|
};
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_pico_w_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_pico_w; \
|
|
mif_.driver_data = &driver_data_; \
|
|
mif_.recv_queue.size = 8192; \
|
|
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: pico-w, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && \
|
|
(defined(MG_ENABLE_DRIVER_RA6) && MG_ENABLE_DRIVER_RA6) || \
|
|
(defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8)
|
|
|
|
struct mg_tcpip_driver_ra_data {
|
|
// MDC clock "divider". MDC clock is software generated,
|
|
uint32_t clock; // core clock frequency in Hz
|
|
uint16_t irqno; // IRQn, R_ICU->IELSR[irqno]
|
|
uint8_t phy_addr; // PHY address
|
|
};
|
|
|
|
#ifndef MG_DRIVER_CLK_FREQ
|
|
#define MG_DRIVER_CLK_FREQ 100000000UL
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_IRQ_NO
|
|
#define MG_DRIVER_IRQ_NO 0
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 0
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_ra_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.clock = MG_DRIVER_CLK_FREQ; \
|
|
driver_data_.irqno = MG_DRIVER_IRQ_NO; \
|
|
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_ra; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: ra, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612) && MG_ENABLE_DRIVER_RW612
|
|
|
|
struct mg_tcpip_driver_rw612_data {
|
|
// 38.1.8 MII Speed Control Register (MSCR)
|
|
// MDC clock frequency must not exceed 2.5 MHz and is calculated as follows:
|
|
// MDC_freq = P_clock / ((mdc_cr + 1) * 2), where P_clock is the
|
|
// peripheral bus clock.
|
|
// IEEE802.3 clause 22 defines a minimum of 10 ns for the hold time on the
|
|
// MDIO output. Depending on the host bus frequency, the setting may need
|
|
// to be increased.
|
|
int mdc_cr;
|
|
int mdc_holdtime; // Valid values: [0-7]
|
|
uint8_t phy_addr;
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 2
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 51
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_HOLDTIME
|
|
#define MG_DRIVER_MDC_HOLDTIME 3
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_rw612_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_rw612; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: rw612, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \
|
|
MG_ENABLE_DRIVER_SAME54
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 5
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_PHY_ADDR
|
|
#define MG_DRIVER_PHY_ADDR 0
|
|
#endif
|
|
|
|
struct mg_tcpip_driver_same54_data {
|
|
int mdc_cr;
|
|
uint8_t phy_addr;
|
|
};
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_same54_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
|
driver_data_.phy_addr = MG_DRIVER_PHY_ADDR; \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_same54; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: same54, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && \
|
|
(defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO)
|
|
|
|
// Specific chip/card driver --> SDIO driver --> HAL --> SDIO hw controller
|
|
|
|
// API with HAL for hardware controller
|
|
// - Provide a function to init the controller (external)
|
|
// - Provide these functions:
|
|
struct mg_tcpip_sdio {
|
|
void *sdio; // Opaque SDIO bus descriptor
|
|
void (*cfg)(void *, uint8_t); // select operating parameters
|
|
// SDIO transaction: send cmd with a possible 1-byte read or write
|
|
bool (*txn)(void *, uint8_t cmd, uint32_t arg, uint32_t *r);
|
|
// SDIO extended transaction: write or read len bytes, using blksz blocks
|
|
bool (*xfr)(void *, bool write, uint32_t arg, uint16_t blksz, uint32_t *,
|
|
uint32_t len, uint32_t *r);
|
|
};
|
|
|
|
// API with driver (e.g.: cyw.c)
|
|
// Once the hardware controller has been initialized:
|
|
// - Init card: selects the card, sets F0 block size, sets bus width and speed
|
|
bool mg_sdio_init(struct mg_tcpip_sdio *sdio);
|
|
// - Enable other possible functions (F1 to F7)
|
|
bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f);
|
|
// - Wait for them to be ready
|
|
bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f);
|
|
// - Set their transfer block length
|
|
bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f,
|
|
uint16_t blksz);
|
|
// - Transfer data to/from a function (abstracts from transaction type)
|
|
// - Requesting a read transfer > blocksize means block transfer will be used.
|
|
// - Drivers must have room to accomodate a whole block transfer, see sdio.c
|
|
// - Transfers of > 1 byte --> (uint32_t *) data. 1-byte --> (uint8_t *) data
|
|
bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
|
|
uint32_t addr, void *data, uint32_t len);
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
|
|
MG_ENABLE_DRIVER_ST67W6
|
|
|
|
struct mg_tcpip_driver_st67w6_data {
|
|
struct mg_wifi_data wifi;
|
|
void *spi;
|
|
bool (*is_ready)(void); // return state of module RDY pin
|
|
struct mg_queue send_queue; // decouple tx calls from module polls
|
|
};
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_st67w6_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_st67w6; \
|
|
mif_.driver_data = &driver_data_; \
|
|
mif_.recv_queue.size = 8192; \
|
|
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
|
|
MG_ENABLE_DRIVER_STM32F
|
|
|
|
struct mg_tcpip_driver_stm32f_data {
|
|
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
|
|
// HCLK range DIVIDER mdc_cr VALUE
|
|
// -------------------------------------
|
|
// -1 <-- tell driver to guess the value
|
|
// 60-100 MHz HCLK/42 0
|
|
// 100-150 MHz HCLK/62 1
|
|
// 20-35 MHz HCLK/16 2
|
|
// 35-60 MHz HCLK/26 3
|
|
// 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
|
|
// 216-310 MHz HCLK/124 5
|
|
// 110, 111 Reserved
|
|
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
|
|
|
uint8_t phy_addr; // PHY address
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 0
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 4
|
|
#endif
|
|
|
|
#if MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
|
#elif !defined(MG_ENABLE_ETH_IRQ)
|
|
#define MG_ENABLE_ETH_IRQ()
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_stm32f_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_stm32f; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
MG_ENABLE_ETH_IRQ(); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: stm32f, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#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 !defined(MG_ENABLE_DRIVER_STM32N)
|
|
#define MG_ENABLE_DRIVER_STM32N 0
|
|
#endif
|
|
#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || MG_ENABLE_DRIVER_STM32N
|
|
|
|
struct mg_tcpip_driver_stm32h_data {
|
|
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
|
|
// HCLK range DIVIDER mdc_cr VALUE
|
|
// -------------------------------------
|
|
// -1 <-- tell driver to guess the value
|
|
// 60-100 MHz HCLK/42 0
|
|
// 100-150 MHz HCLK/62 1
|
|
// 20-35 MHz HCLK/16 2
|
|
// 35-60 MHz HCLK/26 3
|
|
// 150-250 MHz HCLK/102 4 <-- value for max speed HSI
|
|
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI
|
|
// 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
|
|
uint8_t phy_conf; // PHY config
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_CONF
|
|
#define MG_TCPIP_PHY_CONF MG_PHY_CLOCKS_MAC
|
|
#endif
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 0
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 4
|
|
#endif
|
|
|
|
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
|
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
|
#elif !defined(MG_ENABLE_ETH_IRQ)
|
|
#define MG_ENABLE_ETH_IRQ()
|
|
#endif
|
|
|
|
#if MG_ENABLE_IPV6
|
|
#define MG_IPV6_INIT(mif) \
|
|
do { \
|
|
memcpy(mif.ip6ll, (uint8_t[16]) MG_TCPIP_IPV6_LINKLOCAL, 16); \
|
|
memcpy(mif.ip6, (uint8_t[16]) MG_TCPIP_GLOBAL, 16); \
|
|
memcpy(mif.gw6, (uint8_t[16]) MG_TCPIP_GW6, 16); \
|
|
mif.prefix_len = MG_TCPIP_PREFIX_LEN; \
|
|
} while(0)
|
|
#else
|
|
#define MG_IPV6_INIT(mif)
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_stm32h_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
|
driver_data_.phy_conf = MG_TCPIP_PHY_CONF; \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_stm32h; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
MG_IPV6_INIT(mif_); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_ENABLE_ETH_IRQ(); \
|
|
MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
|
|
|
|
struct mg_tcpip_driver_tm4c_data {
|
|
// MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
|
|
// SYSCLK range DIVIDER mdc_cr VALUE
|
|
// -------------------------------------
|
|
// -1 <-- tell driver to guess the value
|
|
// 60-100 MHz SYSCLK/42 0
|
|
// 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed
|
|
// 20-35 MHz SYSCLK/16 2
|
|
// 35-60 MHz SYSCLK/26 3
|
|
// 0x4-0xF Reserved
|
|
int mdc_cr; // Valid values: -1, 0, 1, 2, 3
|
|
};
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#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_TMS570) && MG_ENABLE_DRIVER_TMS570
|
|
struct mg_tcpip_driver_tms570_data {
|
|
int mdc_cr;
|
|
int phy_addr;
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 0
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 1
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_tms570_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_tms570; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: tms570, MAC: %M", mg_print_mac, mif_.mac));\
|
|
} while (0)
|
|
#endif
|
|
|
|
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
|
|
|
|
struct mg_tcpip_driver_xmc_data {
|
|
// 13.2.8.1 Station Management Functions
|
|
// MDC clock divider (). MDC clock is derived from ETH MAC clock
|
|
// It must not exceed 2.5MHz
|
|
// ETH Clock range DIVIDER mdc_cr VALUE
|
|
// --------------------------------------------
|
|
// -1 <-- tell driver to guess the value
|
|
// 60-100 MHz ETH Clock/42 0
|
|
// 100-150 MHz ETH Clock/62 1
|
|
// 20-35 MHz ETH Clock/16 2
|
|
// 35-60 MHz ETH Clock/26 3
|
|
// 150-250 MHz ETH Clock/102 4
|
|
// 250-300 MHz ETH Clock/124 5
|
|
// 110, 111 Reserved
|
|
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
|
uint8_t phy_addr;
|
|
};
|
|
|
|
#ifndef MG_TCPIP_PHY_ADDR
|
|
#define MG_TCPIP_PHY_ADDR 0
|
|
#endif
|
|
|
|
#ifndef MG_DRIVER_MDC_CR
|
|
#define MG_DRIVER_MDC_CR 4
|
|
#endif
|
|
|
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
|
do { \
|
|
static struct mg_tcpip_driver_xmc_data driver_data_; \
|
|
static struct mg_tcpip_if mif_; \
|
|
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
|
|
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
|
|
mif_.ip = MG_TCPIP_IP; \
|
|
mif_.mask = MG_TCPIP_MASK; \
|
|
mif_.gw = MG_TCPIP_GW; \
|
|
mif_.driver = &mg_tcpip_driver_xmc; \
|
|
mif_.driver_data = &driver_data_; \
|
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
|
mg_tcpip_init(mgr, &mif_); \
|
|
MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
#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
|
|
}
|
|
#endif
|
|
#endif // MONGOOSE_H
|