Repository: celix Updated Branches: refs/heads/develop 2dc3840dd -> 3de9cafdb
http://git-wip-us.apache.org/repos/asf/celix/blob/3de9cafd/remote_services/utils/private/src/civetweb.c ---------------------------------------------------------------------- diff --git a/remote_services/utils/private/src/civetweb.c b/remote_services/utils/private/src/civetweb.c index 5c1b5ae..0069307 100644 --- a/remote_services/utils/private/src/civetweb.c +++ b/remote_services/utils/private/src/civetweb.c @@ -24,41 +24,77 @@ #if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ #endif +#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */ +#define _WIN32_WINNT 0x0501 +#endif #else +#if defined(__GNUC__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE /* for setgroups() */ +#endif #ifdef __linux__ -#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ +#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ #endif #ifndef _LARGEFILE_SOURCE -#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ +#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ #endif #ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ +#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ #endif #ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */ +#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */ #endif #ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ +#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ +#endif +#ifdef __sun +#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ +#define __inline inline /* not recognized on older compiler versions */ #endif #endif -#if defined (_MSC_VER) +#if defined(_MSC_VER) /* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ -#pragma warning (disable : 4306 ) +#pragma warning(disable : 4306) /* conditional expression is constant: introduced by FD_SET(..) */ -#pragma warning (disable : 4127) +#pragma warning(disable : 4127) /* non-constant aggregate initializer: issued due to missing C99 support */ -#pragma warning (disable : 4204) +#pragma warning(disable : 4204) +/* padding added after data member */ +#pragma warning(disable : 4820) +/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ +#pragma warning(disable : 4668) +/* no function prototype given: converting '()' to '(void)' */ +#pragma warning(disable : 4255) +/* function has been selected for automatic inline expansion */ +#pragma warning(disable : 4711) #endif -/* Disable WIN32_LEAN_AND_MEAN. - This makes windows.h always include winsock2.h */ -#if defined(WIN32_LEAN_AND_MEAN) -#undef WIN32_LEAN_AND_MEAN +/* This code uses static_assert to check some conditions. + * Unfortunately some compilers still do not support it, so we have a + * replacement function here. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +#define mg_static_assert static_assert +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +#define mg_static_assert static_assert +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define mg_static_assert _Static_assert +#else +char static_assert_replacement[1]; +#define mg_static_assert(cond, txt) \ + extern char static_assert_replacement[(cond) ? 1 : -1] #endif -#if defined USE_IPV6 && defined(_WIN32) -#include <ws2tcpip.h> +mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8, + "int data type size check"); +mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8, + "pointer data type size check"); +mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); +/* mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t data + * type size check"); */ + +/* DTL -- including winsock2.h works better if lean and mean */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #endif #if defined(__SYMBIAN32__) @@ -68,7 +104,7 @@ #endif /* __SYMBIAN32__ */ #ifndef IGNORE_UNUSED_RESULT -#define IGNORE_UNUSED_RESULT(a) (void)((a) && 1) +#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1)) #endif #ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ @@ -79,6 +115,61 @@ #include <fcntl.h> #endif /* !_WIN32_WCE */ +#ifdef __MACH__ + +#define CLOCK_MONOTONIC (1) +#define CLOCK_REALTIME (2) + +#include <sys/time.h> +#include <mach/clock.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#include <assert.h> + +/* clock_gettime is not implemented on OSX */ +int clock_gettime(int clk_id, struct timespec *t); + +int clock_gettime(int clk_id, struct timespec *t) +{ + if (clk_id == CLOCK_REALTIME) { + struct timeval now; + int rv = gettimeofday(&now, NULL); + if (rv) { + return rv; + } + t->tv_sec = now.tv_sec; + t->tv_nsec = now.tv_usec * 1000; + return 0; + + } else if (clk_id == CLOCK_MONOTONIC) { + static uint64_t start_time = 0; + static mach_timebase_info_data_t timebase_ifo = {0, 0}; + + uint64_t now = mach_absolute_time(); + + if (start_time == 0) { + kern_return_t mach_status = mach_timebase_info(&timebase_ifo); +#if defined(DEBUG) + assert(mach_status == KERN_SUCCESS); +#else + /* appease "unused variable" warning for release builds */ + (void)mach_status; +#endif + start_time = now; + } + + now = + (uint64_t)((double)(now - start_time) * (double)timebase_ifo.numer / + (double)timebase_ifo.denom); + + t->tv_sec = now / 1000000000; + t->tv_nsec = now % 1000000000; + return 0; + } + return -1; /* EINVAL - Clock ID is unknown */ +} +#endif + #include <time.h> #include <stdlib.h> #include <stdarg.h> @@ -90,21 +181,32 @@ #include <stdio.h> #ifndef MAX_WORKER_THREADS -#define MAX_WORKER_THREADS (1024*64) +#define MAX_WORKER_THREADS (1024 * 64) +#endif +#ifndef SOCKET_TIMEOUT_QUANTUM +#define SOCKET_TIMEOUT_QUANTUM (10000) #endif +mg_static_assert(MAX_WORKER_THREADS >= 1, + "worker threads must be a positive number"); + #if defined(_WIN32) && !defined(__SYMBIAN32__) /* Windows specific */ -#if defined(_MSC_VER) && _MSC_VER <= 1400 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 /* To make it link in VS2005 */ -#endif #include <windows.h> -typedef const char * SOCK_OPT_TYPE; +#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */ +#include <ws2tcpip.h> + +typedef const char *SOCK_OPT_TYPE; -#ifndef PATH_MAX -#define PATH_MAX MAX_PATH +#if !defined(PATH_MAX) +#define PATH_MAX (MAX_PATH) +#endif + +#if !defined(PATH_MAX) +#define PATH_MAX (4096) #endif +mg_static_assert(PATH_MAX >= 1, "path length must be a positive number"); + #ifndef _IN_PORT_T #ifndef in_port_t #define in_port_t u_short @@ -115,94 +217,96 @@ typedef const char * SOCK_OPT_TYPE; #include <process.h> #include <direct.h> #include <io.h> -#else /* _WIN32_WCE */ +#else /* _WIN32_WCE */ #define NO_CGI /* WinCE has no pipes */ typedef long off_t; -#define errno GetLastError() -#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10) +#define errno ((int)(GetLastError())) +#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10)) #endif /* _WIN32_WCE */ -#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \ - ((uint64_t)((uint32_t)(hi))) << 32)) -#define RATE_DIFF 10000000 /* 100 nsecs */ -#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de) -#define SYS2UNIX_TIME(lo, hi) \ - (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF) +#define MAKEUQUAD(lo, hi) \ + ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) +#define RATE_DIFF (10000000) /* 100 nsecs */ +#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) +#define SYS2UNIX_TIME(lo, hi) \ + ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) /* Visual Studio 6 does not know __func__ or __FUNCTION__ - The rest of MS compilers use __FUNCTION__, not C99 __func__ - Also use _strtoui64 on modern M$ compilers */ -#if defined(_MSC_VER) && _MSC_VER < 1300 + * The rest of MS compilers use __FUNCTION__, not C99 __func__ + * Also use _strtoui64 on modern M$ compilers */ +#if defined(_MSC_VER) +#if (_MSC_VER < 1300) #define STRX(x) #x #define STR(x) STRX(x) #define __func__ __FILE__ ":" STR(__LINE__) -#define strtoull(x, y, z) (unsigned __int64) _atoi64(x) -#define strtoll(x, y, z) _atoi64(x) +#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) +#define strtoll(x, y, z) (_atoi64(x)) #else -#define __func__ __FUNCTION__ -#define strtoull(x, y, z) _strtoui64(x, y, z) -#define strtoll(x, y, z) _strtoi64(x, y, z) +#define __func__ __FUNCTION__ +#define strtoull(x, y, z) (_strtoui64(x, y, z)) +#define strtoll(x, y, z) (_strtoi64(x, y, z)) +#endif #endif /* _MSC_VER */ -#define ERRNO GetLastError() +#define ERRNO ((int)(GetLastError())) #define NO_SOCKLEN_T -#define SSL_LIB "ssleay32.dll" -#define CRYPTO_LIB "libeay32.dll" -#define O_NONBLOCK 0 +#define SSL_LIB "ssleay32.dll" +#define CRYPTO_LIB "libeay32.dll" +#define O_NONBLOCK (0) +#ifndef W_OK #define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */ +#endif #if !defined(EWOULDBLOCK) -#define EWOULDBLOCK WSAEWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK #endif /* !EWOULDBLOCK */ #define _POSIX_ -#define INT64_FMT "I64d" +#define INT64_FMT "I64d" #define WINCDECL __cdecl -#define SHUT_WR 1 +#define SHUT_RD (0) +#define SHUT_WR (1) +#define SHUT_BOTH (2) #define snprintf _snprintf #define vsnprintf _vsnprintf #define access _access -#define mg_sleep(x) Sleep(x) +#define mg_sleep(x) (Sleep(x)) #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) #ifndef popen -#define popen(x, y) _popen(x, y) +#define popen(x, y) (_popen(x, y)) #endif #ifndef pclose -#define pclose(x) _pclose(x) -#endif -#define close(x) _close(x) -#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y)) -#define RTLD_LAZY 0 -#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z)) -#define fdopen(x, y) _fdopen((x), (y)) -#define write(x, y, z) _write((x), (y), (unsigned) z) -#define read(x, y, z) _read((x), (y), (unsigned) z) -#define flockfile(x) EnterCriticalSection(&global_log_file_lock) -#define funlockfile(x) LeaveCriticalSection(&global_log_file_lock) -#define sleep(x) Sleep((x) * 1000) -#define rmdir(x) _rmdir(x) +#define pclose(x) (_pclose(x)) +#endif +#define close(x) (_close(x)) +#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y))) +#define RTLD_LAZY (0) +#define fseeko(x, y, z) (_lseeki64(_fileno(x), (y), (z))) +#define fdopen(x, y) (_fdopen((x), (y))) +#define write(x, y, z) (_write((x), (y), (unsigned)z)) +#define read(x, y, z) (_read((x), (y), (unsigned)z)) +#define flockfile(x) (EnterCriticalSection(&global_log_file_lock)) +#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock)) +#define sleep(x) (Sleep((x)*1000)) +#define rmdir(x) (_rmdir(x)) #if defined(USE_LUA) && defined(USE_WEBSOCKET) #define USE_TIMERS #endif -#if !defined(va_copy) -#define va_copy(x, y) x = y -#endif /* !va_copy MINGW #defines va_copy */ - #if !defined(fileno) -#define fileno(x) _fileno(x) +#define fileno(x) (_fileno(x)) #endif /* !fileno MINGW #defines fileno */ typedef HANDLE pthread_mutex_t; typedef DWORD pthread_key_t; typedef HANDLE pthread_t; typedef struct { - CRITICAL_SECTION threadIdSec; - int waitingthreadcount; /* The number of threads queued. */ - pthread_t *waitingthreadhdls; /* The thread handles. */ + CRITICAL_SECTION threadIdSec; + int waitingthreadcount; /* The number of threads queued. */ + pthread_t *waitingthreadhdls; /* The thread handles. */ } pthread_cond_t; #ifndef __clockid_t_defined @@ -212,13 +316,13 @@ typedef DWORD clockid_t; #define CLOCK_MONOTONIC (1) #endif #ifndef CLOCK_REALTIME -#define CLOCK_REALTIME (2) +#define CLOCK_REALTIME (2) #endif #ifndef _TIMESPEC_DEFINED struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ }; #endif @@ -233,41 +337,41 @@ static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p); #if defined(HAVE_STDINT) #include <stdint.h> #else -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; typedef unsigned __int64 uint64_t; -typedef __int64 int64_t; -#define INT64_MAX 9223372036854775807 +typedef __int64 int64_t; +#define INT64_MAX (9223372036854775807) #endif /* HAVE_STDINT */ /* POSIX dirent interface */ struct dirent { - char d_name[PATH_MAX]; + char d_name[PATH_MAX]; }; typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; + HANDLE handle; + WIN32_FIND_DATAW info; + struct dirent result; } DIR; -#if !defined(USE_IPV6) && defined(_WIN32) +#if defined(_WIN32) && !defined(POLLIN) #ifndef HAVE_POLL struct pollfd { - SOCKET fd; - short events; - short revents; + SOCKET fd; + short events; + short revents; }; -#define POLLIN 1 +#define POLLIN (0x0300) #endif #endif /* Mark required libraries */ -#ifdef _MSC_VER +#if defined(_MSC_VER) #pragma comment(lib, "Ws2_32.lib") #endif -#else /* UNIX specific */ +#else /* UNIX specific */ #include <sys/wait.h> #include <sys/socket.h> #include <sys/poll.h> @@ -278,7 +382,7 @@ struct pollfd { #include <stdint.h> #include <inttypes.h> #include <netdb.h> -typedef const void * SOCK_OPT_TYPE; +typedef const void *SOCK_OPT_TYPE; #if defined(ANDROID) typedef unsigned short int in_port_t; @@ -286,244 +390,312 @@ typedef unsigned short int in_port_t; #include <pwd.h> #include <unistd.h> +#include <grp.h> #include <dirent.h> #if !defined(NO_SSL_DL) && !defined(NO_SSL) #include <dlfcn.h> #endif #include <pthread.h> #if defined(__MACH__) -#define SSL_LIB "libssl.dylib" -#define CRYPTO_LIB "libcrypto.dylib" +#define SSL_LIB "libssl.dylib" +#define CRYPTO_LIB "libcrypto.dylib" #else #if !defined(SSL_LIB) -#define SSL_LIB "libssl.so" +#define SSL_LIB "libssl.so" #endif #if !defined(CRYPTO_LIB) -#define CRYPTO_LIB "libcrypto.so" +#define CRYPTO_LIB "libcrypto.so" #endif #endif #ifndef O_BINARY -#define O_BINARY 0 +#define O_BINARY (0) #endif /* O_BINARY */ -#define closesocket(a) close(a) -#define mg_mkdir(x, y) mkdir(x, y) -#define mg_remove(x) remove(x) -#define mg_sleep(x) usleep((x) * 1000) -#define ERRNO errno +#define closesocket(a) (close(a)) +#define mg_mkdir(x, y) (mkdir(x, y)) +#define mg_remove(x) (remove(x)) +#define mg_sleep(x) (usleep((x)*1000)) +#define ERRNO (errno) #define INVALID_SOCKET (-1) #define INT64_FMT PRId64 typedef int SOCKET; #define WINCDECL +#if defined(__hpux) +/* HPUX 11 does not have monotonic, fall back to realtime */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +/* HPUX defines socklen_t incorrectly as size_t which is 64bit on + * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED + * the prototypes use int* rather than socklen_t* which matches the + * actual library expectation. When called with the wrong size arg + * accept() returns a zero client inet addr and check_acl() always + * fails. Since socklen_t is widely used below, just force replace + * their typedef with int. - DTL + */ +#define socklen_t int +#endif /* hpux */ + #endif /* End of Windows and UNIX specific includes */ +/* va_copy should always be a macro, C99 and C++11 - DTL */ +#ifndef va_copy +#define va_copy(x, y) ((x) = (y)) +#endif + #ifdef _WIN32 static CRITICAL_SECTION global_log_file_lock; -static DWORD pthread_self(void) -{ - return GetCurrentThreadId(); -} +static DWORD pthread_self(void) { return GetCurrentThreadId(); } -int pthread_key_create(pthread_key_t *key, void (*_must_be_zero)(void*) /* destructor function not supported for windows */) +static int pthread_key_create( + pthread_key_t *key, + void (*_must_be_zero)( + void *) /* destructor function not supported for windows */) { - assert(_must_be_zero == NULL); - if ((key!=0) && (_must_be_zero == NULL)) { - *key = TlsAlloc(); - return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; - } - return -2; + assert(_must_be_zero == NULL); + if ((key != 0) && (_must_be_zero == NULL)) { + *key = TlsAlloc(); + return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; + } + return -2; } -int pthread_key_delete(pthread_key_t key) +static int pthread_key_delete(pthread_key_t key) { - return TlsFree(key) ? 0 : 1; + return TlsFree(key) ? 0 : 1; } -int pthread_setspecific(pthread_key_t key, void * value) +static int pthread_setspecific(pthread_key_t key, void *value) { - return TlsSetValue(key, value) ? 0 : 1; + return TlsSetValue(key, value) ? 0 : 1; } -void *pthread_getspecific(pthread_key_t key) -{ - return TlsGetValue(key); -} +#ifdef ENABLE_UNUSED_PTHREAD_FUNCTIONS +static void *pthread_getspecific(pthread_key_t key) { return TlsGetValue(key); } +#endif #endif /* _WIN32 */ - #include "civetweb.h" #define PASSWORDS_FILE_NAME ".htpasswd" -#define CGI_ENVIRONMENT_SIZE 4096 -#define MAX_CGI_ENVIR_VARS 64 -#define MG_BUF_LEN 8192 +#define CGI_ENVIRONMENT_SIZE (4096) +#define MAX_CGI_ENVIR_VARS (64) +#define MG_BUF_LEN (8192) + #ifndef MAX_REQUEST_SIZE -#define MAX_REQUEST_SIZE 16384 +#define MAX_REQUEST_SIZE (16384) #endif + +mg_static_assert(MAX_REQUEST_SIZE >= 256, + "request size length must be a positive number"); + #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #if !defined(DEBUG_TRACE) #if defined(DEBUG) -static void DEBUG_TRACE_FUNC(const char *func, unsigned line, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(3, 4); - -static void DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) { +static void DEBUG_TRACE_FUNC(const char *func, + unsigned line, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(3, 4); - va_list args; - flockfile(stdout); - printf("*** %lu.%p.%s.%u: ", - (unsigned long) time(NULL), (void *) pthread_self(), - func, line); - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - putchar('\n'); - fflush(stdout); - funlockfile(stdout); +static void +DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) +{ + va_list args; + flockfile(stdout); + printf("*** %lu.%p.%s.%u: ", + (unsigned long)time(NULL), + (void *)pthread_self(), + func, + line); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + fflush(stdout); + funlockfile(stdout); } -#define DEBUG_TRACE(fmt, ...) DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) +#define DEBUG_TRACE(fmt, ...) \ + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) #else -#define DEBUG_TRACE(fmt, ...) +#define DEBUG_TRACE(fmt, ...) \ + do { \ + } while (0) #endif /* DEBUG */ #endif /* DEBUG_TRACE */ #if defined(MEMORY_DEBUGGING) -static unsigned long blockCount = 0; -static unsigned long totalMemUsed = 0; - -static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { - - void * data = malloc(size + sizeof(size_t)); - void * memory = 0; - char mallocStr[256]; - - if (data) { - *(size_t*)data = size; - totalMemUsed += size; - blockCount++; - memory = (void *)(((char*)data)+sizeof(size_t)); - } - - sprintf(mallocStr, "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", memory, (unsigned long)size, totalMemUsed, blockCount, file, line); +unsigned long mg_memory_debug_blockCount = 0; +unsigned long mg_memory_debug_totalMemUsed = 0; + +static void *mg_malloc_ex(size_t size, const char *file, unsigned line) +{ + void *data = malloc(size + sizeof(size_t)); + void *memory = 0; + char mallocStr[256]; + + if (data) { + *(size_t *)data = size; + mg_memory_debug_totalMemUsed += size; + mg_memory_debug_blockCount++; + memory = (void *)(((char *)data) + sizeof(size_t)); + } + + sprintf(mallocStr, + "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)size, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - return memory; + return memory; } -static void * mg_calloc_ex(size_t count, size_t size, const char * file, unsigned line) { - - void * data = mg_malloc_ex(size*count, file, line); - if (data) memset(data, 0, size); - - return data; +static void * +mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line) +{ + void *data = mg_malloc_ex(size * count, file, line); + if (data) { + memset(data, 0, size); + } + return data; } -static void mg_free_ex(void * memory, const char * file, unsigned line) { - - char mallocStr[256]; - void * data = (void *)(((char*)memory)-sizeof(size_t)); - size_t size; +static void mg_free_ex(void *memory, const char *file, unsigned line) +{ + char mallocStr[256]; + void *data = (void *)(((char *)memory) - sizeof(size_t)); + size_t size; - if (memory) { - size = *(size_t*)data; - totalMemUsed -= size; - blockCount--; - sprintf(mallocStr, "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", memory, (unsigned long)size, totalMemUsed, blockCount, file, line); + if (memory) { + size = *(size_t *)data; + mg_memory_debug_totalMemUsed -= size; + mg_memory_debug_blockCount--; + sprintf(mallocStr, + "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)size, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - free(data); - } -} - -static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, unsigned line) { - - char mallocStr[256]; - void * data; - void * _realloc; - size_t oldsize; - - if (newsize) { - if (memory) { - data = (void *)(((char*)memory)-sizeof(size_t)); - oldsize = *(size_t*)data; - _realloc = realloc(data, newsize+sizeof(size_t)); - if (_realloc) { - data = _realloc; - totalMemUsed -= oldsize; - sprintf(mallocStr, "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", memory, (unsigned long)oldsize, totalMemUsed, blockCount, file, line); + free(data); + } +} + +static void * +mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line) +{ + char mallocStr[256]; + void *data; + void *_realloc; + size_t oldsize; + + if (newsize) { + if (memory) { + data = (void *)(((char *)memory) - sizeof(size_t)); + oldsize = *(size_t *)data; + _realloc = realloc(data, newsize + sizeof(size_t)); + if (_realloc) { + data = _realloc; + mg_memory_debug_totalMemUsed -= oldsize; + sprintf(mallocStr, + "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)oldsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - totalMemUsed += newsize; - sprintf(mallocStr, "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", memory, (unsigned long)newsize, totalMemUsed, blockCount, file, line); + mg_memory_debug_totalMemUsed += newsize; + sprintf(mallocStr, + "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", + memory, + (unsigned long)newsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, + file, + line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - *(size_t*)data = newsize; - data = (void *)(((char*)data)+sizeof(size_t)); - } else { + *(size_t *)data = newsize; + data = (void *)(((char *)data) + sizeof(size_t)); + } else { #if defined(_WIN32) - OutputDebugStringA("MEM: realloc failed\n"); + OutputDebugStringA("MEM: realloc failed\n"); #else - DEBUG_TRACE("%s", "MEM: realloc failed\n"); + DEBUG_TRACE("%s", "MEM: realloc failed\n"); #endif - return _realloc; - } - } else { - data = mg_malloc_ex(newsize, file, line); - } - } else { - data = 0; - mg_free_ex(memory, file, line); - } + return _realloc; + } + } else { + data = mg_malloc_ex(newsize, file, line); + } + } else { + data = 0; + mg_free_ex(memory, file, line); + } - return data; + return data; } -#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) -#define mg_calloc(a,b) mg_calloc_ex(a, b, __FILE__, __LINE__) -#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) -#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) +#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) +#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__) +#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) +#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) #else -static __inline void * mg_malloc(size_t a) {return malloc(a);} -static __inline void * mg_calloc(size_t a, size_t b) {return calloc(a, b);} -static __inline void * mg_realloc(void * a, size_t b) {return realloc(a, b);} -static __inline void mg_free(void * a) {free(a);} + +static __inline void *mg_malloc(size_t a) { return malloc(a); } +static __inline void *mg_calloc(size_t a, size_t b) { return calloc(a, b); } +static __inline void *mg_realloc(void *a, size_t b) { return realloc(a, b); } +static __inline void mg_free(void *a) { free(a); } + #endif -/* This following lines are just meant as a reminder to use the mg-functions for memory management */ +/* This following lines are just meant as a reminder to use the mg-functions + * for memory management */ #ifdef malloc - #undef malloc +#undef malloc #endif #ifdef calloc - #undef calloc +#undef calloc #endif #ifdef realloc - #undef realloc +#undef realloc #endif #ifdef free - #undef free +#undef free #endif -#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc -#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc +#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc +#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc -#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free - +#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free #define MD5_STATIC static #include "md5.inl" @@ -534,23 +706,19 @@ typedef int socklen_t; #endif /* NO_SOCKLEN_T */ #define _DARWIN_UNLIMITED_SELECT -#define IP_ADDR_STR_LEN 50 /* IPv6 hex string is 46 chars */ +#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */ #if !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 +#define MSG_NOSIGNAL (0) #endif #if !defined(SOMAXCONN) -#define SOMAXCONN 100 -#endif - -#if !defined(PATH_MAX) -#define PATH_MAX 4096 +#define SOMAXCONN (100) #endif /* Size of the accepted socket queue */ #if !defined(MGSQLEN) -#define MGSQLEN 20 +#define MGSQLEN (20) #endif #if defined(NO_SSL_DL) @@ -558,308 +726,374 @@ typedef int socklen_t; #include <openssl/err.h> #else /* SSL loaded dynamically from DLL. - I put the prototypes here to be independent from OpenSSL source - installation. */ + * I put the prototypes here to be independent from OpenSSL source + * installation. */ typedef struct ssl_st SSL; typedef struct ssl_method_st SSL_METHOD; typedef struct ssl_ctx_st SSL_CTX; struct ssl_func { - const char *name; /* SSL function name */ - void (*ptr)(void); /* Function pointer */ + const char *name; /* SSL function name */ + void (*ptr)(void); /* Function pointer */ }; -#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr) -#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr) -#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr) -#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr) -#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr) -#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr) -#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr) -#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr) -#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr) -#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr) -#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr) -#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \ - const char *, int)) ssl_sw[11].ptr) -#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \ - const char *, int)) ssl_sw[12].ptr) -#define SSL_CTX_set_default_passwd_cb \ - (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr) -#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr) -#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr) -#define SSL_CTX_use_certificate_chain_file \ - (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr) -#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr) -#define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr) -#define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr) -#define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr) - -#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr) -#define CRYPTO_set_locking_callback \ - (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr) -#define CRYPTO_set_id_callback \ - (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr) -#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr) -#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr) +#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr) +#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr) +#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr) +#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr) +#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr) +#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr) +#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr) +#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr) +#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr) +#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr) +#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr) +#define SSL_CTX_use_PrivateKey_file \ + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) +#define SSL_CTX_use_certificate_file \ + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) +#define SSL_CTX_set_default_passwd_cb \ + (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) +#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) +#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr) +#define SSL_CTX_use_certificate_chain_file \ + (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) +#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr) +#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr) +#define SSL_CTX_set_verify (*(void (*)(SSL_CTX *, int, int))ssl_sw[19].ptr) +#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr) + +#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) +#define CRYPTO_set_locking_callback \ + (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) +#define CRYPTO_set_id_callback \ + (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) +#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr) +#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr) /* set_ssl_option() function updates this array. - It loads SSL library dynamically and changes NULLs to the actual addresses - of respective functions. The macros above (like SSL_connect()) are really - just calling these functions indirectly via the pointer. */ -static struct ssl_func ssl_sw[] = { - {"SSL_free", NULL}, - {"SSL_accept", NULL}, - {"SSL_connect", NULL}, - {"SSL_read", NULL}, - {"SSL_write", NULL}, - {"SSL_get_error", NULL}, - {"SSL_set_fd", NULL}, - {"SSL_new", NULL}, - {"SSL_CTX_new", NULL}, - {"SSLv23_server_method", NULL}, - {"SSL_library_init", NULL}, - {"SSL_CTX_use_PrivateKey_file", NULL}, - {"SSL_CTX_use_certificate_file",NULL}, - {"SSL_CTX_set_default_passwd_cb",NULL}, - {"SSL_CTX_free", NULL}, - {"SSL_load_error_strings", NULL}, - {"SSL_CTX_use_certificate_chain_file", NULL}, - {"SSLv23_client_method", NULL}, - {"SSL_pending", NULL}, - {"SSL_CTX_set_verify", NULL}, - {"SSL_shutdown", NULL}, - {NULL, NULL} -}; + * It loads SSL library dynamically and changes NULLs to the actual addresses + * of respective functions. The macros above (like SSL_connect()) are really + * just calling these functions indirectly via the pointer. */ +static struct ssl_func ssl_sw[] = {{"SSL_free", NULL}, + {"SSL_accept", NULL}, + {"SSL_connect", NULL}, + {"SSL_read", NULL}, + {"SSL_write", NULL}, + {"SSL_get_error", NULL}, + {"SSL_set_fd", NULL}, + {"SSL_new", NULL}, + {"SSL_CTX_new", NULL}, + {"SSLv23_server_method", NULL}, + {"SSL_library_init", NULL}, + {"SSL_CTX_use_PrivateKey_file", NULL}, + {"SSL_CTX_use_certificate_file", NULL}, + {"SSL_CTX_set_default_passwd_cb", NULL}, + {"SSL_CTX_free", NULL}, + {"SSL_load_error_strings", NULL}, + {"SSL_CTX_use_certificate_chain_file", NULL}, + {"SSLv23_client_method", NULL}, + {"SSL_pending", NULL}, + {"SSL_CTX_set_verify", NULL}, + {"SSL_shutdown", NULL}, + {NULL, NULL}}; /* Similar array as ssl_sw. These functions could be located in different - lib. */ + * lib. */ #if !defined(NO_SSL) -static struct ssl_func crypto_sw[] = { - {"CRYPTO_num_locks", NULL}, - {"CRYPTO_set_locking_callback", NULL}, - {"CRYPTO_set_id_callback", NULL}, - {"ERR_get_error", NULL}, - {"ERR_error_string", NULL}, - {NULL, NULL} -}; +static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL}, + {"CRYPTO_set_locking_callback", NULL}, + {"CRYPTO_set_id_callback", NULL}, + {"ERR_get_error", NULL}, + {"ERR_error_string", NULL}, + {NULL, NULL}}; #endif /* NO_SSL */ #endif /* NO_SSL_DL */ -static const char *month_names[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -/* Unified socket address. For IPv6 support, add IPv6 address structure - in the union u. */ +static const char *month_names[] = {"Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"}; + +/* Unified socket address. For IPv6 support, add IPv6 address structure in the + * union u. */ union usa { - struct sockaddr sa; - struct sockaddr_in sin; + struct sockaddr sa; + struct sockaddr_in sin; #if defined(USE_IPV6) - struct sockaddr_in6 sin6; + struct sockaddr_in6 sin6; #endif }; /* Describes a string (chunk of memory). */ struct vec { - const char *ptr; - size_t len; + const char *ptr; + size_t len; }; struct file { - int is_directory; - time_t modification_time; - int64_t size; - FILE *fp; - const char *membuf; /* Non-NULL if file data is in memory */ - /* set to 1 if the content is gzipped - in which case we need a content-encoding: gzip header */ - int gzipped; + uint64_t size; + time_t last_modified; + FILE *fp; + const char *membuf; /* Non-NULL if file data is in memory */ + int is_directory; + int gzipped; /* set to 1 if the content is gzipped + * in which case we need a content-encoding: gzip header */ }; -#define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0} +#define STRUCT_FILE_INITIALIZER \ + { \ + (uint64_t)0, (time_t)0, (FILE *)NULL, (const char *)NULL, 0, 0 \ + } /* Describes listening socket, or socket which was accept()-ed by the master - thread and queued for future handling by the worker thread. */ + * thread and queued for future handling by the worker thread. */ struct socket { - SOCKET sock; /* Listening socket */ - union usa lsa; /* Local socket address */ - union usa rsa; /* Remote socket address */ - unsigned is_ssl:1; /* Is port SSL-ed */ - unsigned ssl_redir:1; /* Is port supposed to redirect everything to SSL - port */ + SOCKET sock; /* Listening socket */ + union usa lsa; /* Local socket address */ + union usa rsa; /* Remote socket address */ + unsigned char is_ssl; /* Is port SSL-ed */ + unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL + * port */ }; /* NOTE(lsm): this enum shoulds be in sync with the config_options below. */ enum { - CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER, - PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, THROTTLE, - ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE, - GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, - EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE, - NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT, - DECODE_URL, + CGI_EXTENSIONS, + CGI_ENVIRONMENT, + PUT_DELETE_PASSWORDS_FILE, + CGI_INTERPRETER, + PROTECT_URI, + AUTHENTICATION_DOMAIN, + SSI_EXTENSIONS, + THROTTLE, + ACCESS_LOG_FILE, + ENABLE_DIRECTORY_LISTING, + ERROR_LOG_FILE, + GLOBAL_PASSWORDS_FILE, + INDEX_FILES, + ENABLE_KEEP_ALIVE, + ACCESS_CONTROL_LIST, + EXTRA_MIME_TYPES, + LISTENING_PORTS, + DOCUMENT_ROOT, + SSL_CERTIFICATE, + NUM_THREADS, + RUN_AS_USER, + REWRITE, + HIDE_FILES, + REQUEST_TIMEOUT, +#if defined(USE_WEBSOCKET) + WEBSOCKET_TIMEOUT, +#endif + DECODE_URL, #if defined(USE_LUA) - LUA_PRELOAD_FILE, LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS, + LUA_PRELOAD_FILE, + LUA_SCRIPT_EXTENSIONS, + LUA_SERVER_PAGE_EXTENSIONS, #endif #if defined(USE_WEBSOCKET) - WEBSOCKET_ROOT, + WEBSOCKET_ROOT, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) - LUA_WEBSOCKET_EXTENSIONS, + LUA_WEBSOCKET_EXTENSIONS, #endif - ACCESS_CONTROL_ALLOW_ORIGIN, ERROR_PAGES, + ACCESS_CONTROL_ALLOW_ORIGIN, + ERROR_PAGES, - NUM_OPTIONS + NUM_OPTIONS }; /* Config option name, config types, default value */ static struct mg_option config_options[] = { - {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, - {"cgi_environment", CONFIG_TYPE_STRING, NULL}, - {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, - {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, - {"protect_uri", CONFIG_TYPE_STRING, NULL}, - {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, - {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, - {"throttle", CONFIG_TYPE_STRING, NULL}, - {"access_log_file", CONFIG_TYPE_FILE, NULL}, - {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, - {"error_log_file", CONFIG_TYPE_FILE, NULL}, - {"global_auth_file", CONFIG_TYPE_FILE, NULL}, - {"index_files", CONFIG_TYPE_STRING, + {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, + {"cgi_environment", CONFIG_TYPE_STRING, NULL}, + {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, + {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, + {"protect_uri", CONFIG_TYPE_STRING, NULL}, + {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, + {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, + {"throttle", CONFIG_TYPE_STRING, NULL}, + {"access_log_file", CONFIG_TYPE_FILE, NULL}, + {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, + {"error_log_file", CONFIG_TYPE_FILE, NULL}, + {"global_auth_file", CONFIG_TYPE_FILE, NULL}, + {"index_files", + CONFIG_TYPE_STRING, #ifdef USE_LUA - "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php"}, + "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi," + "index.shtml,index.php"}, #else - "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, -#endif - {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, - {"access_control_list", CONFIG_TYPE_STRING, NULL}, - {"extra_mime_types", CONFIG_TYPE_STRING, NULL}, - {"listening_ports", CONFIG_TYPE_STRING, "8080"}, - {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, - {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, - {"num_threads", CONFIG_TYPE_NUMBER, "50"}, - {"run_as_user", CONFIG_TYPE_STRING, NULL}, - {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL}, - {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, - {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, - {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"}, + "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, +#endif + {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, + {"access_control_list", CONFIG_TYPE_STRING, NULL}, + {"extra_mime_types", CONFIG_TYPE_STRING, NULL}, + {"listening_ports", CONFIG_TYPE_STRING, "8080"}, + {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, + {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, + {"num_threads", CONFIG_TYPE_NUMBER, "50"}, + {"run_as_user", CONFIG_TYPE_STRING, NULL}, + {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL}, + {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, + {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, +#if defined(USE_WEBSOCKET) + {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, +#endif + {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"}, #if defined(USE_LUA) - {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, - {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, - {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, + {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, + {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, #endif #if defined(USE_WEBSOCKET) - {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, + {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) - {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, #endif - {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, - {"error_pages", CONFIG_TYPE_DIRECTORY, NULL}, + {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, + {"error_pages", CONFIG_TYPE_DIRECTORY, NULL}, + + {NULL, CONFIG_TYPE_UNKNOWN, NULL}}; + + +/* Check if the config_options and the corresponding enum have compatible + * sizes. */ +mg_static_assert((sizeof(config_options) / sizeof(config_options[0])) == + (NUM_OPTIONS + 1), + "config_options and enum not sync"); - {NULL, CONFIG_TYPE_UNKNOWN, NULL} -}; struct mg_request_handler_info { - char *uri; - size_t uri_len; - mg_request_handler handler; + /* Name/Pattern of the URI. */ + char *uri; + size_t uri_len; + + /* URI type: ws/wss (websocket) or http/https (web page). */ + int is_websocket_handler; + + /* Handler for http/https requests. */ + mg_request_handler handler; + + /* Handler for ws/wss (websocket) requests. */ + mg_websocket_connect_handler connect_handler; + mg_websocket_ready_handler ready_handler; + mg_websocket_data_handler data_handler; + mg_websocket_close_handler close_handler; + + /* User supplied argument for the handler function. */ + void *cbdata; - void *cbdata; - struct mg_request_handler_info *next; + /* next request handler in a linked list */ + struct mg_request_handler_info *next; }; struct mg_context { - volatile int stop_flag; /* Should we stop event loop */ - SSL_CTX *ssl_ctx; /* SSL context */ - char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ - struct mg_callbacks callbacks; /* User-defined callback function */ - void *user_data; /* User-defined data */ - int context_type; /* 1 = server context, 2 = client context */ - - struct socket *listening_sockets; - in_port_t *listening_ports; - int num_listening_sockets; - - volatile int num_threads; /* Number of threads */ - pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ - pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ - - struct socket queue[MGSQLEN]; /* Accepted sockets */ - volatile int sq_head; /* Head of the socket queue */ - volatile int sq_tail; /* Tail of the socket queue */ - pthread_cond_t sq_full; /* Signaled when socket is produced */ - pthread_cond_t sq_empty; /* Signaled when socket is consumed */ - pthread_t masterthreadid; /* The master thread ID */ - int workerthreadcount; /* The amount of worker threads. */ - pthread_t *workerthreadids; /* The worker thread IDs */ - - unsigned long start_time; /* Server start time, used for authentication */ - pthread_mutex_t nonce_mutex; /* Protects nonce_count */ - unsigned long nonce_count; /* Used nonces, used for authentication */ - - char *systemName; /* What operating system is running */ - - /* linked list of uri handlers */ - struct mg_request_handler_info *request_handlers; + volatile int stop_flag; /* Should we stop event loop */ + SSL_CTX *ssl_ctx; /* SSL context */ + char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ + struct mg_callbacks callbacks; /* User-defined callback function */ + void *user_data; /* User-defined data */ + int context_type; /* 1 = server context, 2 = client context */ + + struct socket *listening_sockets; + in_port_t *listening_ports; + unsigned int num_listening_sockets; + + volatile int num_threads; /* Number of threads */ + pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ + pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ + + struct socket queue[MGSQLEN]; /* Accepted sockets */ + volatile int sq_head; /* Head of the socket queue */ + volatile int sq_tail; /* Tail of the socket queue */ + pthread_cond_t sq_full; /* Signaled when socket is produced */ + pthread_cond_t sq_empty; /* Signaled when socket is consumed */ + pthread_t masterthreadid; /* The master thread ID */ + unsigned int workerthreadcount; /* The amount of worker threads. */ + pthread_t *workerthreadids; /* The worker thread IDs */ + + unsigned long start_time; /* Server start time, used for authentication */ + pthread_mutex_t nonce_mutex; /* Protects nonce_count */ + unsigned long nonce_count; /* Used nonces, used for authentication */ + + char *systemName; /* What operating system is running */ + + /* linked list of uri handlers */ + struct mg_request_handler_info *request_handlers; #if defined(USE_LUA) && defined(USE_WEBSOCKET) - /* linked list of shared lua websockets */ - struct mg_shared_lua_websocket_list *shared_lua_websockets; + /* linked list of shared lua websockets */ + struct mg_shared_lua_websocket_list *shared_lua_websockets; #endif #ifdef USE_TIMERS - struct ttimers * timers; + struct ttimers *timers; #endif }; struct mg_connection { - struct mg_request_info request_info; - struct mg_context *ctx; - SSL *ssl; /* SSL descriptor */ - SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ - struct socket client; /* Connected client */ - time_t birth_time; /* Time when request was received */ - int64_t num_bytes_sent; /* Total bytes sent to client */ - int64_t content_len; /* Content-Length header value */ - int64_t consumed_content; /* How many bytes of content have been read */ - char *buf; /* Buffer for received data */ - char *path_info; /* PATH_INFO part of the URL */ - int must_close; /* 1 if connection must be closed */ - int in_error_handler; /* 1 if in handler for user defined error pages */ - int buf_size; /* Buffer size */ - int request_len; /* Size of the request + headers in a buffer */ - int data_len; /* Total size of data in a buffer */ - int status_code; /* HTTP reply status code, e.g. 200 */ - int throttle; /* Throttling, bytes/sec. <= 0 means no throttle */ - time_t last_throttle_time; /* Last time throttled data was sent */ - int64_t last_throttle_bytes; /* Bytes sent this second */ - pthread_mutex_t mutex; /* Used by mg_lock_connection/mg_unlock_connection to ensure atomic transmissions for websockets */ + struct mg_request_info request_info; + struct mg_context *ctx; + SSL *ssl; /* SSL descriptor */ + SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ + struct socket client; /* Connected client */ + time_t conn_birth_time; /* Time (wall clock) when connection was + * established */ + struct timespec req_time; /* Time (since system start) when the request + * was received */ + int64_t num_bytes_sent; /* Total bytes sent to client */ + int64_t content_len; /* Content-Length header value */ + int64_t consumed_content; /* How many bytes of content have been read */ + int is_chunked; /* Transfer-encoding is chunked: 0=no, 1=yes: + * data available, 2: all data read */ + size_t chunk_remainder; /* Unread data from the last chunk */ + char *buf; /* Buffer for received data */ + char *path_info; /* PATH_INFO part of the URL */ + int must_close; /* 1 if connection must be closed */ + int in_error_handler; /* 1 if in handler for user defined error + * pages */ + int buf_size; /* Buffer size */ + int request_len; /* Size of the request + headers in a buffer */ + int data_len; /* Total size of data in a buffer */ + int status_code; /* HTTP reply status code, e.g. 200 */ + int throttle; /* Throttling, bytes/sec. <= 0 means no + * throttle */ + time_t last_throttle_time; /* Last time throttled data was sent */ + int64_t last_throttle_bytes; /* Bytes sent this second */ + pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure + * atomic transmissions for websockets */ #if defined(USE_LUA) && defined(USE_WEBSOCKET) - void * lua_websocket_state; /* Lua_State for a websocket connection */ + void *lua_websocket_state; /* Lua_State for a websocket connection */ #endif }; -static pthread_key_t sTlsKey; /* Thread local storage index */ +static pthread_key_t sTlsKey; /* Thread local storage index */ static int sTlsInit = 0; struct mg_workerTLS { - int is_master; + int is_master; #if defined(_WIN32) && !defined(__SYMBIAN32__) - HANDLE pthread_cond_helper_mutex; + HANDLE pthread_cond_helper_mutex; #endif }; /* Directory entry */ struct de { - struct mg_connection *conn; - char *file_name; - struct file file; + struct mg_connection *conn; + char *file_name; + struct file file; }; #if defined(USE_WEBSOCKET) @@ -868,4450 +1102,5690 @@ static int is_websocket_protocol(const struct mg_connection *conn); #define is_websocket_protocol(conn) (0) #endif -int mg_atomic_inc(volatile int * addr) +static int mg_atomic_inc(volatile int *addr) { - int ret; + int ret; #if defined(_WIN32) && !defined(__SYMBIAN32__) - ret = InterlockedIncrement((volatile unsigned int *) addr); + /* Depending on the SDK, this function uses either + * (volatile unsigned int *) or (volatile LONG *), + * so whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedIncrement((volatile long *)addr); #elif defined(__GNUC__) - ret = __sync_add_and_fetch(addr, 1); + ret = __sync_add_and_fetch(addr, 1); #else - ret = (++(*addr)); + ret = (++(*addr)); #endif - return ret; + return ret; } -int mg_atomic_dec(volatile int * addr) +static int mg_atomic_dec(volatile int *addr) { - int ret; + int ret; #if defined(_WIN32) && !defined(__SYMBIAN32__) - ret = InterlockedDecrement((volatile unsigned int *) addr); + /* Depending on the SDK, this function uses either + * (volatile unsigned int *) or (volatile LONG *), + * so whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedDecrement((volatile long *)addr); #elif defined(__GNUC__) - ret = __sync_sub_and_fetch(addr, 1); + ret = __sync_sub_and_fetch(addr, 1); #else - ret = (--(*addr)); + ret = (--(*addr)); #endif - return ret; + return ret; } #if !defined(NO_THREAD_NAME) #if defined(_WIN32) && defined(_MSC_VER) /* Set the thread name for debugging purposes in Visual Studio - http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx -*/ -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; /* Must be 0x1000. */ - LPCSTR szName; /* Pointer to name (in user addr space). */ - DWORD dwThreadID; /* Thread ID (-1=caller thread). */ - DWORD dwFlags; /* Reserved for future use, must be zero. */ + * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + */ +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO { + DWORD dwType; /* Must be 0x1000. */ + LPCSTR szName; /* Pointer to name (in user addr space). */ + DWORD dwThreadID; /* Thread ID (-1=caller thread). */ + DWORD dwFlags; /* Reserved for future use, must be zero. */ } THREADNAME_INFO; #pragma pack(pop) #elif defined(__linux__) #include <sys/prctl.h> #endif -void mg_set_thread_name(const char* name) +static void mg_set_thread_name(const char *name) { - char threadName[16]; /* Max. thread length in Linux/OSX/.. */ + char threadName[16]; /* Max. thread length in Linux/OSX/.. */ - if (snprintf(threadName, sizeof(threadName), "civetweb-%s", name)<0) return; - threadName[sizeof(threadName)-1] = 0; + /* TODO (low): use strcpy and strcat instad of snprintf, use server name, + * don't + * return */ + if (snprintf(threadName, sizeof(threadName), "civetweb-%s", name) < 0) { + return; + } + + threadName[sizeof(threadName) - 1] = 0; #if defined(_WIN32) #if defined(_MSC_VER) - /* Windows and Visual Studio Compiler */ - __try - { - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = -1; - info.dwFlags = 0; - - RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } + /* Windows and Visual Studio Compiler */ + __try + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = ~0U; + info.dwFlags = 0; + + RaiseException(0x406D1388, + 0, + sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) {} #elif defined(__MINGW32__) - /* No option known to set thread name for MinGW */ + /* No option known to set thread name for MinGW */ + ; #endif #elif defined(__linux__) - /* Linux */ - (void)prctl(PR_SET_NAME,threadName,0,0,0); + /* Linux */ + (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); #elif defined(__APPLE__) || defined(__MACH__) - /* OS X (TODO: test) */ - (void)pthread_setname_np(threadName); + /* OS X (TODO: test) */ + (void)pthread_setname_np(threadName); #elif defined(BSD) || defined(__FreeBSD__) || defined(__OpenBSD__) - /* BSD (TODO: test) */ - pthread_set_name_np(pthread_self(), threadName); + /* BSD (TODO: test) */ + pthread_set_name_np(pthread_self(), threadName); +#elif defined(__AIX__) || defined(_AIX) || defined(__hpux) || defined(__sun) +/* pthread_set_name_np seems to be missing on AIX, hpux, sun, ... */ #else - /* POSIX */ - (void)pthread_setname_np(pthread_self(), threadName); + /* POSIX */ + (void)pthread_setname_np(pthread_self(), threadName); #endif } #else /* !defined(NO_THREAD_NAME) */ -void mg_set_thread_name(const char* threadName) {} +void mg_set_thread_name(const char *threadName) {} #endif #if defined(MG_LEGACY_INTERFACE) const char **mg_get_valid_option_names(void) { - static const char * data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; - int i; + static const char * + data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; + int i; - for (i=0; config_options[i].name != NULL; i++) { - data[i * 2] = config_options[i].name; - data[i * 2 + 1] = config_options[i].default_value; - } + for (i = 0; config_options[i].name != NULL; i++) { + data[i * 2] = config_options[i].name; + data[i * 2 + 1] = config_options[i].default_value; + } - return data; + return data; } #endif -const struct mg_option *mg_get_valid_options(void) -{ - return config_options; -} - +const struct mg_option *mg_get_valid_options(void) { return config_options; } -static int is_file_in_memory(struct mg_connection *conn, const char *path, +static int is_file_in_memory(struct mg_connection *conn, + const char *path, struct file *filep) { - size_t size = 0; - if ((filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL : - conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) { - /* NOTE: override filep->size only on success. Otherwise, it might - break constructs like if (!mg_stat() || !mg_fopen()) ... */ - filep->size = size; - } - return filep->membuf != NULL; + size_t size = 0; + if (!conn || !filep) { + return 0; + } + + filep->last_modified = (time_t)0; + + if ((filep->membuf = + conn->ctx->callbacks.open_file == NULL + ? NULL + : conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) { + /* NOTE: override filep->size only on success. Otherwise, it might + * break constructs like if (!mg_stat() || !mg_fopen()) ... */ + filep->size = size; + } + return filep->membuf != NULL; } static int is_file_opened(const struct file *filep) { - return filep->membuf != NULL || filep->fp != NULL; + if (!filep) { + return 0; + } + + return filep->membuf != NULL || filep->fp != NULL; } -static int mg_fopen(struct mg_connection *conn, const char *path, - const char *mode, struct file *filep) +static int mg_fopen(struct mg_connection *conn, + const char *path, + const char *mode, + struct file *filep) { - if (!is_file_in_memory(conn, path, filep)) { + if (!filep) { + return 0; + } + + if (!is_file_in_memory(conn, path, filep)) { #ifdef _WIN32 - wchar_t wbuf[PATH_MAX], wmode[20]; - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); - filep->fp = _wfopen(wbuf, wmode); + wchar_t wbuf[PATH_MAX], wmode[20]; + to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); + filep->fp = _wfopen(wbuf, wmode); #else - filep->fp = fopen(path, mode); + filep->fp = fopen(path, mode); #endif - } + } - return is_file_opened(filep); + return is_file_opened(filep); } static void mg_fclose(struct file *filep) { - if (filep != NULL && filep->fp != NULL) { - fclose(filep->fp); - } + if (filep != NULL && filep->fp != NULL) { + fclose(filep->fp); + } } static void mg_strlcpy(register char *dst, register const char *src, size_t n) { - for (; *src != '\0' && n > 1; n--) { - *dst++ = *src++; - } - *dst = '\0'; + for (; *src != '\0' && n > 1; n--) { + *dst++ = *src++; + } + *dst = '\0'; } static int lowercase(const char *s) { - return tolower(* (const unsigned char *) s); + return tolower(*(const unsigned char *)s); } int mg_strncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; + int diff = 0; - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); + if (len > 0) { + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + } - return diff; + return diff; } static int mg_strcasecmp(const char *s1, const char *s2) { - int diff; + int diff; - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0'); - return diff; + return diff; } -static char * mg_strndup(const char *ptr, size_t len) +static char *mg_strndup(const char *ptr, size_t len) { - char *p; + char *p; - if ((p = (char *) mg_malloc(len + 1)) != NULL) { - mg_strlcpy(p, ptr, len + 1); - } + if ((p = (char *)mg_malloc(len + 1)) != NULL) { + mg_strlcpy(p, ptr, len + 1); + } - return p; + return p; } -static char * mg_strdup(const char *str) -{ - return mg_strndup(str, strlen(str)); -} +static char *mg_strdup(const char *str) { return mg_strndup(str, strlen(str)); } static const char *mg_strcasestr(const char *big_str, const char *small_str) { - int i, big_len = (int)strlen(big_str), small_len = (int)strlen(small_str); + size_t i, big_len = strlen(big_str), small_len = strlen(small_str); - for (i = 0; i <= big_len - small_len; i++) { - if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { - return big_str + i; - } - } + if (big_len >= small_len) { + for (i = 0; i <= (big_len - small_len); i++) { + if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { + return big_str + i; + } + } + } - return NULL; + return NULL; } /* Like snprintf(), but never returns negative value, or a value - that is larger than a supplied buffer. - Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability - in his audit report. */ -static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen, - const char *fmt, va_list ap) -{ - int n; + * that is larger than a supplied buffer. + * Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability + * in his audit report. */ +static int mg_vsnprintf(struct mg_connection *conn, + char *buf, + size_t buflen, + const char *fmt, + va_list ap) +{ + int n; + + if (buflen == 0) { + return 0; + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +/* Using fmt as a non-literal is intended here, since it is mostly called + * indirectly by mg_snprintf */ +#endif - if (buflen == 0) - return 0; + n = vsnprintf(buf, buflen, fmt, ap); - n = vsnprintf(buf, buflen, fmt, ap); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif - if (n < 0) { - mg_cry(conn, "vsnprintf error"); - n = 0; - } else if (n >= (int) buflen) { - mg_cry(conn, "truncating vsnprintf buffer: [%.*s]", - n > 200 ? 200 : n, buf); - n = (int) buflen - 1; - } - buf[n] = '\0'; + if (n < 0) { + mg_cry(conn, "vsnprintf error"); + n = 0; + } else if (n >= (int)buflen) { + mg_cry(conn, + "truncating vsnprintf buffer: [%.*s]", + n > 200 ? 200 : n, + buf); + n = (int)buflen - 1; + } + buf[n] = '\0'; - return n; + return n; } -static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, - PRINTF_FORMAT_STRING(const char *fmt), ...) -PRINTF_ARGS(4, 5); +static int mg_snprintf(struct mg_connection *conn, + char *buf, + size_t buflen, + PRINTF_FORMAT_STRING(const char *fmt), + ...) PRINTF_ARGS(4, 5); -static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, - const char *fmt, ...) +static int mg_snprintf( + struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, ...) { - va_list ap; - int n; + va_list ap; + int n; - va_start(ap, fmt); - n = mg_vsnprintf(conn, buf, buflen, fmt, ap); - va_end(ap); + va_start(ap, fmt); + n = mg_vsnprintf(conn, buf, buflen, fmt, ap); + va_end(ap); - return n; + return n; } static int get_option_index(const char *name) { - int i; + int i; - for (i = 0; config_options[i].name != NULL; i++) { - if (strcmp(config_options[i].name, name) == 0) { - return i; - } - } - return -1; + for (i = 0; config_options[i].name != NULL; i++) { + if (strcmp(config_options[i].name, name) == 0) { + return i; + } + } + return -1; } const char *mg_get_option(const struct mg_context *ctx, const char *name) { - int i; - if ((i = get_option_index(name)) == -1) { - return NULL; - } else if (ctx->config[i] == NULL) { - return ""; - } else { - return ctx->config[i]; - } + int i; + if ((i = get_option_index(name)) == -1) { + return NULL; + } else if (!ctx || ctx->config[i] == NULL) { + return ""; + } else { + return ctx->config[i]; + } +} + +struct mg_context *mg_get_context(const struct mg_connection *conn) +{ + return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); +} + +void *mg_get_user_data(const struct mg_context *ctx) +{ + return (ctx == NULL) ? NULL : ctx->user_data; } -struct mg_context *mg_get_context(struct mg_connection * conn) +void mg_set_user_connection_data(const struct mg_connection *conn, void *data) { - return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); + if (conn != NULL) { + ((struct mg_connection *)conn)->request_info.conn_data = data; + } } -void *mg_get_user_data(struct mg_context *ctx) +void *mg_get_user_connection_data(const struct mg_connection *conn) { - return (ctx == NULL) ? NULL : ctx->user_data; + if (conn != NULL) { + return conn->request_info.conn_data; + } + return NULL; } -size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl) +size_t +mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl) { - size_t i; - for (i = 0; i < size && i < (size_t)ctx->num_listening_sockets; i++) - { - ssl[i] = ctx->listening_sockets[i].is_ssl; - ports[i] = ctx->listening_ports[i]; - } - return i; + size_t i; + if (!ctx) { + return 0; + } + for (i = 0; i < size && i < ctx->num_listening_sockets; i++) { + ssl[i] = ctx->listening_sockets[i].is_ssl; + ports[i] = ctx->listening_ports[i]; + } + return i; } -static void sockaddr_to_string(char *buf, size_t len, - const union usa *usa) +static void sockaddr_to_string(char *buf, size_t len, const union usa *usa) { - buf[0] = '\0'; + buf[0] = '\0'; + + if (!usa) { + return; + } + + if (usa->sa.sa_family == AF_INET) { + getnameinfo(&usa->sa, + sizeof(usa->sin), + buf, + (unsigned)len, + NULL, + 0, + NI_NUMERICHOST); + } #if defined(USE_IPV6) - inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ? - (void *) &usa->sin.sin_addr : - (void *) &usa->sin6.sin6_addr, buf, len); -#elif defined(_WIN32) - /* Only Windows Vista (and newer) have inet_ntop() */ - mg_strlcpy(buf, inet_ntoa(usa->sin.sin_addr), len); -#else - inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len); + else if (usa->sa.sa_family == AF_INET6) { + getnameinfo(&usa->sa, + sizeof(usa->sin6), + buf, + (unsigned)len, + NULL, + 0, + NI_NUMERICHOST); + } #endif } -/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be included in all responses other than 100, 101, 5xx. */ +/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be + * included in all responses other than 100, 101, 5xx. */ static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { - struct tm *tm; + struct tm *tm; + + tm = gmtime(t); + if (tm != NULL) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); + } else { + mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); + buf[buf_len - 1] = '\0'; + } +} - tm = gmtime(t); - if (tm != NULL) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); - } else { - mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); - buf[buf_len - 1] = '\0'; - } +/* difftime for struct timespec. Return value is in seconds. */ +static double mg_difftimespec(const struct timespec *ts_now, + const struct timespec *ts_before) +{ + return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 + + (double)(ts_now->tv_sec - ts_before->tv_sec); } /* Print error message to the opened error log stream. */ -void mg_cry(struct mg_connection *conn, const char *fmt, ...) -{ - char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; - va_list ap; - FILE *fp; - time_t timestamp; - - va_start(ap, fmt); - IGNORE_UNUSED_RESULT(vsnprintf(buf, sizeof(buf), fmt, ap)); - va_end(ap); - - /* Do not lock when getting the callback value, here and below. - I suppose this is fine, since function cannot disappear in the - same way string option can. */ - if (conn->ctx->callbacks.log_message == NULL || - conn->ctx->callbacks.log_message(conn, buf) == 0) { - fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : - fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); - - if (fp != NULL) { - flockfile(fp); - timestamp = time(NULL); - - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp, - src_addr); - - if (conn->request_info.request_method != NULL) { - fprintf(fp, "%s %s: ", conn->request_info.request_method, - conn->request_info.uri); - } - - fprintf(fp, "%s", buf); - fputc('\n', fp); - funlockfile(fp); - fclose(fp); - } - } +void mg_cry(const struct mg_connection *conn, const char *fmt, ...) +{ + char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; + va_list ap; + FILE *fp; + time_t timestamp; + + va_start(ap, fmt); + IGNORE_UNUSED_RESULT(vsnprintf(buf, sizeof(buf), fmt, ap)); + va_end(ap); + + /* Do not lock when getting the callback value, here and below. + * I suppose this is fine, since function cannot disappear in the + * same way string option can. */ + if (conn && (conn->ctx->callbacks.log_message == NULL || + conn->ctx->callbacks.log_message(conn, buf) == 0)) { + fp = conn->ctx->config[ERROR_LOG_FILE] == NULL + ? NULL + : fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); + + if (fp != NULL) { + flockfile(fp); + timestamp = time(NULL); + + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + fprintf(fp, + "[%010lu] [error] [client %s] ", + (unsigned long)timestamp, + src_addr); + + if (conn->request_info.request_method != NULL) { + fprintf(fp, + "%s %s: ", + conn->request_info.request_method, + conn->request_info.uri); + } + + fprintf(fp, "%s", buf); + fputc('\n', fp); + funlockfile(fp); + fclose(fp); + } + } } /* Return fake connection structure. Used for logging, if connection - is not applicable at the moment of logging. */ + * is not applicable at the moment of logging. */ static struct mg_connection *fc(struct mg_context *ctx) { - static struct mg_connection fake_connection; - fake_connection.ctx = ctx; - return &fake_connection; + static struct mg_connection fake_connection; + fake_connection.ctx = ctx; + return &fake_connection; } -const char *mg_version(void) -{ - return CIVETWEB_VERSION; -} +const char *mg_version(void) { return CIVETWEB_VERSION; } -struct mg_request_info *mg_get_request_info(struct mg_connection *conn) +const struct mg_request_info * +mg_get_request_info(const struct mg_connection *conn) { - return &conn->request_info; + if (!conn) { + return NULL; + } + return &conn->request_info; } /* Skip the characters until one of the delimiters characters found. - 0-terminate resulting word. Skip the delimiter and following whitespaces. - Advance pointer to buffer to the next word. Return found 0-terminated word. - Delimiters can be quoted with quotechar. */ -static char *skip_quoted(char **buf, const char *delimiters, - const char *whitespace, char quotechar) -{ - char *p, *begin_word, *end_word, *end_whitespace; - - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); - - /* Check for quotechar */ - if (end_word > begin_word) { - p = end_word - 1; - while (*p == quotechar) { - /* TODO (bel): it seems this code is never reached, so quotechar is actually - not needed - check if this code may be droped */ - - /* If there is anything beyond end_word, copy it */ - if (*end_word == '\0') { - *p = '\0'; - break; - } else { - size_t end_off = strcspn(end_word + 1, delimiters); - memmove (p, end_word, end_off + 1); - p += end_off; /* p must correspond to end_word - 1 */ - end_word += end_off + 1; - } - } - for (p++; p < end_word; p++) { - *p = '\0'; - } - } - - if (*end_word == '\0') { - *buf = end_word; - } else { - end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); - - for (p = end_word; p < end_whitespace; p++) { - *p = '\0'; - } - - *buf = end_whitespace; - } - - return begin_word; + * 0-terminate resulting word. Skip the delimiter and following whitespaces. + * Advance pointer to buffer to the next word. Return found 0-terminated word. + * Delimiters can be quoted with quotechar. */ +static char *skip_quoted(char **buf, + const char *delimiters, + const char *whitespace, + char quotechar) +{ + char *p, *begin_word, *end_word, *end_whitespace; + + begin_word = *buf; + end_word = begin_word + strcspn(begin_word, delimiters); + + /* Check for quotechar */ + if (end_word > begin_word) { + p = end_word - 1; + while (*p == quotechar) { + /* TODO (bel, low): it seems this code is never reached, so + * quotechar is actually not needed - check if this code may be + * droped */ + + /* If there is anything beyond end_word, copy it */ + if (*end_word == '\0') { + *p = '\0'; + break; + } else { + size_t end_off = strcspn(end_word + 1, delimiters); + memmove(p, end_word, end_off + 1); + p += end_off; /* p must correspond to end_word - 1 */ + end_word += end_off + 1; + } + } + for (p++; p < end_word; p++) { + *p = '\0'; + } + } + + if (*end_word == '\0') { + *buf = end_word; + } else { + end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); + + for (p = end_word; p < end_whitespace; p++) { + *p = '\0'; + } + + *buf = end_whitespace; + } + + return begin_word; } /* Simplified version of skip_quoted without quote char - and whitespace == delimiters */ + * and whitespace == delimiters */ static char *skip(char **buf, const char *delimiters) { - return skip_quoted(buf, delimiters, delimiters, 0); + return skip_quoted(buf, delimiters, delimiters, 0); } - /* Return HTTP header value, or NULL if not found. */ static const char *get_header(const struct mg_request_info *ri, const char *name) { - int i; - - for (i = 0; i < ri->num_headers; i++) - if (!mg_strcasecmp(name, ri->http_headers[i].name)) - return ri->http_headers[i].value; + int i; + if (ri) { + for (i = 0; i < ri->num_headers; i++) { + if (!mg_strcasecmp(name, ri->http_headers[i].name)) { + return ri->http_headers[i].value; + } + } + } - return NULL; + return NULL; } const char *mg_get_header(const struct mg_connection *conn, const char *name) { - return get_header(&conn->request_info, name); + if (!conn) { + return NULL; + } + + return get_header(&conn->request_info, name); } /* A helper function for traversing a comma separated list of values. - It returns a list pointer shifted to the next value, or NULL if the end - of the list found. - Value is stored in val vector. If value has form "x=y", then eq_val - vector is initialized to point to the "y" part, and val vector length - is adjusted to point only to "x". */ -static const char *next_option(const char *list, struct vec *val, - struct vec *eq_val) -{ - if (list == NULL || *list == '\0') { - /* End of the list */ - list = NULL; - } else { - val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - /* Comma found. Store length and shift the list ptr */ - val->len = list - val->ptr; - list++; - } else { - /* This value is the last one */ - list = val->ptr + strlen(val->ptr); - val->len = list - val->ptr; - } - - if (eq_val != NULL) { - /* Value has form "x=y", adjust pointers and lengths - so that val points to "x", and eq_val points to "y". */ - eq_val->len = 0; - eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); - if (eq_val->ptr != NULL) { - eq_val->ptr++; /* Skip over '=' character */ - eq_val->len = val->ptr + val->len - eq_val->ptr; - val->len = (eq_val->ptr - val->ptr) - 1; - } - } - } - - return list; + * It returns a list pointer shifted to the next value, or NULL if the end + * of the list found. + * Value is stored in val vector. If value has form "x=y", then eq_val + * vector is initialized to point to the "y" part, and val vector length + * is adjusted to point only to "x". */ +static const char * +next_option(const char *list, struct vec *val, struct vec *eq_val) +{ + if (val == NULL || list == NULL || *list == '\0') { + /* End of the list */ + list = NULL; + } else { + val->ptr = list; + if ((list = strchr(val->ptr, ',')) != NULL) { + /* Comma found. Store length and shift the list ptr */ + val->len = ((size_t)(list - val->ptr)); + list++; + } else { + /* This value is the last one */ + list = val->ptr + strlen(val->ptr); + val->len = ((size_t)(list - val->ptr)); + } + + if (eq_val != NULL) { + /* Value has form "x=y", adjust pointers and lengths + * so that val points to "x", and eq_val points to "y". */ + eq_val->len = 0; + eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len); + if (eq_val->ptr != NULL) { + eq_val->ptr++; /* Skip over '=' character */ + eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; + val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; + } + } + } + + return list; } /* Perform case-insensitive match of string against pattern */ -static int match_prefix(const char *pattern, int pattern_len, const char *str) -{ - const char *or_str; - int i, j, len, res; - - if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { - res = match_prefix(pattern, (int)(or_str - pattern), str); - return res > 0 ? res : - match_prefix(or_str + 1, (int)((pattern + pattern_len) - (or_str + 1)), str); - } - - i = j = 0; - for (; i < pattern_len; i++, j++) { - if (pattern[i] == '?' && str[j] != '\0') { - continue; - } else if (pattern[i] == '$') { - return str[j] == '\0' ? j : -1; - } else if (pattern[i] == '*') { - i++; - if (pattern[i] == '*') { - i++; - len = (int) strlen(str + j); - } else { - len = (int) strcspn(str + j, "/"); - } - if (i == pattern_len) { - return j + len; - } - do { - res = match_prefix(pattern + i, pattern_len - i, str + j + len); - } while (res == -1 && len-- > 0); - return res == -1 ? -1 : j + res + len; - } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { - return -1; - } - } - return j; +static int +match_prefix(const char *pattern, size_t pattern_len, const char *str) +{ + const char *or_str; + size_t i; + int j, len, res; + + if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { + res = match_prefix(pattern, (size_t)(or_str - pattern), str); + return res > 0 ? res : match_prefix(or_str + 1, + (size_t)((pattern + pattern_len) - + (or_str + 1)), + str); + } + + for (i = 0, j = 0; i < pattern_len; i++, j++) { + if (pattern[i] == '?' && str[j] != '\0') { + continue; + } else if (pattern[i] == '$') { + return str[j] == '\0' ? j : -1; + } else if (pattern[i] == '*') { + i++; + if (pattern[i] == '*') { + i++; + len = (int)strlen(str + j); + } else { + len = (int)strcspn(str + j, "/"); + } + if (i == pattern_len) { + return j + len; + } + do { + res = match_prefix(pattern + i, pattern_len - i, str + j + len); + } while (res == -1 && len-- > 0); + return res == -1 ? -1 : j + res + len; + } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { + return -1; + } + } + return j; } /* HTTP 1.1 assumes keep alive if "Connection:" header is not set - This function must tolerate situations when connection info is not - set up, for example if request parsing failed. */ + * This function must tolerate situations when connection info is not + * set up, for example if request parsing failed. */ static int should_keep_alive(const struct mg_connection *conn) { - const char *http_version = conn->request_info.http_version; - const char *header = mg_get_header(conn, "Connection"); - if (conn->must_close || - conn->status_code == 401 || - mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || - (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || - (header == NULL && http_version && 0!=strcmp(http_version, "1.1"))) { - return 0; - } - return 1; + if (conn != NULL) { + const char *http_version = conn->request_info.http_version; + const char *header = mg_get_header(conn, "Connection"); + if (conn->must_close || conn->status_code == 401 || + mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || + (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || + (header == NULL && http_version && + 0 != strcmp(http_version, "1.1"))) { + return 0; + } + return 1; + } + return 0; } static int should_decode_url(const struct mg_connection *conn) { - return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); + if (!conn || !conn->ctx) { + return 0; + } + + return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); } static const char *suggest_connection_header(const struct mg_connection *conn) { - return should_keep_alive(conn) ? "keep-alive" : "close"; -} - -static void handle_file_based_request(struct mg_connection *conn, const char *path, struct file *filep); -static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep); - -static const char *mg_get_response_code_text(int response_code, struct mg_connection *conn) -{ - switch (response_code) - { - /* RFC2616 Section 10.1 - Informational 1xx */ - case 100: return "Continue"; /* RFC2616 Section 10.1.1 */ - case 101: return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ - case 102: return "Processing"; /* RFC2518 Section 10.1 */ - - /* RFC2616 Section 10.2 - Successful 2xx */ - case 200: return "OK"; /* RFC2616 Section 10.2.1 */ - case 201: return "Created"; /* RFC2616 Section 10.2.2 */ - case 202: return "Accepted"; /* RFC2616 Section 10.2.3 */ - case 203: return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ - case 204: return "No Content"; /* RFC2616 Section 10.2.5 */ - case 205: return "Reset Content"; /* RFC2616 Section 10.2.6 */ - case 206: return "Partial Content"; /* RFC2616 Section 10.2.7 */ - case 207: return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */ - - /* RFC2616 Section 10.3 - Redirection 3xx */ - case 300: return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ - case 301: return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ - case 302: return "Found"; /* RFC2616 Section 10.3.3 */ - case 303: return "See Other"; /* RFC2616 Section 10.3.4 */ - case 304: return "Not Modified"; /* RFC2616 Section 10.3.5 */ - case 305: return "Use Proxy"; /* RFC2616 Section 10.3.6 */ - case 307: return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ - - /* RFC2616 Section 10.4 - Client Error 4xx */ - case 400: return "Bad Request"; /* RFC2616 Section 10.4.1 */ - case 401: return "Unauthorized"; /* RFC2616 Section 10.4.2 */ - case 402: return "Payment Required"; /* RFC2616 Section 10.4.3 */ - case 403: return "Forbidden"; /* RFC2616 Section 10.4.4 */ - case 404: return "Not Found"; /* RFC2616 Section 10.4.5 */ - case 405: return "Method Not Allowed"; /* RFC2616 Section 10. <TRUNCATED>
