// Copyright (c) 2004-2013 Sergey Lyubka // Copyright (c) 2013-2022 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.9" #ifdef __cplusplus extern "C" { #endif #define MG_ARCH_CUSTOM 0 // User creates its own mongoose_custom.h #define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ... #define MG_ARCH_WIN32 2 // Windows #define MG_ARCH_ESP32 3 // ESP32 #define MG_ARCH_ESP8266 4 // ESP8266 #define MG_ARCH_FREERTOS 5 // FreeRTOS #define MG_ARCH_AZURERTOS 6 // MS Azure RTOS #define MG_ARCH_ZEPHYR 7 // Zephyr RTOS #define MG_ARCH_NEWLIB 8 // Bare metal ARM #define MG_ARCH_RTX 9 // Keil MDK RTX #define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS #define MG_ARCH_RP2040 11 // Raspberry Pi RP2040 #if !defined(MG_ARCH) #if defined(__unix__) || defined(__APPLE__) #define MG_ARCH MG_ARCH_UNIX #elif defined(_WIN32) #define MG_ARCH MG_ARCH_WIN32 #elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR) #define MG_ARCH MG_ARCH_ESP8266 #elif defined(__ZEPHYR__) #define MG_ARCH MG_ARCH_ZEPHYR #elif defined(ESP_PLATFORM) #define MG_ARCH MG_ARCH_ESP32 #elif defined(FREERTOS_IP_H) #define MG_ARCH MG_ARCH_FREERTOS #define MG_ENABLE_FREERTOS_TCP 1 #elif defined(AZURE_RTOS_THREADX) #define MG_ARCH MG_ARCH_AZURERTOS #elif defined(PICO_TARGET_NAME) #define MG_ARCH MG_ARCH_RP2040 #endif #endif // !defined(MG_ARCH) #if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) #include "mongoose_custom.h" // keep this include #endif #if !defined(MG_ARCH) #error "MG_ARCH is not specified and we couldn't guess it. Set -D MG_ARCH=..." #endif // http://esr.ibiblio.org/?p=5095 #define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100) #if MG_ARCH == MG_ARCH_AZURERTOS #include #include #include #include #include #include #include #include #include #include #include #define PATH_MAX FX_MAXIMUM_PATH #define MG_DIRSEP '\\' #define socklen_t int #define closesocket(x) soc_close(x) #undef FOPEN_MAX #endif #if MG_ARCH == MG_ARCH_ESP32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MG_PATH_MAX 128 #endif #if MG_ARCH == MG_ARCH_ESP8266 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MG_PATH_MAX 128 #endif #if MG_ARCH == MG_ARCH_FREERTOS #include // #include // Cannot include errno - might conflict with lwip! #include #include #include #include #include #include // rand(), strtol(), atoi() #include #include #include #include #ifndef MG_IO_SIZE #define MG_IO_SIZE 512 #endif #define calloc(a, b) mg_calloc(a, b) #define free(a) vPortFree(a) #define malloc(a) pvPortMalloc(a) #define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr) // Re-route calloc/free to the FreeRTOS's 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; } #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 // MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_NEWLIB #define _POSIX_TIMERS #include #include #include #include #include #include #include #include #include #include #include #include #define MG_PATH_MAX 100 #define MG_ENABLE_SOCKET 0 #define MG_ENABLE_DIRLIST 0 #endif #if MG_ARCH == MG_ARCH_RP2040 #include #include #include #include #include #include #include #include #include int mkdir(const char *, mode_t); #endif #if MG_ARCH == MG_ARCH_RTX #include #include #include #include #include #include #include #include #include #include #if !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) #define MG_ENABLE_RL 1 #endif #endif #if MG_ARCH == MG_ARCH_TIRTOS #include #include #include #include #include #include #include #include #include #include #include #endif #if MG_ARCH == MG_ARCH_UNIX #define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors #if defined(__APPLE__) #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL #include #elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL #include #else #include #endif #include #include #include #include #include #include #ifndef MG_ENABLE_DIRLIST #define MG_ENABLE_DIRLIST 1 #endif #ifndef MG_PATH_MAX #define MG_PATH_MAX FILENAME_MAX #endif #endif #if MG_ARCH == MG_ARCH_WIN32 #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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; #else #include #include #include #endif #include #include #include // 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 #define MG_INVALID_SOCKET INVALID_SOCKET #define MG_SOCKET_TYPE SOCKET typedef unsigned long nfds_t; #define MG_SOCKET_ERRNO WSAGetLastError() #if defined(_MSC_VER) #pragma comment(lib, "ws2_32.lib") #ifndef alloca #define alloca(a) _alloca(a) #endif #endif #define poll(a, b, c) WSAPoll((a), (b), (c)) #ifndef SO_EXCLUSIVEADDRUSE #define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR)) #endif #define closesocket(x) closesocket(x) typedef int socklen_t; #define MG_DIRSEP '\\' #ifndef MG_PATH_MAX #define MG_PATH_MAX FILENAME_MAX #endif #ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS #endif #ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK #endif #define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX) #define sleep(x) Sleep(x) #define mkdir(a, b) _mkdir(a) #ifndef S_ISDIR #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) #endif #ifndef MG_ENABLE_DIRLIST #define MG_ENABLE_DIRLIST 1 #endif #endif #if MG_ARCH == MG_ARCH_ZEPHYR #include #include #include #include #include #include #include #include #include #include #include #include #include #define MG_PUTCHAR(x) printk("%c", x) #ifndef strdup #define strdup(s) ((char *) mg_strdup(mg_str(s)).ptr) #endif #define strerror(x) zsock_gai_strerror(x) #define FD_CLOEXEC 0 #define F_SETFD 0 #define MG_ENABLE_SSI 0 int rand(void); int sscanf(const char *, const char *, ...); #endif #if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 sockaddr_in freertos_sockaddr #define sockaddr freertos_sockaddr #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__) #include #include #else struct timeval { time_t tv_sec; long tv_usec; }; #endif #include #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 #define MG_ENABLE_CUSTOM_MILLIS 1 #define closesocket(x) closesocket(x) #define mkdir(a, b) (-1) #define EWOULDBLOCK BSD_EWOULDBLOCK #define EAGAIN BSD_EWOULDBLOCK #define EINPROGRESS BSD_EWOULDBLOCK #define EINTR BSD_EWOULDBLOCK #define ECONNRESET BSD_ECONNRESET #define EPIPE BSD_ECONNRESET #define TCP_NODELAY SO_KEEPALIVE #endif #ifndef MG_ENABLE_LOG #define MG_ENABLE_LOG 1 #endif #ifndef MG_ENABLE_MIP #define MG_ENABLE_MIP 0 // Mongoose built-in network stack #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_MIP #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_MBEDTLS #define MG_ENABLE_MBEDTLS 0 #endif #ifndef MG_ENABLE_OPENSSL #define MG_ENABLE_OPENSSL 0 #endif #ifndef MG_ENABLE_CUSTOM_TLS #define MG_ENABLE_CUSTOM_TLS 0 #endif #ifndef MG_ENABLE_SSI #define MG_ENABLE_SSI 0 #endif #ifndef MG_ENABLE_IPV6 #define MG_ENABLE_IPV6 0 #endif #ifndef MG_ENABLE_MD5 #define MG_ENABLE_MD5 1 #endif // Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP) #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_PACKED_FS #define MG_ENABLE_PACKED_FS 0 #endif #ifndef MG_IO_SIZE #define MG_IO_SIZE 2048 // Granularity of the send/recv IO buffer growth #endif #ifndef MG_MAX_RECV_SIZE #define MG_MAX_RECV_SIZE (3 * 1024 * 1024) // Maximum recv IO buffer size #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 3 #endif #ifndef MG_DIRSEP #define MG_DIRSEP '/' #endif #ifndef MG_ENABLE_FILE #if defined(FOPEN_MAX) #define MG_ENABLE_FILE 1 #else #define MG_ENABLE_FILE 0 #endif #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 struct mg_str { const char *ptr; // Pointer to string data size_t len; // String len }; #define MG_NULL_STR \ { NULL, 0 } #define MG_C_STR(a) \ { (a), sizeof(a) - 1 } // Using macro to avoid shadowing C++ struct constructor, see #1298 #define mg_str(s) mg_str_s(s) struct mg_str mg_str(const char *s); struct mg_str mg_str_n(const char *s, size_t n); int mg_lower(const char *s); int mg_ncasecmp(const char *s1, const char *s2, size_t len); int mg_casecmp(const char *s1, const char *s2); int mg_vcmp(const struct mg_str *s1, const char *s2); int mg_vcasecmp(const struct mg_str *str1, const char *str2); int mg_strcmp(const struct mg_str str1, const struct mg_str str2); struct mg_str mg_strstrip(struct mg_str s); struct mg_str mg_strdup(const struct mg_str s); const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle); bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps); bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n); bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v); bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char delim); char *mg_hex(const void *buf, size_t len, char *dst); void mg_unhex(const char *buf, size_t len, unsigned char *to); unsigned long mg_unhexn(const char *s, size_t len); int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip); int64_t mg_to64(struct mg_str str); uint64_t mg_tou64(struct mg_str str); char *mg_remove_double_dots(char *s); typedef void (*mg_pfn_t)(char, void *); // Custom putchar typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer void mg_pfn_iobuf(char ch, void *param); // iobuf printer size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *); size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...); size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap); size_t mg_snprintf(char *, size_t, const char *fmt, ...); char *mg_vmprintf(const char *fmt, va_list *ap); char *mg_mprintf(const char *fmt, ...); enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE }; void mg_log(const char *fmt, ...); bool mg_log_prefix(int ll, const char *file, int line, const char *fname); void mg_log_set(int log_level); void mg_hexdump(const void *buf, size_t len); void mg_log_set_fn(mg_pfn_t fn, void *param); #if MG_ENABLE_LOG #define MG_LOG(level, args) \ do { \ if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \ } while (0) #else #define MG_LOG(level, args) \ do { \ if (0) mg_log args; \ } while (0) #endif #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 { unsigned long id; // Timer ID 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 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); bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now); enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; // Filesystem API functions // st() returns MG_FS_* flags and populates file size and modification time // ls() calls fn() for every directory entry, allowing to list a directory // // NOTE: UNIX-style shorthand names for the API functions are deliberately // chosen to avoid conflicts with some libraries that make macros for e.g. // stat(), write(), read() calls. struct mg_fs { int (*st)(const char *path, size_t *size, time_t *mtime); // stat file void (*ls)(const char *path, void (*fn)(const char *, void *), void *); void *(*op)(const char *path, int flags); // Open file void (*cl)(void *fd); // Close file size_t (*rd)(void *fd, void *buf, size_t len); // Read file size_t (*wr)(void *fd, const void *buf, size_t len); // Write file size_t (*sk)(void *fd, size_t offset); // Set file position bool (*mv)(const char *from, const char *to); // Rename file bool (*rm)(const char *path); // Delete file bool (*mkd)(const char *path); // Create directory }; extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek extern struct mg_fs mg_fs_packed; // Packed FS, see examples/device-dashboard extern struct mg_fs mg_fs_fat; // FAT FS // File descriptor struct mg_fd { void *fd; struct mg_fs *fs; }; struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags); void mg_fs_close(struct mg_fd *fd); char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size); bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t); bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); void mg_random(void *buf, size_t len); char *mg_random_str(char *buf, size_t len); uint16_t mg_ntohs(uint16_t net); uint32_t mg_ntohl(uint32_t net); uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); uint64_t mg_millis(void); #define mg_htons(x) mg_ntohs(x) #define mg_htonl(x) mg_ntohl(x) #define MG_U32(a, b, c, d) \ (((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \ ((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255)) // 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) unsigned short mg_url_port(const char *url); int mg_url_is_ssl(const char *url); struct mg_str mg_url_host(const char *url); struct mg_str mg_url_user(const char *url); struct mg_str mg_url_pass(const char *url); const char *mg_url_uri(const char *url); struct mg_iobuf { unsigned char *buf; // Pointer to stored data size_t size; // Total size available size_t len; // Current number of bytes size_t align; // Alignment during allocation }; int mg_iobuf_init(struct mg_iobuf *, size_t, size_t); int mg_iobuf_resize(struct mg_iobuf *, size_t); void mg_iobuf_free(struct mg_iobuf *); size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t); size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len); int mg_base64_update(unsigned char p, char *to, int len); int mg_base64_final(char *to, int len); int mg_base64_encode(const unsigned char *p, int n, char *to); int mg_base64_decode(const char *src, int n, char *dst); 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 *); struct mg_connection; typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, void *ev_data, void *fn_data); void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_error(struct mg_connection *c, const char *fmt, ...); enum { MG_EV_ERROR, // Error char *error_message MG_EV_OPEN, // Connection created NULL MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis MG_EV_RESOLVE, // Host name is resolved NULL MG_EV_CONNECT, // Connection established NULL MG_EV_ACCEPT, // Connection accepted NULL MG_EV_TLS_HS, // TLS handshake succeeded NULL MG_EV_READ, // Data received from socket long *bytes_read MG_EV_WRITE, // Data written to socket long *bytes_written MG_EV_CLOSE, // Connection closed NULL MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message * MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis MG_EV_USER // Starting ID for user events }; struct mg_dns { const char *url; // DNS server URL struct mg_connection *c; // DNS server connection }; struct mg_addr { uint16_t port; // TCP or UDP port in network byte order uint32_t ip; // IP address in network byte order uint8_t ip6[16]; // IPv6 address bool is_ip6; // True when address is IPv6 address }; struct mg_mgr { struct mg_connection *conns; // List of active connections struct mg_dns dns4; // DNS for IPv4 struct mg_dns dns6; // DNS for IPv6 int dnstimeout; // DNS resolve timeout in milliseconds bool use_dns6; // Use DNS6 server by default, see #1532 unsigned long nextid; // Next connection ID unsigned long timerid; // Next timer ID void *userdata; // Arbitrary user data pointer uint16_t mqtt_id; // MQTT IDs for pub/sub void *active_dns_requests; // DNS requests in progress struct mg_timer *timers; // Active timers int epoll_fd; // Used when MG_EPOLL_ENABLE=1 void *priv; // Used by the MIP stack size_t extraconnsize; // Used by the MIP stack #if MG_ENABLE_FREERTOS_TCP SocketSet_t ss; // NOTE(lsm): referenced from socket struct #endif }; struct mg_connection { struct mg_connection *next; // Linkage in struct mg_mgr :: connections struct mg_mgr *mgr; // Our container struct mg_addr loc; // Local address struct mg_addr rem; // Remote address void *fd; // Connected socket, or LWIP data unsigned long id; // Auto-incrementing unique connection ID struct mg_iobuf recv; // Incoming data struct mg_iobuf send; // Outgoing data mg_event_handler_t fn; // User-specified event handler function void *fn_data; // User-specified function parameter mg_event_handler_t pfn; // Protocol-specific handler function void *pfn_data; // Protocol-specific function parameter char data[MG_DATA_SIZE]; // Arbitrary connection data void *tls; // TLS specific data unsigned is_listening : 1; // Listening connection unsigned is_client : 1; // Outbound (client) connection unsigned is_accepted : 1; // Accepted (server) connection unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress unsigned is_connecting : 1; // Non-blocking connect is in progress unsigned is_tls : 1; // TLS-enabled connection unsigned is_tls_hs : 1; // TLS handshake is in progress unsigned is_udp : 1; // UDP connection unsigned is_websocket : 1; // WebSocket connection unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator unsigned is_hexdumping : 1; // Hexdump in/out traffic unsigned is_draining : 1; // Send remaining data, then close and free unsigned is_closing : 1; // Close and free the connection immediately unsigned is_full : 1; // Stop reads, until cleared unsigned is_resp : 1; // Response is still being generated unsigned is_readable : 1; // Connection is ready to read unsigned is_writable : 1; // Connection is ready to write }; void mg_mgr_poll(struct mg_mgr *, int ms); void mg_mgr_init(struct mg_mgr *); void mg_mgr_free(struct mg_mgr *); struct mg_connection *mg_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, mg_event_handler_t fn, void *fn_data); void mg_connect_resolved(struct mg_connection *); bool mg_send(struct mg_connection *, const void *, size_t); size_t mg_printf(struct mg_connection *, const char *fmt, ...); size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap); bool mg_aton(struct mg_str str, struct mg_addr *addr); int mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *, bool udp); // These functions are used to integrate with custom network stacks struct mg_connection *mg_alloc_conn(struct mg_mgr *); void mg_close_conn(struct mg_connection *c); bool mg_open_listener(struct mg_connection *c, const char *url); struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, unsigned flags, void (*fn)(void *), void *arg); // Low-level IO primives used by TLS layer enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 }; long mg_io_send(struct mg_connection *c, const void *buf, size_t len); long mg_io_recv(struct mg_connection *c, void *buf, size_t len); struct mg_http_header { struct mg_str name; // Header name struct mg_str value; // Header value }; struct mg_http_message { struct mg_str method, uri, query, proto; // Request/response line struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers struct mg_str body; // Body struct mg_str head; // Request + headers struct mg_str chunk; // Chunk for chunked encoding, or partial body struct mg_str message; // Request + headers + body }; // Parameter for mg_http_serve_dir() struct mg_http_serve_opts { const char *root_dir; // Web root directory, must be non-NULL const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml const char *extra_headers; // Extra HTTP headers to add in responses const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,.. const char *page404; // Path to the 404 page, or NULL by default struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX }; // Parameter for mg_http_next_multipart struct mg_http_part { struct mg_str name; // Form field name struct mg_str filename; // Filename for file uploads struct mg_str body; // Part contents }; int mg_http_parse(const char *s, size_t len, struct mg_http_message *); int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm); struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data); void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, const struct mg_http_serve_opts *); void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm, const char *path, const struct mg_http_serve_opts *); void mg_http_reply(struct mg_connection *, int status_code, const char *headers, const char *body_fmt, ...); struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name); struct mg_str mg_http_var(struct mg_str buf, struct mg_str name); int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t); int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); bool mg_http_match_uri(const struct mg_http_message *, const char *glob); long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, struct mg_fs *fs, const char *path, size_t max_size); void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); int mg_http_status(const struct mg_http_message *hm); void mg_hello(const char *url); void mg_http_serve_ssi(struct mg_connection *c, const char *root, const char *fullpath); struct mg_tls_opts { const char *ca; // CA certificate file. For both listeners and clients const char *crl; // Certificate Revocation List. For clients const char *cert; // Certificate const char *certkey; // Certificate key const char *ciphers; // Cipher list struct mg_str srvname; // If not empty, enables server name verification struct mg_fs *fs; // FS API for reading certificate files }; void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *); void mg_tls_free(struct mg_connection *); long mg_tls_send(struct mg_connection *, const void *buf, size_t len); long mg_tls_recv(struct mg_connection *, void *buf, size_t len); size_t mg_tls_pending(struct mg_connection *); void mg_tls_handshake(struct mg_connection *); #if MG_ENABLE_MBEDTLS #include #include #include struct mg_tls { char *cafile; // CA certificate path mbedtls_x509_crt ca; // Parsed CA certificate mbedtls_x509_crt cert; // Parsed certificate mbedtls_ssl_context ssl; // SSL/TLS context mbedtls_ssl_config conf; // SSL-TLS config mbedtls_pk_context pk; // Private key context }; #endif #if MG_ENABLE_OPENSSL #include #include struct mg_tls { SSL_CTX *ctx; SSL *ssl; }; #endif #define WEBSOCKET_OP_CONTINUE 0 #define WEBSOCKET_OP_TEXT 1 #define WEBSOCKET_OP_BINARY 2 #define WEBSOCKET_OP_CLOSE 8 #define WEBSOCKET_OP_PING 9 #define WEBSOCKET_OP_PONG 10 struct mg_ws_message { struct mg_str data; // Websocket message data uint8_t flags; // Websocket message flags }; struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data, const char *fmt, ...); void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *, const char *fmt, ...); size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op); size_t mg_ws_wrap(struct mg_connection *, size_t len, int op); 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 *); struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data); void mg_sntp_request(struct mg_connection *c); int64_t mg_sntp_parse(const unsigned char *buf, size_t len); #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 enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; struct mg_mqtt_opts { struct mg_str user; // Username, can be empty struct mg_str pass; // Password, can be empty struct mg_str client_id; // Client ID struct mg_str will_topic; // Will topic struct mg_str will_message; // Will message uint8_t will_qos; // Will message quality of service uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4. uint16_t keepalive; // Keep-alive timer in seconds bool will_retain; // Retain last will bool clean; // Use clean session, 0 or 1 }; struct mg_mqtt_message { struct mg_str topic; // Parsed topic struct mg_str data; // Parsed message struct mg_str dgram; // Whole MQTT datagram, including headers uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH uint8_t cmd; // MQTT command, one of MQTT_CMD_* uint8_t qos; // Quality of service uint8_t ack; // Connack return code. 0 - success }; struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, const struct mg_mqtt_opts *opts, mg_event_handler_t fn, void *fn_data); struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data); void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic, struct mg_str data, int qos, bool retain); void mg_mqtt_sub(struct mg_connection *, struct mg_str topic, int qos); int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, uint32_t len); void mg_mqtt_ping(struct mg_connection *); void mg_mqtt_pong(struct mg_connection *); void mg_mqtt_disconnect(struct mg_connection *); // 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 }; struct mg_dns_header { uint16_t txnid; // Transaction ID uint16_t flags; uint16_t num_questions; uint16_t num_answers; uint16_t num_authority_prs; uint16_t num_other_prs; }; // DNS resource record struct mg_dns_rr { uint16_t nlen; // Name or pointer length uint16_t atype; // Address type uint16_t aclass; // Address class uint16_t alen; // Address length }; void mg_resolve(struct mg_connection *, const char *url); void mg_resolve_cancel(struct mg_connection *); bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, bool is_question, struct mg_dns_rr *); #ifndef MG_JSON_MAX_DEPTH #define MG_JSON_MAX_DEPTH 30 #endif // Error return values - negative. Successful returns are >= 0 enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; int mg_json_get(struct mg_str json, const char *path, int *toklen); bool mg_json_get_num(struct mg_str json, const char *path, double *v); bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); long mg_json_get_long(struct mg_str json, const char *path, long dflt); char *mg_json_get_str(struct mg_str json, const char *path); char *mg_json_get_hex(struct mg_str json, const char *path, int *len); char *mg_json_get_b64(struct mg_str json, const char *path, int *len); // JSON-RPC request descriptor struct mg_rpc_req { struct mg_rpc **head; // RPC handlers list head struct mg_rpc *rpc; // RPC handler being called mg_pfn_t pfn; // Response printing function void *pfn_data; // Response printing function data void *req_data; // Arbitrary request data struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]} }; // JSON-RPC method handler struct mg_rpc { struct mg_rpc *next; // Next in list struct mg_str method; // Method pattern void (*fn)(struct mg_rpc_req *); // Handler function void *fn_data; // Handler function argument }; void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern, void (*handler)(struct mg_rpc_req *), void *handler_data); void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *)); void mg_rpc_process(struct mg_rpc_req *); // Helper functions to print result or error frame 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); void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...); void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *); void mg_rpc_list(struct mg_rpc_req *r); struct mip_if; // MIP network interface struct mip_driver { bool (*init)(struct mip_if *); // Initialise driver size_t (*tx)(const void *, size_t, struct mip_if *); // Transmit frame size_t (*rx)(void *buf, size_t len, struct mip_if *); // Receive frame (poll) bool (*up)(struct mip_if *); // Up/down status }; // Receive queue - single producer, single consumer queue. Interrupt-based // drivers copy received frames to the queue in interrupt context. mip_poll() // function runs in event loop context, reads from the queue struct queue { uint8_t *buf; size_t len; volatile size_t tail, head; }; #define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 #define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size // Network interface struct mip_if { uint8_t mac[6]; // MAC address. Must be set to a valid MAC uint32_t ip, mask, gw; // IP address, mask, default gateway struct mg_str rx; // Output (TX) buffer struct mg_str tx; // Input (RX) buffer bool enable_dhcp_client; // Enable DCHP client bool enable_dhcp_server; // Enable DCHP server struct mip_driver *driver; // Low level driver void *driver_data; // Driver-specific data struct mg_mgr *mgr; // Mongoose event manager struct queue queue; // Set queue.len for interrupt based drivers // Internal state, user can use it but should not change it uint64_t now; // Current time uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state uint64_t lease_expire; // Lease expiration time uint8_t arp_cache[MIP_ARP_CS]; // Each entry is 12 bytes uint16_t eport; // Next ephemeral port uint16_t dropped; // Number of dropped frames uint8_t state; // Current state #define MIP_STATE_DOWN 0 // Interface is down #define MIP_STATE_UP 1 // Interface is up #define MIP_STATE_READY 2 // Interface is up and has IP }; void mip_init(struct mg_mgr *, struct mip_if *); void mip_free(struct mip_if *); void mip_qwrite(void *buf, size_t len, struct mip_if *ifp); size_t mip_qread(void *buf, struct mip_if *ifp); // conveniency rx function for IRQ-driven drivers size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp); extern struct mip_driver mip_driver_stm32; extern struct mip_driver mip_driver_w5500; extern struct mip_driver mip_driver_tm4c; // Drivers that require SPI, can use this SPI abstraction struct mip_spi { void *spi; // Opaque SPI bus descriptor void (*begin)(void *); // SPI begin: slave select low void (*end)(void *); // SPI end: slave select high uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply }; #ifdef MIP_QPROFILE enum { QP_IRQTRIGGERED = 0, // payload is number of interrupts so far QP_FRAMEPUSHED, // available space in the frame queue QP_FRAMEPOPPED, // available space in the frame queue QP_FRAMEDONE, // available space in the frame queue QP_FRAMEDROPPED, // number of dropped frames QP_QUEUEOVF // profiling queue is full, payload is number of frame drops }; void qp_mark(unsigned int type, int len); void qp_log(void); // timestamp, type, payload void qp_init(void); #else #define qp_mark(a, b) #endif struct mip_driver_stm32_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 }; struct mip_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 }; #ifdef __cplusplus } #endif #endif // MONGOOSE_H